modulegraphs.nim 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2017 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements the module graph data structure. The module graph
  10. ## represents a complete Nim project. Single modules can either be kept in RAM
  11. ## or stored in a rod-file.
  12. import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils]
  13. import ../dist/checksums/src/checksums/md5
  14. import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
  15. import ic / [packed_ast, ic]
  16. when defined(nimPreviewSlimSystem):
  17. import std/assertions
  18. type
  19. SigHash* = distinct MD5Digest
  20. LazySym* = object
  21. id*: FullId
  22. sym*: PSym
  23. Iface* = object ## data we don't want to store directly in the
  24. ## ast.PSym type for s.kind == skModule
  25. module*: PSym ## module this "Iface" belongs to
  26. converters*: seq[LazySym]
  27. patterns*: seq[LazySym]
  28. pureEnums*: seq[LazySym]
  29. interf: TStrTable
  30. interfHidden: TStrTable
  31. uniqueName*: Rope
  32. Operators* = object
  33. opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym
  34. opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym
  35. FullId* = object
  36. module*: int
  37. packed*: PackedItemId
  38. LazyType* = object
  39. id*: FullId
  40. typ*: PType
  41. LazyInstantiation* = object
  42. module*: int
  43. sym*: FullId
  44. concreteTypes*: seq[FullId]
  45. inst*: PInstantiation
  46. PipelinePass* = enum
  47. NonePass
  48. SemPass
  49. JSgenPass
  50. CgenPass
  51. EvalPass
  52. InterpreterPass
  53. GenDependPass
  54. Docgen2TexPass
  55. Docgen2JsonPass
  56. Docgen2Pass
  57. ModuleGraph* {.acyclic.} = ref object
  58. ifaces*: seq[Iface] ## indexed by int32 fileIdx
  59. packed*: PackedModuleGraph
  60. encoders*: seq[PackedEncoder]
  61. typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId.
  62. procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
  63. attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc.
  64. methodsPerGenericType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
  65. memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual,member and ctor so far).
  66. initializersPerType*: Table[ItemId, PNode] # Type ID, AST call to the default ctor (c++ only)
  67. enumToStringProcs*: Table[ItemId, LazySym]
  68. emittedTypeInfo*: Table[string, FileIndex]
  69. startupPackedConfig*: PackedConfig
  70. packageSyms*: TStrTable
  71. deps*: IntSet # the dependency graph or potentially its transitive closure.
  72. importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
  73. suggestMode*: bool # whether we are in nimsuggest mode or not.
  74. invalidTransitiveClosure: bool
  75. interactive*: bool
  76. withinSystem*: bool # in system.nim or a module imported by system.nim
  77. inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
  78. # first module that included it
  79. importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
  80. # module dependencies.
  81. backend*: RootRef # minor hack so that a backend can extend this easily
  82. config*: ConfigRef
  83. cache*: IdentCache
  84. vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will
  85. # be clarified in later compiler implementations.
  86. repl*: RootRef # REPL state is shared project-wise.
  87. doStopCompile*: proc(): bool {.closure.}
  88. usageSym*: PSym # for nimsuggest
  89. owners*: seq[PSym]
  90. suggestSymbols*: SuggestSymbolDatabase
  91. suggestErrors*: Table[FileIndex, seq[Suggest]]
  92. methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
  93. bucketTable*: CountTable[ItemId]
  94. objectTree*: Table[ItemId, seq[tuple[depth: int, value: PType]]]
  95. methodsPerType*: Table[ItemId, seq[LazySym]]
  96. dispatchers*: seq[LazySym]
  97. systemModule*: PSym
  98. sysTypes*: array[TTypeKind, PType]
  99. compilerprocs*: TStrTable
  100. exposed*: TStrTable
  101. packageTypes*: TStrTable
  102. emptyNode*: PNode
  103. canonTypes*: Table[SigHash, PType]
  104. symBodyHashes*: Table[int, SigHash] # symId to digest mapping
  105. importModuleCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PSym {.nimcall.}
  106. includeFileCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PNode {.nimcall.}
  107. cacheSeqs*: Table[string, PNode] # state that is shared to support the 'macrocache' API; IC: implemented
  108. cacheCounters*: Table[string, BiggestInt] # IC: implemented
  109. cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented
  110. passes*: seq[TPass]
  111. pipelinePass*: PipelinePass
  112. onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
  113. onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
  114. onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
  115. globalDestructors*: seq[PNode]
  116. strongSemCheck*: proc (graph: ModuleGraph; owner: PSym; body: PNode) {.nimcall.}
  117. compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.}
  118. idgen*: IdGenerator
  119. operators*: Operators
  120. cachedFiles*: StringTableRef
  121. TPassContext* = object of RootObj # the pass's context
  122. idgen*: IdGenerator
  123. PPassContext* = ref TPassContext
  124. TPassOpen* = proc (graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nimcall.}
  125. TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.}
  126. TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
  127. TPass* = tuple[open: TPassOpen,
  128. process: TPassProcess,
  129. close: TPassClose,
  130. isFrontend: bool]
  131. proc resetForBackend*(g: ModuleGraph) =
  132. g.compilerprocs = initStrTable()
  133. g.typeInstCache.clear()
  134. g.procInstCache.clear()
  135. for a in mitems(g.attachedOps):
  136. a.clear()
  137. g.methodsPerGenericType.clear()
  138. g.enumToStringProcs.clear()
  139. g.dispatchers.setLen(0)
  140. g.methodsPerType.clear()
  141. const
  142. cb64 = [
  143. "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
  144. "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
  145. "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
  146. "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
  147. "0", "1", "2", "3", "4", "5", "6", "7", "8", "9a",
  148. "9b", "9c"]
  149. proc toBase64a(s: cstring, len: int): string =
  150. ## encodes `s` into base64 representation.
  151. result = newStringOfCap(((len + 2) div 3) * 4)
  152. result.add "__"
  153. var i = 0
  154. while i < len - 2:
  155. let a = ord(s[i])
  156. let b = ord(s[i+1])
  157. let c = ord(s[i+2])
  158. result.add cb64[a shr 2]
  159. result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
  160. result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
  161. result.add cb64[c and 0x3F]
  162. inc(i, 3)
  163. if i < len-1:
  164. let a = ord(s[i])
  165. let b = ord(s[i+1])
  166. result.add cb64[a shr 2]
  167. result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
  168. result.add cb64[((b and 0x0F) shl 2)]
  169. elif i < len:
  170. let a = ord(s[i])
  171. result.add cb64[a shr 2]
  172. result.add cb64[(a and 3) shl 4]
  173. template interfSelect(iface: Iface, importHidden: bool): TStrTable =
  174. var ret = iface.interf.addr # without intermediate ptr, it creates a copy and compiler becomes 15x slower!
  175. if importHidden: ret = iface.interfHidden.addr
  176. ret[]
  177. template semtab(g: ModuleGraph, m: PSym): TStrTable =
  178. g.ifaces[m.position].interf
  179. template semtabAll*(g: ModuleGraph, m: PSym): TStrTable =
  180. g.ifaces[m.position].interfHidden
  181. proc initStrTables*(g: ModuleGraph, m: PSym) =
  182. semtab(g, m) = initStrTable()
  183. semtabAll(g, m) = initStrTable()
  184. proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) =
  185. strTableAdd(semtab(g, m), s)
  186. strTableAdd(semtabAll(g, m), s)
  187. proc isCachedModule(g: ModuleGraph; module: int): bool {.inline.} =
  188. result = module < g.packed.len and g.packed[module].status == loaded
  189. proc isCachedModule*(g: ModuleGraph; m: PSym): bool {.inline.} =
  190. isCachedModule(g, m.position)
  191. proc simulateCachedModule(g: ModuleGraph; moduleSym: PSym; m: PackedModule) =
  192. when false:
  193. echo "simulating ", moduleSym.name.s, " ", moduleSym.position
  194. simulateLoadedModule(g.packed, g.config, g.cache, moduleSym, m)
  195. proc initEncoder*(g: ModuleGraph; module: PSym) =
  196. let id = module.position
  197. if id >= g.encoders.len:
  198. setLen g.encoders, id+1
  199. ic.initEncoder(g.encoders[id],
  200. g.packed[id].fromDisk, module, g.config, g.startupPackedConfig)
  201. type
  202. ModuleIter* = object
  203. fromRod: bool
  204. modIndex: int
  205. ti: TIdentIter
  206. rodIt: RodIter
  207. importHidden: bool
  208. proc initModuleIter*(mi: var ModuleIter; g: ModuleGraph; m: PSym; name: PIdent): PSym =
  209. assert m.kind == skModule
  210. mi.modIndex = m.position
  211. mi.fromRod = isCachedModule(g, mi.modIndex)
  212. mi.importHidden = optImportHidden in m.options
  213. if mi.fromRod:
  214. result = initRodIter(mi.rodIt, g.config, g.cache, g.packed, FileIndex mi.modIndex, name, mi.importHidden)
  215. else:
  216. result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden), name)
  217. proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
  218. if mi.fromRod:
  219. result = nextRodIter(mi.rodIt, g.packed)
  220. else:
  221. result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden))
  222. iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
  223. let importHidden = optImportHidden in m.options
  224. if isCachedModule(g, m):
  225. var rodIt: RodIter = default(RodIter)
  226. var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden)
  227. while r != nil:
  228. yield r
  229. r = nextRodIter(rodIt, g.packed)
  230. else:
  231. for s in g.ifaces[m.position].interfSelect(importHidden).data:
  232. if s != nil:
  233. yield s
  234. proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
  235. let importHidden = optImportHidden in m.options
  236. if isCachedModule(g, m):
  237. result = interfaceSymbol(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden)
  238. else:
  239. result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)
  240. proc someSymAmb*(g: ModuleGraph; m: PSym; name: PIdent; amb: var bool): PSym =
  241. let importHidden = optImportHidden in m.options
  242. if isCachedModule(g, m):
  243. result = nil
  244. for s in interfaceSymbols(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden):
  245. if result == nil:
  246. # set result to the first symbol
  247. result = s
  248. else:
  249. # another symbol found
  250. amb = true
  251. break
  252. else:
  253. var ti: TIdentIter = default(TIdentIter)
  254. result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name)
  255. if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil:
  256. # another symbol exists with same name
  257. amb = true
  258. proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
  259. result = someSym(g, g.systemModule, name)
  260. iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym =
  261. var mi: ModuleIter = default(ModuleIter)
  262. var r = initModuleIter(mi, g, g.systemModule, name)
  263. while r != nil:
  264. yield r
  265. r = nextModuleIter(mi, g)
  266. proc resolveType(g: ModuleGraph; t: var LazyType): PType =
  267. result = t.typ
  268. if result == nil and isCachedModule(g, t.id.module):
  269. result = loadTypeFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
  270. t.typ = result
  271. assert result != nil
  272. proc resolveSym(g: ModuleGraph; t: var LazySym): PSym =
  273. result = t.sym
  274. if result == nil and isCachedModule(g, t.id.module):
  275. result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
  276. t.sym = result
  277. assert result != nil
  278. proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
  279. result = t.inst
  280. if result == nil and isCachedModule(g, t.module):
  281. result = PInstantiation(sym: loadSymFromId(g.config, g.cache, g.packed, t.sym.module, t.sym.packed))
  282. result.concreteTypes = newSeq[PType](t.concreteTypes.len)
  283. for i in 0..high(result.concreteTypes):
  284. result.concreteTypes[i] = loadTypeFromId(g.config, g.cache, g.packed,
  285. t.concreteTypes[i].module, t.concreteTypes[i].packed)
  286. t.inst = result
  287. assert result != nil
  288. proc resolveAttachedOp*(g: ModuleGraph; t: var LazySym): PSym =
  289. result = t.sym
  290. if result == nil:
  291. result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
  292. t.sym = result
  293. assert result != nil
  294. iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType =
  295. if g.typeInstCache.contains(s.itemId):
  296. let x = addr(g.typeInstCache[s.itemId])
  297. for t in mitems(x[]):
  298. yield resolveType(g, t)
  299. iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation =
  300. if g.procInstCache.contains(s.itemId):
  301. let x = addr(g.procInstCache[s.itemId])
  302. for t in mitems(x[]):
  303. yield resolveInst(g, t)
  304. proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
  305. ## returns the requested attached operation for type `t`. Can return nil
  306. ## if no such operation exists.
  307. if g.attachedOps[op].contains(t.itemId):
  308. result = resolveAttachedOp(g, g.attachedOps[op][t.itemId])
  309. else:
  310. result = nil
  311. proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
  312. ## we also need to record this to the packed module.
  313. g.attachedOps[op][t.itemId] = LazySym(sym: value)
  314. proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
  315. ## we also need to record this to the packed module.
  316. g.attachedOps[op][t.itemId] = LazySym(sym: value)
  317. proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
  318. if g.config.symbolFiles != disabledSf:
  319. assert module < g.encoders.len
  320. assert isActive(g.encoders[module])
  321. toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
  322. #storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].fromDisk)
  323. iterator getDispatchers*(g: ModuleGraph): PSym =
  324. for i in g.dispatchers.mitems:
  325. yield resolveSym(g, i)
  326. proc addDispatchers*(g: ModuleGraph, value: PSym) =
  327. # TODO: add it for packed modules
  328. g.dispatchers.add LazySym(sym: value)
  329. iterator resolveLazySymSeq(g: ModuleGraph, list: var seq[LazySym]): PSym =
  330. for it in list.mitems:
  331. yield resolveSym(g, it)
  332. proc setMethodsPerType*(g: ModuleGraph; id: ItemId, methods: seq[LazySym]) =
  333. # TODO: add it for packed modules
  334. g.methodsPerType[id] = methods
  335. iterator getMethodsPerType*(g: ModuleGraph; t: PType): PSym =
  336. if g.methodsPerType.contains(t.itemId):
  337. for it in mitems g.methodsPerType[t.itemId]:
  338. yield resolveSym(g, it)
  339. proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
  340. result = resolveSym(g, g.enumToStringProcs[t.itemId])
  341. assert result != nil
  342. proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) =
  343. g.enumToStringProcs[t.itemId] = LazySym(sym: value)
  344. iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) =
  345. if g.methodsPerGenericType.contains(t.itemId):
  346. for it in mitems g.methodsPerGenericType[t.itemId]:
  347. yield (it[0], resolveSym(g, it[1]))
  348. proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) =
  349. g.methodsPerGenericType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
  350. proc hasDisabledAsgn*(g: ModuleGraph; t: PType): bool =
  351. let op = getAttachedOp(g, t, attachedAsgn)
  352. result = op != nil and sfError in op.flags
  353. proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
  354. for k in low(TTypeAttachedOp)..high(TTypeAttachedOp):
  355. let op = getAttachedOp(g, src, k)
  356. if op != nil:
  357. setAttachedOp(g, module, dest, k, op)
  358. proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
  359. result = nil
  360. if g.config.symbolFiles == disabledSf: return nil
  361. # slow, linear search, but the results are cached:
  362. for module in 0..<len(g.packed):
  363. #if isCachedModule(g, module):
  364. let x = searchForCompilerproc(g.packed[module], name)
  365. if x >= 0:
  366. result = loadSymFromId(g.config, g.cache, g.packed, module, toPackedItemId(x))
  367. if result != nil:
  368. strTableAdd(g.compilerprocs, result)
  369. return result
  370. proc loadPackedSym*(g: ModuleGraph; s: var LazySym) =
  371. if s.sym == nil:
  372. s.sym = loadSymFromId(g.config, g.cache, g.packed, s.id.module, s.id.packed)
  373. proc `$`*(u: SigHash): string =
  374. toBase64a(cast[cstring](unsafeAddr u), sizeof(u))
  375. proc `==`*(a, b: SigHash): bool =
  376. result = equalMem(unsafeAddr a, unsafeAddr b, sizeof(a))
  377. proc hash*(u: SigHash): Hash =
  378. result = 0
  379. for x in 0..3:
  380. result = (result shl 8) or u.MD5Digest[x].int
  381. proc hash*(x: FileIndex): Hash {.borrow.}
  382. template getPContext(): untyped =
  383. when c is PContext: c
  384. else: c.c
  385. when defined(nimsuggest):
  386. template onUse*(info: TLineInfo; s: PSym) = discard
  387. template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
  388. else:
  389. template onUse*(info: TLineInfo; s: PSym) = discard
  390. template onDef*(info: TLineInfo; s: PSym) = discard
  391. template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
  392. proc stopCompile*(g: ModuleGraph): bool {.inline.} =
  393. result = g.doStopCompile != nil and g.doStopCompile()
  394. proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic): PSym =
  395. result = newSym(skProc, getIdent(g.cache, name), idgen, nil, unknownLineInfo, {})
  396. result.magic = m
  397. result.flags = {sfNeverRaises}
  398. proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym =
  399. result = createMagic(g, g.idgen, name, m)
  400. proc uniqueModuleName*(conf: ConfigRef; m: PSym): string =
  401. ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
  402. ## so that it is useful as a C identifier snippet.
  403. let fid = FileIndex(m.position)
  404. let path = AbsoluteFile toFullPath(conf, fid)
  405. var isLib = false
  406. var rel = ""
  407. if path.string.startsWith(conf.libpath.string):
  408. isLib = true
  409. rel = relativeTo(path, conf.libpath).string
  410. else:
  411. rel = relativeTo(path, conf.projectPath).string
  412. if not isLib and not belongsToProjectPackage(conf, m):
  413. # special handlings for nimble packages
  414. when DirSep == '\\':
  415. let rel2 = replace(rel, '\\', '/')
  416. else:
  417. let rel2 = rel
  418. const pkgs2 = "pkgs2/"
  419. var start = rel2.find(pkgs2)
  420. if start >= 0:
  421. start += pkgs2.len
  422. start += skipUntil(rel2, {'/'}, start)
  423. if start+1 < rel2.len:
  424. rel = "pkg/" & rel2[start+1..<rel.len] # strips paths
  425. let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
  426. result = newStringOfCap(trunc)
  427. for i in 0..<trunc:
  428. let c = rel[i]
  429. case c
  430. of 'a'..'z', '0'..'9':
  431. result.add c
  432. of {os.DirSep, os.AltSep}:
  433. result.add 'Z' # because it looks a bit like '/'
  434. of '.':
  435. result.add 'O' # a circle
  436. else:
  437. # We mangle upper letters too so that there cannot
  438. # be clashes with our special meanings of 'Z' and 'O'
  439. result.addInt ord(c)
  440. proc registerModule*(g: ModuleGraph; m: PSym) =
  441. assert m != nil
  442. assert m.kind == skModule
  443. if m.position >= g.ifaces.len:
  444. setLen(g.ifaces, m.position + 1)
  445. if m.position >= g.packed.len:
  446. setLen(g.packed.pm, m.position + 1)
  447. g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
  448. uniqueName: rope(uniqueModuleName(g.config, m)))
  449. initStrTables(g, m)
  450. proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
  451. registerModule(g, g.packed[int m].module)
  452. proc initOperators*(g: ModuleGraph): Operators =
  453. # These are safe for IC.
  454. # Public because it's used by DrNim.
  455. result = Operators(
  456. opLe: createMagic(g, "<=", mLeI),
  457. opLt: createMagic(g, "<", mLtI),
  458. opAnd: createMagic(g, "and", mAnd),
  459. opOr: createMagic(g, "or", mOr),
  460. opIsNil: createMagic(g, "isnil", mIsNil),
  461. opEq: createMagic(g, "==", mEqI),
  462. opAdd: createMagic(g, "+", mAddI),
  463. opSub: createMagic(g, "-", mSubI),
  464. opMul: createMagic(g, "*", mMulI),
  465. opDiv: createMagic(g, "div", mDivI),
  466. opLen: createMagic(g, "len", mLengthSeq),
  467. opNot: createMagic(g, "not", mNot),
  468. opContains: createMagic(g, "contains", mInSet)
  469. )
  470. proc initModuleGraphFields(result: ModuleGraph) =
  471. # A module ID of -1 means that the symbol is not attached to a module at all,
  472. # but to the module graph:
  473. result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32)
  474. result.packageSyms = initStrTable()
  475. result.deps = initIntSet()
  476. result.importDeps = initTable[FileIndex, seq[FileIndex]]()
  477. result.ifaces = @[]
  478. result.importStack = @[]
  479. result.inclToMod = initTable[FileIndex, FileIndex]()
  480. result.owners = @[]
  481. result.suggestSymbols = initTable[FileIndex, SuggestFileSymbolDatabase]()
  482. result.suggestErrors = initTable[FileIndex, seq[Suggest]]()
  483. result.methods = @[]
  484. result.compilerprocs = initStrTable()
  485. result.exposed = initStrTable()
  486. result.packageTypes = initStrTable()
  487. result.emptyNode = newNode(nkEmpty)
  488. result.cacheSeqs = initTable[string, PNode]()
  489. result.cacheCounters = initTable[string, BiggestInt]()
  490. result.cacheTables = initTable[string, BTree[string, PNode]]()
  491. result.canonTypes = initTable[SigHash, PType]()
  492. result.symBodyHashes = initTable[int, SigHash]()
  493. result.operators = initOperators(result)
  494. result.emittedTypeInfo = initTable[string, FileIndex]()
  495. result.cachedFiles = newStringTable()
  496. proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
  497. result = ModuleGraph()
  498. result.config = config
  499. result.cache = cache
  500. initModuleGraphFields(result)
  501. proc resetAllModules*(g: ModuleGraph) =
  502. g.packageSyms = initStrTable()
  503. g.deps = initIntSet()
  504. g.ifaces = @[]
  505. g.importStack = @[]
  506. g.inclToMod = initTable[FileIndex, FileIndex]()
  507. g.usageSym = nil
  508. g.owners = @[]
  509. g.methods = @[]
  510. g.compilerprocs = initStrTable()
  511. g.exposed = initStrTable()
  512. initModuleGraphFields(g)
  513. proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
  514. result = nil
  515. if fileIdx.int32 >= 0:
  516. if isCachedModule(g, fileIdx.int32):
  517. result = g.packed[fileIdx.int32].module
  518. elif fileIdx.int32 < g.ifaces.len:
  519. result = g.ifaces[fileIdx.int32].module
  520. proc moduleOpenForCodegen*(g: ModuleGraph; m: FileIndex): bool {.inline.} =
  521. if g.config.symbolFiles == disabledSf:
  522. result = true
  523. else:
  524. result = g.packed[m.int32].status notin {undefined, stored, loaded}
  525. proc rememberEmittedTypeInfo*(g: ModuleGraph; m: FileIndex; ti: string) =
  526. #assert(not isCachedModule(g, m.int32))
  527. if g.config.symbolFiles != disabledSf:
  528. #assert g.encoders[m.int32].isActive
  529. assert g.packed[m.int32].status != stored
  530. g.packed[m.int32].fromDisk.emittedTypeInfo.add ti
  531. #echo "added typeinfo ", m.int32, " ", ti, " suspicious ", not g.encoders[m.int32].isActive
  532. proc rememberFlag*(g: ModuleGraph; m: PSym; flag: ModuleBackendFlag) =
  533. if g.config.symbolFiles != disabledSf:
  534. #assert g.encoders[m.int32].isActive
  535. assert g.packed[m.position].status != stored
  536. g.packed[m.position].fromDisk.backendFlags.incl flag
  537. proc closeRodFile*(g: ModuleGraph; m: PSym) =
  538. if g.config.symbolFiles in {readOnlySf, v2Sf}:
  539. # For stress testing we seek to reload the symbols from memory. This
  540. # way much of the logic is tested but the test is reproducible as it does
  541. # not depend on the hard disk contents!
  542. let mint = m.position
  543. saveRodFile(toRodFile(g.config, AbsoluteFile toFullPath(g.config, FileIndex(mint))),
  544. g.encoders[mint], g.packed[mint].fromDisk)
  545. g.packed[mint].status = stored
  546. elif g.config.symbolFiles == stressTest:
  547. # debug code, but maybe a good idea for production? Could reduce the compiler's
  548. # memory consumption considerably at the cost of more loads from disk.
  549. let mint = m.position
  550. simulateCachedModule(g, m, g.packed[mint].fromDisk)
  551. g.packed[mint].status = loaded
  552. proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
  553. proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
  554. assert m.position == m.info.fileIndex.int32
  555. if g.suggestMode:
  556. g.deps.incl m.position.dependsOn(dep.int)
  557. # we compute the transitive closure later when querying the graph lazily.
  558. # this improves efficiency quite a lot:
  559. #invalidTransitiveClosure = true
  560. proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
  561. discard hasKeyOrPut(g.inclToMod, includeFile, module)
  562. proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
  563. ## returns 'fileIdx' if the file belonging to this index is
  564. ## directly used as a module or else the module that first
  565. ## references this include file.
  566. if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len and g.ifaces[fileIdx.int32].module != nil:
  567. result = fileIdx
  568. else:
  569. result = g.inclToMod.getOrDefault(fileIdx)
  570. proc transitiveClosure(g: var IntSet; n: int) =
  571. # warshall's algorithm
  572. for k in 0..<n:
  573. for i in 0..<n:
  574. for j in 0..<n:
  575. if i != j and not g.contains(i.dependsOn(j)):
  576. if g.contains(i.dependsOn(k)) and g.contains(k.dependsOn(j)):
  577. g.incl i.dependsOn(j)
  578. proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
  579. let m = g.getModule fileIdx
  580. if m != nil:
  581. g.suggestSymbols.del(fileIdx)
  582. g.suggestErrors.del(fileIdx)
  583. incl m.flags, sfDirty
  584. proc unmarkAllDirty*(g: ModuleGraph) =
  585. for i in 0i32..<g.ifaces.len.int32:
  586. let m = g.ifaces[i].module
  587. if m != nil:
  588. m.flags.excl sfDirty
  589. proc isDirty*(g: ModuleGraph; m: PSym): bool =
  590. result = g.suggestMode and sfDirty in m.flags
  591. proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
  592. # we need to mark its dependent modules D as dirty right away because after
  593. # nimsuggest is done with this module, the module's dirty flag will be
  594. # cleared but D still needs to be remembered as 'dirty'.
  595. if g.invalidTransitiveClosure:
  596. g.invalidTransitiveClosure = false
  597. transitiveClosure(g.deps, g.ifaces.len)
  598. # every module that *depends* on this file is also dirty:
  599. for i in 0i32..<g.ifaces.len.int32:
  600. if g.deps.contains(i.dependsOn(fileIdx.int)):
  601. g.markDirty(FileIndex(i))
  602. proc needsCompilation*(g: ModuleGraph): bool =
  603. # every module that *depends* on this file is also dirty:
  604. result = false
  605. for i in 0i32..<g.ifaces.len.int32:
  606. let m = g.ifaces[i].module
  607. if m != nil:
  608. if sfDirty in m.flags:
  609. return true
  610. proc needsCompilation*(g: ModuleGraph, fileIdx: FileIndex): bool =
  611. result = false
  612. let module = g.getModule(fileIdx)
  613. if module != nil and g.isDirty(module):
  614. return true
  615. for i in 0i32..<g.ifaces.len.int32:
  616. let m = g.ifaces[i].module
  617. if m != nil and g.isDirty(m) and g.deps.contains(fileIdx.int32.dependsOn(i)):
  618. return true
  619. proc getBody*(g: ModuleGraph; s: PSym): PNode {.inline.} =
  620. result = s.ast[bodyPos]
  621. if result == nil and g.config.symbolFiles in {readOnlySf, v2Sf, stressTest}:
  622. result = loadProcBody(g.config, g.cache, g.packed, s)
  623. s.ast[bodyPos] = result
  624. assert result != nil
  625. proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
  626. cachedModules: var seq[FileIndex]): PSym =
  627. ## Returns 'nil' if the module needs to be recompiled.
  628. if g.config.symbolFiles in {readOnlySf, v2Sf, stressTest}:
  629. result = moduleFromRodFile(g.packed, g.config, g.cache, fileIdx, cachedModules)
  630. else:
  631. result = nil
  632. proc configComplete*(g: ModuleGraph) =
  633. rememberStartupConfig(g.startupPackedConfig, g.config)
  634. proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) =
  635. let conf = graph.config
  636. let isNimscript = conf.isDefined("nimscript")
  637. if (not isNimscript) or hintProcessing in conf.cmdlineNotes:
  638. let path = toFilenameOption(conf, fileIdx, conf.filenameOption)
  639. let indent = ">".repeat(graph.importStack.len)
  640. let fromModule2 = if fromModule != nil: $fromModule.name.s else: "(toplevel)"
  641. let mode = if isNimscript: "(nims) " else: ""
  642. rawMessage(conf, hintProcessing, "$#$# $#: $#: $#" % [mode, indent, fromModule2, moduleStatus, path])
  643. proc getPackage*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
  644. ## Returns a package symbol for yet to be defined module for fileIdx.
  645. ## The package symbol is added to the graph if it doesn't exist.
  646. let pkgSym = getPackage(graph.config, graph.cache, fileIdx)
  647. # check if the package is already in the graph
  648. result = graph.packageSyms.strTableGet(pkgSym.name)
  649. if result == nil:
  650. # the package isn't in the graph, so create and add it
  651. result = pkgSym
  652. graph.packageSyms.strTableAdd(pkgSym)
  653. func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool =
  654. ## Check if symbol belongs to the 'stdlib' package.
  655. sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId
  656. proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): SuggestFileSymbolDatabase =
  657. result = graph.suggestSymbols.getOrDefault(fileIdx, newSuggestFileSymbolDatabase(fileIdx, optIdeExceptionInlayHints in graph.config.globalOptions))
  658. doAssert(result.fileIndex == fileIdx)
  659. iterator suggestSymbolsIter*(g: ModuleGraph): SymInfoPair =
  660. for xs in g.suggestSymbols.values:
  661. for i in xs.lineInfo.low..xs.lineInfo.high:
  662. yield xs.getSymInfoPair(i)
  663. iterator suggestErrorsIter*(g: ModuleGraph): Suggest =
  664. for xs in g.suggestErrors.values:
  665. for x in xs:
  666. yield x