rodimpl.nim 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2018 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 new compilation cache.
  10. import strutils, intsets, tables, ropes, db_sqlite, msgs, options,
  11. renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp,
  12. btrees, trees, condsyms, nversion, pathutils
  13. ## Todo:
  14. ## - Dependency computation should use *signature* hashes in order to
  15. ## avoid recompiling dependent modules.
  16. ## - Patch the rest of the compiler to do lazy loading of proc bodies.
  17. ## - serialize the AST in a smarter way (avoid storing some ASTs twice!)
  18. template db(): DbConn = g.incr.db
  19. proc encodeConfig(g: ModuleGraph): string =
  20. result = newStringOfCap(100)
  21. result.add RodFileVersion
  22. for d in definedSymbolNames(g.config.symbols):
  23. result.add ' '
  24. result.add d
  25. template serialize(field) =
  26. result.add ' '
  27. result.add($g.config.field)
  28. depConfigFields(serialize)
  29. proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile;
  30. cycleCheck: var IntSet): bool =
  31. let root = db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
  32. fullpath.string)
  33. if root[0].len == 0: return true
  34. if root[1] != hashFileCached(g.config, fileIdx, fullpath):
  35. return true
  36. # cycle detection: assume "not changed" is correct.
  37. if cycleCheck.containsOrIncl(int fileIdx):
  38. return false
  39. # check dependencies (recursively):
  40. for row in db.fastRows(sql"select fullpath from filenames where id in (select dependency from deps where module = ?)",
  41. root[0]):
  42. let dep = AbsoluteFile row[0]
  43. if needsRecompile(g, g.config.fileInfoIdx(dep), dep, cycleCheck):
  44. return true
  45. return false
  46. proc getModuleId(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): int =
  47. ## Analyse the known dependency graph.
  48. if g.config.symbolFiles == disabledSf: return getID()
  49. when false:
  50. if g.config.symbolFiles in {disabledSf, writeOnlySf} or
  51. g.incr.configChanged:
  52. return getID()
  53. let module = g.incr.db.getRow(
  54. sql"select id, fullHash, nimid from modules where fullpath = ?", string fullpath)
  55. let currentFullhash = hashFileCached(g.config, fileIdx, fullpath)
  56. if module[0].len == 0:
  57. result = getID()
  58. db.exec(sql"insert into modules(fullpath, interfHash, fullHash, nimid) values (?, ?, ?, ?)",
  59. string fullpath, "", currentFullhash, result)
  60. else:
  61. result = parseInt(module[2])
  62. if currentFullhash == module[1]:
  63. # not changed, so use the cached AST:
  64. doAssert(result != 0)
  65. var cycleCheck = initIntSet()
  66. if not needsRecompile(g, fileIdx, fullpath, cycleCheck):
  67. if not g.incr.configChanged or g.config.symbolFiles == readOnlySf:
  68. #echo "cached successfully! ", string fullpath
  69. return -result
  70. elif g.config.symbolFiles == readOnlySf:
  71. internalError(g.config, "file needs to be recompiled: " & (string fullpath))
  72. db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0])
  73. db.exec(sql"delete from deps where module = ?", module[0])
  74. db.exec(sql"delete from types where module = ?", module[0])
  75. db.exec(sql"delete from syms where module = ?", module[0])
  76. db.exec(sql"delete from toplevelstmts where module = ?", module[0])
  77. db.exec(sql"delete from statics where module = ?", module[0])
  78. proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): (PSym, int) =
  79. let id = getModuleId(g, fileIdx, fullpath)
  80. result = (g.incr.r.syms.getOrDefault(abs id), id)
  81. proc pushType(w: var Writer, t: PType) =
  82. if not containsOrIncl(w.tmarks, t.uniqueId):
  83. w.tstack.add(t)
  84. proc pushSym(w: var Writer, s: PSym) =
  85. if not containsOrIncl(w.smarks, s.id):
  86. w.sstack.add(s)
  87. template w: untyped = g.incr.w
  88. proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode,
  89. result: var string) =
  90. if n == nil:
  91. # nil nodes have to be stored too:
  92. result.add("()")
  93. return
  94. result.add('(')
  95. encodeVInt(ord(n.kind), result)
  96. # we do not write comments for now
  97. # Line information takes easily 20% or more of the filesize! Therefore we
  98. # omit line information if it is the same as the parent's line information:
  99. if fInfo.fileIndex != n.info.fileIndex:
  100. result.add('?')
  101. encodeVInt(n.info.col, result)
  102. result.add(',')
  103. encodeVInt(int n.info.line, result)
  104. result.add(',')
  105. #encodeVInt(toDbFileId(g.incr, g.config, n.info.fileIndex), result)
  106. encodeVInt(n.info.fileIndex.int, result)
  107. elif fInfo.line != n.info.line:
  108. result.add('?')
  109. encodeVInt(n.info.col, result)
  110. result.add(',')
  111. encodeVInt(int n.info.line, result)
  112. elif fInfo.col != n.info.col:
  113. result.add('?')
  114. encodeVInt(n.info.col, result)
  115. # No need to output the file index, as this is the serialization of one
  116. # file.
  117. let f = n.flags * PersistentNodeFlags
  118. if f != {}:
  119. result.add('$')
  120. encodeVInt(cast[int32](f), result)
  121. if n.typ != nil:
  122. result.add('^')
  123. encodeVInt(n.typ.uniqueId, result)
  124. pushType(w, n.typ)
  125. case n.kind
  126. of nkCharLit..nkUInt64Lit:
  127. if n.intVal != 0:
  128. result.add('!')
  129. encodeVBiggestInt(n.intVal, result)
  130. of nkFloatLit..nkFloat64Lit:
  131. if n.floatVal != 0.0:
  132. result.add('!')
  133. encodeStr($n.floatVal, result)
  134. of nkStrLit..nkTripleStrLit:
  135. if n.strVal != "":
  136. result.add('!')
  137. encodeStr(n.strVal, result)
  138. of nkIdent:
  139. result.add('!')
  140. encodeStr(n.ident.s, result)
  141. of nkSym:
  142. result.add('!')
  143. encodeVInt(n.sym.id, result)
  144. pushSym(w, n.sym)
  145. else:
  146. for i in 0..<n.len:
  147. encodeNode(g, n.info, n[i], result)
  148. result.add(')')
  149. proc encodeLoc(g: ModuleGraph; loc: TLoc, result: var string) =
  150. var oldLen = result.len
  151. result.add('<')
  152. if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
  153. if loc.storage != low(loc.storage):
  154. result.add('*')
  155. encodeVInt(ord(loc.storage), result)
  156. if loc.flags != {}:
  157. result.add('$')
  158. encodeVInt(cast[int32](loc.flags), result)
  159. if loc.lode != nil:
  160. result.add('^')
  161. encodeNode(g, unknownLineInfo, loc.lode, result)
  162. if loc.r != nil:
  163. result.add('!')
  164. encodeStr($loc.r, result)
  165. if oldLen + 1 == result.len:
  166. # no data was necessary, so remove the '<' again:
  167. setLen(result, oldLen)
  168. else:
  169. result.add('>')
  170. proc encodeType(g: ModuleGraph, t: PType, result: var string) =
  171. if t == nil:
  172. # nil nodes have to be stored too:
  173. result.add("[]")
  174. return
  175. # we need no surrounding [] here because the type is in a line of its own
  176. if t.kind == tyForward: internalError(g.config, "encodeType: tyForward")
  177. # for the new rodfile viewer we use a preceding [ so that the data section
  178. # can easily be disambiguated:
  179. result.add('[')
  180. encodeVInt(ord(t.kind), result)
  181. result.add('+')
  182. encodeVInt(t.uniqueId, result)
  183. if t.id != t.uniqueId:
  184. result.add('+')
  185. encodeVInt(t.id, result)
  186. if t.n != nil:
  187. encodeNode(g, unknownLineInfo, t.n, result)
  188. if t.flags != {}:
  189. result.add('$')
  190. encodeVInt(cast[int32](t.flags), result)
  191. if t.callConv != low(t.callConv):
  192. result.add('?')
  193. encodeVInt(ord(t.callConv), result)
  194. if t.owner != nil:
  195. result.add('*')
  196. encodeVInt(t.owner.id, result)
  197. pushSym(w, t.owner)
  198. if t.sym != nil:
  199. result.add('&')
  200. encodeVInt(t.sym.id, result)
  201. pushSym(w, t.sym)
  202. if t.size != - 1:
  203. result.add('/')
  204. encodeVBiggestInt(t.size, result)
  205. if t.align != 2:
  206. result.add('=')
  207. encodeVInt(t.align, result)
  208. if t.lockLevel.ord != UnspecifiedLockLevel.ord:
  209. result.add('\14')
  210. encodeVInt(t.lockLevel.int16, result)
  211. if t.paddingAtEnd != 0:
  212. result.add('\15')
  213. encodeVInt(t.paddingAtEnd, result)
  214. for a in t.attachedOps:
  215. result.add('\16')
  216. if a == nil:
  217. encodeVInt(-1, result)
  218. else:
  219. encodeVInt(a.id, result)
  220. pushSym(w, a)
  221. for i, s in items(t.methods):
  222. result.add('\19')
  223. encodeVInt(i, result)
  224. result.add('\20')
  225. encodeVInt(s.id, result)
  226. pushSym(w, s)
  227. encodeLoc(g, t.loc, result)
  228. if t.typeInst != nil:
  229. result.add('\21')
  230. encodeVInt(t.typeInst.uniqueId, result)
  231. pushType(w, t.typeInst)
  232. for i in 0..<t.len:
  233. if t[i] == nil:
  234. result.add("^()")
  235. else:
  236. result.add('^')
  237. encodeVInt(t[i].uniqueId, result)
  238. pushType(w, t[i])
  239. proc encodeLib(g: ModuleGraph, lib: PLib, info: TLineInfo, result: var string) =
  240. result.add('|')
  241. encodeVInt(ord(lib.kind), result)
  242. result.add('|')
  243. encodeStr($lib.name, result)
  244. result.add('|')
  245. encodeNode(g, info, lib.path, result)
  246. proc encodeInstantiations(g: ModuleGraph; s: seq[PInstantiation];
  247. result: var string) =
  248. for t in s:
  249. result.add('\15')
  250. encodeVInt(t.sym.id, result)
  251. pushSym(w, t.sym)
  252. for tt in t.concreteTypes:
  253. result.add('\17')
  254. encodeVInt(tt.uniqueId, result)
  255. pushType(w, tt)
  256. result.add('\20')
  257. encodeVInt(t.compilesId, result)
  258. proc encodeSym(g: ModuleGraph, s: PSym, result: var string) =
  259. if s == nil:
  260. # nil nodes have to be stored too:
  261. result.add("{}")
  262. return
  263. # we need no surrounding {} here because the symbol is in a line of its own
  264. encodeVInt(ord(s.kind), result)
  265. result.add('+')
  266. encodeVInt(s.id, result)
  267. result.add('&')
  268. encodeStr(s.name.s, result)
  269. if s.typ != nil:
  270. result.add('^')
  271. encodeVInt(s.typ.uniqueId, result)
  272. pushType(w, s.typ)
  273. result.add('?')
  274. if s.info.col != -1'i16: encodeVInt(s.info.col, result)
  275. result.add(',')
  276. encodeVInt(int s.info.line, result)
  277. result.add(',')
  278. #encodeVInt(toDbFileId(g.incr, g.config, s.info.fileIndex), result)
  279. encodeVInt(s.info.fileIndex.int, result)
  280. if s.owner != nil:
  281. result.add('*')
  282. encodeVInt(s.owner.id, result)
  283. pushSym(w, s.owner)
  284. if s.flags != {}:
  285. result.add('$')
  286. encodeVBiggestInt(cast[int64](s.flags), result)
  287. if s.magic != mNone:
  288. result.add('@')
  289. encodeVInt(ord(s.magic), result)
  290. result.add('!')
  291. encodeVInt(cast[int32](s.options), result)
  292. if s.position != 0:
  293. result.add('%')
  294. encodeVInt(s.position, result)
  295. if s.offset != - 1:
  296. result.add('`')
  297. encodeVInt(s.offset, result)
  298. encodeLoc(g, s.loc, result)
  299. if s.annex != nil: encodeLib(g, s.annex, s.info, result)
  300. if s.constraint != nil:
  301. result.add('#')
  302. encodeNode(g, unknownLineInfo, s.constraint, result)
  303. case s.kind
  304. of skType, skGenericParam:
  305. for t in s.typeInstCache:
  306. result.add('\14')
  307. encodeVInt(t.uniqueId, result)
  308. pushType(w, t)
  309. of routineKinds:
  310. encodeInstantiations(g, s.procInstCache, result)
  311. if s.gcUnsafetyReason != nil:
  312. result.add('\16')
  313. encodeVInt(s.gcUnsafetyReason.id, result)
  314. pushSym(w, s.gcUnsafetyReason)
  315. if s.transformedBody != nil:
  316. result.add('\24')
  317. encodeNode(g, s.info, s.transformedBody, result)
  318. of skModule, skPackage:
  319. encodeInstantiations(g, s.usedGenerics, result)
  320. # we don't serialize:
  321. #tab*: TStrTable # interface table for modules
  322. of skLet, skVar, skField, skForVar:
  323. if s.guard != nil:
  324. result.add('\18')
  325. encodeVInt(s.guard.id, result)
  326. pushSym(w, s.guard)
  327. if s.bitsize != 0:
  328. result.add('\19')
  329. encodeVInt(s.bitsize, result)
  330. else: discard
  331. # lazy loading will soon reload the ast lazily, so the ast needs to be
  332. # the last entry of a symbol:
  333. if s.ast != nil:
  334. # we used to attempt to save space here by only storing a dummy AST if
  335. # it is not necessary, but Nim's heavy compile-time evaluation features
  336. # make that unfeasible nowadays:
  337. encodeNode(g, s.info, s.ast, result)
  338. proc storeSym(g: ModuleGraph; s: PSym) =
  339. if sfForward in s.flags and s.kind != skModule:
  340. w.forwardedSyms.add s
  341. return
  342. var buf = newStringOfCap(160)
  343. encodeSym(g, s, buf)
  344. # XXX only store the name for exported symbols in order to speed up lookup
  345. # times once we enable the skStub logic.
  346. let m = getModule(s)
  347. let mid = if m == nil: 0 else: abs(m.id)
  348. db.exec(sql"insert into syms(nimid, module, name, data, exported) values (?, ?, ?, ?, ?)",
  349. s.id, mid, s.name.s, buf, ord(sfExported in s.flags))
  350. proc storeType(g: ModuleGraph; t: PType) =
  351. var buf = newStringOfCap(160)
  352. encodeType(g, t, buf)
  353. let m = if t.owner != nil: getModule(t.owner) else: nil
  354. let mid = if m == nil: 0 else: abs(m.id)
  355. db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)",
  356. t.uniqueId, mid, buf)
  357. proc transitiveClosure(g: ModuleGraph) =
  358. var i = 0
  359. while true:
  360. if i > 100_000:
  361. doAssert false, "loop never ends!"
  362. if w.sstack.len > 0:
  363. let s = w.sstack.pop()
  364. when false:
  365. echo "popped ", s.name.s, " ", s.id
  366. storeSym(g, s)
  367. elif w.tstack.len > 0:
  368. let t = w.tstack.pop()
  369. storeType(g, t)
  370. when false:
  371. echo "popped type ", typeToString(t), " ", t.uniqueId
  372. else:
  373. break
  374. inc i
  375. proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) =
  376. if g.config.symbolFiles == disabledSf: return
  377. var buf = newStringOfCap(160)
  378. encodeNode(g, module.info, n, buf)
  379. db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)",
  380. abs(module.id), module.offset, buf)
  381. inc module.offset
  382. transitiveClosure(g)
  383. proc recordStmt*(g: ModuleGraph; module: PSym; n: PNode) =
  384. storeNode(g, module, n)
  385. proc storeFilename(g: ModuleGraph; fullpath: AbsoluteFile; fileIdx: FileIndex) =
  386. let id = db.getValue(sql"select id from filenames where fullpath = ?", fullpath.string)
  387. if id.len == 0:
  388. let fullhash = hashFileCached(g.config, fileIdx, fullpath)
  389. db.exec(sql"insert into filenames(nimid, fullpath, fullhash) values (?, ?, ?)",
  390. int(fileIdx), fullpath.string, fullhash)
  391. proc storeRemaining*(g: ModuleGraph; module: PSym) =
  392. if g.config.symbolFiles == disabledSf: return
  393. var stillForwarded: seq[PSym] = @[]
  394. for s in w.forwardedSyms:
  395. if sfForward notin s.flags:
  396. storeSym(g, s)
  397. else:
  398. stillForwarded.add s
  399. swap w.forwardedSyms, stillForwarded
  400. transitiveClosure(g)
  401. var nimid = 0
  402. for x in items(g.config.m.fileInfos):
  403. storeFilename(g, x.fullPath, FileIndex(nimid))
  404. inc nimid
  405. # ---------------- decoder -----------------------------------
  406. type
  407. BlobReader = object
  408. s: string
  409. pos: int
  410. using
  411. b: var BlobReader
  412. g: ModuleGraph
  413. proc loadSym(g; id: int, info: TLineInfo): PSym
  414. proc loadType(g; id: int, info: TLineInfo): PType
  415. proc decodeLineInfo(g; b; info: var TLineInfo) =
  416. if b.s[b.pos] == '?':
  417. inc(b.pos)
  418. if b.s[b.pos] == ',': info.col = -1'i16
  419. else: info.col = int16(decodeVInt(b.s, b.pos))
  420. if b.s[b.pos] == ',':
  421. inc(b.pos)
  422. if b.s[b.pos] == ',': info.line = 0'u16
  423. else: info.line = uint16(decodeVInt(b.s, b.pos))
  424. if b.s[b.pos] == ',':
  425. inc(b.pos)
  426. #info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos))
  427. info.fileIndex = FileIndex decodeVInt(b.s, b.pos)
  428. proc skipNode(b) =
  429. # ')' itself cannot be part of a string literal so that this is correct.
  430. assert b.s[b.pos] == '('
  431. var par = 0
  432. var pos = b.pos+1
  433. while true:
  434. case b.s[pos]
  435. of ')':
  436. if par == 0: break
  437. dec par
  438. of '(': inc par
  439. else: discard
  440. inc pos
  441. b.pos = pos+1 # skip ')'
  442. proc decodeNodeLazyBody(g; b; fInfo: TLineInfo,
  443. belongsTo: PSym): PNode =
  444. result = nil
  445. if b.s[b.pos] == '(':
  446. inc(b.pos)
  447. if b.s[b.pos] == ')':
  448. inc(b.pos)
  449. return # nil node
  450. result = newNodeI(TNodeKind(decodeVInt(b.s, b.pos)), fInfo)
  451. decodeLineInfo(g, b, result.info)
  452. if b.s[b.pos] == '$':
  453. inc(b.pos)
  454. result.flags = cast[TNodeFlags](int32(decodeVInt(b.s, b.pos)))
  455. if b.s[b.pos] == '^':
  456. inc(b.pos)
  457. var id = decodeVInt(b.s, b.pos)
  458. result.typ = loadType(g, id, result.info)
  459. case result.kind
  460. of nkCharLit..nkUInt64Lit:
  461. if b.s[b.pos] == '!':
  462. inc(b.pos)
  463. result.intVal = decodeVBiggestInt(b.s, b.pos)
  464. of nkFloatLit..nkFloat64Lit:
  465. if b.s[b.pos] == '!':
  466. inc(b.pos)
  467. var fl = decodeStr(b.s, b.pos)
  468. result.floatVal = parseFloat(fl)
  469. of nkStrLit..nkTripleStrLit:
  470. if b.s[b.pos] == '!':
  471. inc(b.pos)
  472. result.strVal = decodeStr(b.s, b.pos)
  473. else:
  474. result.strVal = ""
  475. of nkIdent:
  476. if b.s[b.pos] == '!':
  477. inc(b.pos)
  478. var fl = decodeStr(b.s, b.pos)
  479. result.ident = g.cache.getIdent(fl)
  480. else:
  481. internalError(g.config, result.info, "decodeNode: nkIdent")
  482. of nkSym:
  483. if b.s[b.pos] == '!':
  484. inc(b.pos)
  485. var id = decodeVInt(b.s, b.pos)
  486. result.sym = loadSym(g, id, result.info)
  487. else:
  488. internalError(g.config, result.info, "decodeNode: nkSym")
  489. else:
  490. var i = 0
  491. while b.s[b.pos] != ')':
  492. when false:
  493. if belongsTo != nil and i == bodyPos:
  494. addSonNilAllowed(result, nil)
  495. belongsTo.offset = b.pos
  496. skipNode(b)
  497. else:
  498. discard
  499. addSonNilAllowed(result, decodeNodeLazyBody(g, b, result.info, nil))
  500. inc i
  501. if b.s[b.pos] == ')': inc(b.pos)
  502. else: internalError(g.config, result.info, "decodeNode: ')' missing")
  503. else:
  504. internalError(g.config, fInfo, "decodeNode: '(' missing " & $b.pos)
  505. proc decodeNode(g; b; fInfo: TLineInfo): PNode =
  506. result = decodeNodeLazyBody(g, b, fInfo, nil)
  507. proc decodeLoc(g; b; loc: var TLoc, info: TLineInfo) =
  508. if b.s[b.pos] == '<':
  509. inc(b.pos)
  510. if b.s[b.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
  511. loc.k = TLocKind(decodeVInt(b.s, b.pos))
  512. else:
  513. loc.k = low(loc.k)
  514. if b.s[b.pos] == '*':
  515. inc(b.pos)
  516. loc.storage = TStorageLoc(decodeVInt(b.s, b.pos))
  517. else:
  518. loc.storage = low(loc.storage)
  519. if b.s[b.pos] == '$':
  520. inc(b.pos)
  521. loc.flags = cast[TLocFlags](int32(decodeVInt(b.s, b.pos)))
  522. else:
  523. loc.flags = {}
  524. if b.s[b.pos] == '^':
  525. inc(b.pos)
  526. loc.lode = decodeNode(g, b, info)
  527. # rrGetType(b, decodeVInt(b.s, b.pos), info)
  528. else:
  529. loc.lode = nil
  530. if b.s[b.pos] == '!':
  531. inc(b.pos)
  532. loc.r = rope(decodeStr(b.s, b.pos))
  533. else:
  534. loc.r = nil
  535. if b.s[b.pos] == '>': inc(b.pos)
  536. else: internalError(g.config, info, "decodeLoc " & b.s[b.pos])
  537. proc loadBlob(g; query: SqlQuery; id: int): BlobReader =
  538. let blob = db.getValue(query, id)
  539. if blob.len == 0:
  540. internalError(g.config, "symbolfiles: cannot find ID " & $ id)
  541. result = BlobReader(pos: 0)
  542. shallowCopy(result.s, blob)
  543. # ensure we can read without index checks:
  544. result.s.add '\0'
  545. proc loadType(g; id: int; info: TLineInfo): PType =
  546. result = g.incr.r.types.getOrDefault(id)
  547. if result != nil: return result
  548. var b = loadBlob(g, sql"select data from types where nimid = ?", id)
  549. if b.s[b.pos] == '[':
  550. inc(b.pos)
  551. if b.s[b.pos] == ']':
  552. inc(b.pos)
  553. return # nil type
  554. new(result)
  555. result.kind = TTypeKind(decodeVInt(b.s, b.pos))
  556. if b.s[b.pos] == '+':
  557. inc(b.pos)
  558. result.uniqueId = decodeVInt(b.s, b.pos)
  559. setId(result.uniqueId)
  560. #if debugIds: registerID(result)
  561. else:
  562. internalError(g.config, info, "decodeType: no id")
  563. if b.s[b.pos] == '+':
  564. inc(b.pos)
  565. result.id = decodeVInt(b.s, b.pos)
  566. else:
  567. result.id = result.uniqueId
  568. # here this also avoids endless recursion for recursive type
  569. g.incr.r.types.add(result.uniqueId, result)
  570. if b.s[b.pos] == '(': result.n = decodeNode(g, b, unknownLineInfo)
  571. if b.s[b.pos] == '$':
  572. inc(b.pos)
  573. result.flags = cast[TTypeFlags](int32(decodeVInt(b.s, b.pos)))
  574. if b.s[b.pos] == '?':
  575. inc(b.pos)
  576. result.callConv = TCallingConvention(decodeVInt(b.s, b.pos))
  577. if b.s[b.pos] == '*':
  578. inc(b.pos)
  579. result.owner = loadSym(g, decodeVInt(b.s, b.pos), info)
  580. if b.s[b.pos] == '&':
  581. inc(b.pos)
  582. result.sym = loadSym(g, decodeVInt(b.s, b.pos), info)
  583. if b.s[b.pos] == '/':
  584. inc(b.pos)
  585. result.size = decodeVInt(b.s, b.pos)
  586. else:
  587. result.size = -1
  588. if b.s[b.pos] == '=':
  589. inc(b.pos)
  590. result.align = decodeVInt(b.s, b.pos).int16
  591. else:
  592. result.align = 2
  593. if b.s[b.pos] == '\14':
  594. inc(b.pos)
  595. result.lockLevel = decodeVInt(b.s, b.pos).TLockLevel
  596. else:
  597. result.lockLevel = UnspecifiedLockLevel
  598. if b.s[b.pos] == '\15':
  599. inc(b.pos)
  600. result.paddingAtEnd = decodeVInt(b.s, b.pos).int16
  601. for a in low(result.attachedOps)..high(result.attachedOps):
  602. if b.s[b.pos] == '\16':
  603. inc(b.pos)
  604. let id = decodeVInt(b.s, b.pos)
  605. if id >= 0:
  606. result.attachedOps[a] = loadSym(g, id, info)
  607. while b.s[b.pos] == '\19':
  608. inc(b.pos)
  609. let x = decodeVInt(b.s, b.pos)
  610. doAssert b.s[b.pos] == '\20'
  611. inc(b.pos)
  612. let y = loadSym(g, decodeVInt(b.s, b.pos), info)
  613. result.methods.add((x, y))
  614. decodeLoc(g, b, result.loc, info)
  615. if b.s[b.pos] == '\21':
  616. inc(b.pos)
  617. let d = decodeVInt(b.s, b.pos)
  618. result.typeInst = loadType(g, d, info)
  619. while b.s[b.pos] == '^':
  620. inc(b.pos)
  621. if b.s[b.pos] == '(':
  622. inc(b.pos)
  623. if b.s[b.pos] == ')': inc(b.pos)
  624. else: internalError(g.config, info, "decodeType ^(" & b.s[b.pos])
  625. rawAddSon(result, nil)
  626. else:
  627. let d = decodeVInt(b.s, b.pos)
  628. result.sons.add loadType(g, d, info)
  629. proc decodeLib(g; b; info: TLineInfo): PLib =
  630. result = nil
  631. if b.s[b.pos] == '|':
  632. new(result)
  633. inc(b.pos)
  634. result.kind = TLibKind(decodeVInt(b.s, b.pos))
  635. if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 1")
  636. inc(b.pos)
  637. result.name = rope(decodeStr(b.s, b.pos))
  638. if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 2")
  639. inc(b.pos)
  640. result.path = decodeNode(g, b, info)
  641. proc decodeInstantiations(g; b; info: TLineInfo;
  642. s: var seq[PInstantiation]) =
  643. while b.s[b.pos] == '\15':
  644. inc(b.pos)
  645. var ii: PInstantiation
  646. new ii
  647. ii.sym = loadSym(g, decodeVInt(b.s, b.pos), info)
  648. ii.concreteTypes = @[]
  649. while b.s[b.pos] == '\17':
  650. inc(b.pos)
  651. ii.concreteTypes.add loadType(g, decodeVInt(b.s, b.pos), info)
  652. if b.s[b.pos] == '\20':
  653. inc(b.pos)
  654. ii.compilesId = decodeVInt(b.s, b.pos)
  655. s.add ii
  656. proc loadSymFromBlob(g; b; info: TLineInfo): PSym =
  657. if b.s[b.pos] == '{':
  658. inc(b.pos)
  659. if b.s[b.pos] == '}':
  660. inc(b.pos)
  661. return # nil sym
  662. var k = TSymKind(decodeVInt(b.s, b.pos))
  663. var id: int
  664. if b.s[b.pos] == '+':
  665. inc(b.pos)
  666. id = decodeVInt(b.s, b.pos)
  667. setId(id)
  668. else:
  669. internalError(g.config, info, "decodeSym: no id")
  670. var ident: PIdent
  671. if b.s[b.pos] == '&':
  672. inc(b.pos)
  673. ident = g.cache.getIdent(decodeStr(b.s, b.pos))
  674. else:
  675. internalError(g.config, info, "decodeSym: no ident")
  676. #echo "decoding: {", ident.s
  677. result = PSym(id: id, kind: k, name: ident)
  678. # read the rest of the symbol description:
  679. g.incr.r.syms.add(result.id, result)
  680. if b.s[b.pos] == '^':
  681. inc(b.pos)
  682. result.typ = loadType(g, decodeVInt(b.s, b.pos), info)
  683. decodeLineInfo(g, b, result.info)
  684. if b.s[b.pos] == '*':
  685. inc(b.pos)
  686. result.owner = loadSym(g, decodeVInt(b.s, b.pos), result.info)
  687. if b.s[b.pos] == '$':
  688. inc(b.pos)
  689. result.flags = cast[TSymFlags](decodeVBiggestInt(b.s, b.pos))
  690. if b.s[b.pos] == '@':
  691. inc(b.pos)
  692. result.magic = TMagic(decodeVInt(b.s, b.pos))
  693. if b.s[b.pos] == '!':
  694. inc(b.pos)
  695. result.options = cast[TOptions](int32(decodeVInt(b.s, b.pos)))
  696. if b.s[b.pos] == '%':
  697. inc(b.pos)
  698. result.position = decodeVInt(b.s, b.pos)
  699. if b.s[b.pos] == '`':
  700. inc(b.pos)
  701. result.offset = decodeVInt(b.s, b.pos)
  702. else:
  703. result.offset = -1
  704. decodeLoc(g, b, result.loc, result.info)
  705. result.annex = decodeLib(g, b, info)
  706. if b.s[b.pos] == '#':
  707. inc(b.pos)
  708. result.constraint = decodeNode(g, b, unknownLineInfo)
  709. case result.kind
  710. of skType, skGenericParam:
  711. while b.s[b.pos] == '\14':
  712. inc(b.pos)
  713. result.typeInstCache.add loadType(g, decodeVInt(b.s, b.pos), result.info)
  714. of routineKinds:
  715. decodeInstantiations(g, b, result.info, result.procInstCache)
  716. if b.s[b.pos] == '\16':
  717. inc(b.pos)
  718. result.gcUnsafetyReason = loadSym(g, decodeVInt(b.s, b.pos), result.info)
  719. if b.s[b.pos] == '\24':
  720. inc b.pos
  721. result.transformedBody = decodeNode(g, b, result.info)
  722. #result.transformedBody = nil
  723. of skModule, skPackage:
  724. decodeInstantiations(g, b, result.info, result.usedGenerics)
  725. of skLet, skVar, skField, skForVar:
  726. if b.s[b.pos] == '\18':
  727. inc(b.pos)
  728. result.guard = loadSym(g, decodeVInt(b.s, b.pos), result.info)
  729. if b.s[b.pos] == '\19':
  730. inc(b.pos)
  731. result.bitsize = decodeVInt(b.s, b.pos).int16
  732. else: discard
  733. if b.s[b.pos] == '(':
  734. #if result.kind in routineKinds:
  735. # result.ast = nil
  736. #else:
  737. result.ast = decodeNode(g, b, result.info)
  738. if sfCompilerProc in result.flags:
  739. registerCompilerProc(g, result)
  740. #echo "loading ", result.name.s
  741. proc loadSym(g; id: int; info: TLineInfo): PSym =
  742. result = g.incr.r.syms.getOrDefault(id)
  743. if result != nil: return result
  744. var b = loadBlob(g, sql"select data from syms where nimid = ?", id)
  745. result = loadSymFromBlob(g, b, info)
  746. doAssert id == result.id, "symbol ID is not consistent!"
  747. proc registerModule*(g; module: PSym) =
  748. g.incr.r.syms.add(abs module.id, module)
  749. proc loadModuleSymTab(g; module: PSym) =
  750. ## goal: fill module.tab
  751. g.incr.r.syms.add(module.id, module)
  752. for row in db.fastRows(sql"select nimid, data from syms where module = ? and exported = 1", abs(module.id)):
  753. let id = parseInt(row[0])
  754. var s = g.incr.r.syms.getOrDefault(id)
  755. if s == nil:
  756. var b = BlobReader(pos: 0)
  757. shallowCopy(b.s, row[1])
  758. # ensure we can read without index checks:
  759. b.s.add '\0'
  760. s = loadSymFromBlob(g, b, module.info)
  761. assert s != nil
  762. if s.kind != skField:
  763. strTableAdd(module.tab, s)
  764. if sfSystemModule in module.flags:
  765. g.systemModule = module
  766. proc replay(g: ModuleGraph; module: PSym; n: PNode) =
  767. # XXX check if we need to replay nkStaticStmt here.
  768. case n.kind
  769. #of nkStaticStmt:
  770. #evalStaticStmt(module, g, n[0], module)
  771. #of nkVarSection, nkLetSection:
  772. # nkVarSections are already covered by the vmgen which produces nkStaticStmt
  773. of nkMethodDef:
  774. methodDef(g, n[namePos].sym, fromCache=true)
  775. of nkCommentStmt:
  776. # pragmas are complex and can be user-overriden via templates. So
  777. # instead of using the original ``nkPragma`` nodes, we rely on the
  778. # fact that pragmas.nim was patched to produce specialized recorded
  779. # statements for us in the form of ``nkCommentStmt`` with (key, value)
  780. # pairs. Ordinary nkCommentStmt nodes never have children so this is
  781. # not ambiguous.
  782. # Fortunately only a tiny subset of the available pragmas need to
  783. # be replayed here. This is always a subset of ``pragmas.stmtPragmas``.
  784. if n.len >= 2:
  785. internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit
  786. case n[0].strVal
  787. of "hint": message(g.config, n.info, hintUser, n[1].strVal)
  788. of "warning": message(g.config, n.info, warnUser, n[1].strVal)
  789. of "error": localError(g.config, n.info, errUser, n[1].strVal)
  790. of "compile":
  791. internalAssert g.config, n.len == 3 and n[2].kind == nkStrLit
  792. let cname = AbsoluteFile n[1].strVal
  793. var cf = Cfile(nimname: splitFile(cname).name, cname: cname,
  794. obj: AbsoluteFile n[2].strVal,
  795. flags: {CfileFlag.External})
  796. extccomp.addExternalFileToCompile(g.config, cf)
  797. of "link":
  798. extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal)
  799. of "passl":
  800. extccomp.addLinkOption(g.config, n[1].strVal)
  801. of "passc":
  802. extccomp.addCompileOption(g.config, n[1].strVal)
  803. of "localpassc":
  804. extccomp.addLocalCompileOption(g.config, n[1].strVal, toFullPathConsiderDirty(g.config, module.info.fileIndex))
  805. of "cppdefine":
  806. options.cppDefine(g.config, n[1].strVal)
  807. of "inc":
  808. let destKey = n[1].strVal
  809. let by = n[2].intVal
  810. let v = getOrDefault(g.cacheCounters, destKey)
  811. g.cacheCounters[destKey] = v+by
  812. of "put":
  813. let destKey = n[1].strVal
  814. let key = n[2].strVal
  815. let val = n[3]
  816. if not contains(g.cacheTables, destKey):
  817. g.cacheTables[destKey] = initBTree[string, PNode]()
  818. if not contains(g.cacheTables[destKey], key):
  819. g.cacheTables[destKey].add(key, val)
  820. else:
  821. internalError(g.config, n.info, "key already exists: " & key)
  822. of "incl":
  823. let destKey = n[1].strVal
  824. let val = n[2]
  825. if not contains(g.cacheSeqs, destKey):
  826. g.cacheSeqs[destKey] = newTree(nkStmtList, val)
  827. else:
  828. block search:
  829. for existing in g.cacheSeqs[destKey]:
  830. if exprStructuralEquivalent(existing, val, strictSymEquality=true):
  831. break search
  832. g.cacheSeqs[destKey].add val
  833. of "add":
  834. let destKey = n[1].strVal
  835. let val = n[2]
  836. if not contains(g.cacheSeqs, destKey):
  837. g.cacheSeqs[destKey] = newTree(nkStmtList, val)
  838. else:
  839. g.cacheSeqs[destKey].add val
  840. else:
  841. internalAssert g.config, false
  842. of nkImportStmt:
  843. for x in n:
  844. internalAssert g.config, x.kind == nkSym
  845. let modpath = AbsoluteFile toFullPath(g.config, x.sym.info)
  846. let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, modpath))
  847. internalAssert g.config, imported.id < 0
  848. of nkStmtList, nkStmtListExpr:
  849. for x in n: replay(g, module, x)
  850. of nkExportStmt:
  851. for x in n:
  852. doAssert x.kind == nkSym
  853. strTableAdd(module.tab, x.sym)
  854. else: discard "nothing to do for this node"
  855. proc loadNode*(g: ModuleGraph; module: PSym): PNode =
  856. loadModuleSymTab(g, module)
  857. result = newNodeI(nkStmtList, module.info)
  858. for row in db.rows(sql"select data from toplevelstmts where module = ? order by position asc",
  859. abs module.id):
  860. var b = BlobReader(pos: 0)
  861. # ensure we can read without index checks:
  862. b.s = row[0] & '\0'
  863. result.add decodeNode(g, b, module.info)
  864. db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId)
  865. replay(g, module, result)
  866. proc setupModuleCache*(g: ModuleGraph) =
  867. # historical note: there used to be a `rodfiles` dir with special tests
  868. # for incremental compilation via symbol files. This was likely replaced by ic.
  869. if g.config.symbolFiles == disabledSf: return
  870. g.recordStmt = recordStmt
  871. let dbfile = getNimcacheDir(g.config) / RelativeFile"rodfiles.db"
  872. if g.config.symbolFiles == writeOnlySf:
  873. removeFile(dbfile)
  874. createDir getNimcacheDir(g.config)
  875. let ec = encodeConfig(g)
  876. if not fileExists(dbfile):
  877. db = open(connection=string dbfile, user="nim", password="",
  878. database="nim")
  879. createDb(db)
  880. db.exec(sql"insert into config(config) values (?)", ec)
  881. else:
  882. db = open(connection=string dbfile, user="nim", password="",
  883. database="nim")
  884. let oldConfig = db.getValue(sql"select config from config")
  885. g.incr.configChanged = oldConfig != ec
  886. # ensure the filename IDs stay consistent:
  887. for row in db.rows(sql"select fullpath, nimid from filenames order by nimid"):
  888. let id = fileInfoIdx(g.config, AbsoluteFile row[0])
  889. doAssert id.int == parseInt(row[1])
  890. db.exec(sql"update config set config = ?", ec)
  891. db.exec(sql"pragma journal_mode=off")
  892. # This MUST be turned off, otherwise it's way too slow even for testing purposes:
  893. db.exec(sql"pragma SYNCHRONOUS=off")
  894. db.exec(sql"pragma LOCKING_MODE=exclusive")
  895. let lastId = db.getValue(sql"select max(idgen) from controlblock")
  896. if lastId.len > 0:
  897. idgen.setId(parseInt lastId)