rodread.nim 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2013 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # This module is responsible for loading of rod files.
  10. #
  11. # Reading and writing binary files are really hard to debug. Therefore we use
  12. # a "creative" text/binary hybrid format. ROD-files are more efficient
  13. # to process because symbols can be loaded on demand.
  14. #
  15. # A ROD file consists of:
  16. #
  17. # - a header:
  18. # NIM:$fileversion\n
  19. # - the module's id (even if the module changed, its ID will not!):
  20. # ID:Ax3\n
  21. # - HASH value of this module:
  22. # HASH:HASH-val\n
  23. # - a section containing the compiler options and defines this
  24. # module has been compiled with:
  25. # OPTIONS:options\n
  26. # GOPTIONS:options\n # global options
  27. # CMD:command\n
  28. # DEFINES:defines\n
  29. # - FILES(
  30. # myfile.inc
  31. # lib/mymodA
  32. # )
  33. # - an include file dependency section:
  34. # INCLUDES(
  35. # <fileidx> <Hash of myfile.inc>\n # fileidx is the LINE in the file section!
  36. # )
  37. # - a module dependency section:
  38. # DEPS: <fileidx> <fileidx>\n
  39. # - an interface section:
  40. # INTERF(
  41. # identifier1 id\n # id is the symbol's id
  42. # identifier2 id\n
  43. # )
  44. # - a compiler proc section:
  45. # COMPILERPROCS(
  46. # identifier1 id\n # id is the symbol's id
  47. # )
  48. # - an index consisting of (ID, linenumber)-pairs:
  49. # INDEX(
  50. # id-diff idx-diff\n
  51. # id-diff idx-diff\n
  52. # )
  53. #
  54. # Since the whole index has to be read in advance, we compress it by
  55. # storing the integer differences to the last entry instead of using the
  56. # real numbers.
  57. #
  58. # - an import index consisting of (ID, moduleID)-pairs:
  59. # IMPORTS(
  60. # id-diff moduleID-diff\n
  61. # id-diff moduleID-diff\n
  62. # )
  63. # - a list of all exported type converters because they are needed for correct
  64. # semantic checking:
  65. # CONVERTERS:id id\n # symbol ID
  66. #
  67. # This is a misnomer now; it's really a "load unconditionally" section as
  68. # it is also used for pattern templates.
  69. #
  70. # - a list of all (private or exported) methods because they are needed for
  71. # correct dispatcher generation:
  72. # METHODS: id id\n # symbol ID
  73. # - an AST section that contains the module's AST:
  74. # INIT(
  75. # idx\n # position of the node in the DATA section
  76. # idx\n
  77. # )
  78. # - a data section, where each type, symbol or AST is stored.
  79. # DATA(
  80. # type
  81. # (node)
  82. # sym
  83. # )
  84. #
  85. # The data section MUST be the last section of the file, because processing
  86. # stops immediately after ``DATA(`` and the rest is only loaded on demand
  87. # by using a mem'mapped file.
  88. #
  89. import
  90. os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
  91. ropes, idents, securehash, idgen, types, rodutils, memfiles, tables
  92. type
  93. TReasonForRecompile* = enum ## all the reasons that can trigger recompilation
  94. rrEmpty, # dependencies not yet computed
  95. rrNone, # no need to recompile
  96. rrRodDoesNotExist, # rod file does not exist
  97. rrRodInvalid, # rod file is invalid
  98. rrHashChange, # file has been edited since last recompilation
  99. rrDefines, # defines have changed
  100. rrOptions, # options have changed
  101. rrInclDeps, # an include has changed
  102. rrModDeps # a module this module depends on has been changed
  103. const
  104. reasonToFrmt*: array[TReasonForRecompile, string] = ["",
  105. "no need to recompile: $1", "symbol file for $1 does not exist",
  106. "symbol file for $1 has the wrong version",
  107. "file edited since last compilation: $1",
  108. "list of conditional symbols changed for: $1",
  109. "list of options changed for: $1",
  110. "an include file edited: $1",
  111. "a module $1 depends on has changed"]
  112. type
  113. TIndex*{.final.} = object # an index with compression
  114. lastIdxKey*, lastIdxVal*: int
  115. tab*: TIITable
  116. r*: string # writers use this
  117. offset*: int # readers use this
  118. TRodReader* = object of RootObj
  119. pos: int # position; used for parsing
  120. s: cstring # mmap'ed file contents
  121. options: TOptions
  122. reason: TReasonForRecompile
  123. modDeps: seq[int32]
  124. files: seq[int32]
  125. dataIdx: int # offset of start of data section
  126. convertersIdx: int # offset of start of converters section
  127. initIdx, interfIdx, compilerProcsIdx, methodsIdx: int
  128. filename: string
  129. index, imports: TIndex
  130. readerIndex: int
  131. line: int # only used for debugging, but is always in the code
  132. moduleID: int
  133. syms: Table[int, PSym] # already processed symbols
  134. memfile: MemFile # unfortunately there is no point in time where we
  135. # can close this! XXX
  136. methods*: TSymSeq
  137. origFile: string
  138. inViewMode: bool
  139. cache*: IdentCache
  140. PRodReader* = ref TRodReader
  141. var rodCompilerprocs*: TStrTable # global because this is needed by magicsys
  142. proc rawLoadStub(s: PSym)
  143. var gTypeTable: TIdTable
  144. proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym
  145. # `info` is only used for debugging purposes
  146. proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType
  147. proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
  148. if r.s[r.pos] == '?':
  149. inc(r.pos)
  150. if r.s[r.pos] == ',': info.col = -1'i16
  151. else: info.col = int16(decodeVInt(r.s, r.pos))
  152. if r.s[r.pos] == ',':
  153. inc(r.pos)
  154. if r.s[r.pos] == ',': info.line = -1'i16
  155. else: info.line = int16(decodeVInt(r.s, r.pos))
  156. if r.s[r.pos] == ',':
  157. inc(r.pos)
  158. info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], info.line, info.col)
  159. proc skipNode(r: PRodReader) =
  160. assert r.s[r.pos] == '('
  161. var par = 0
  162. var pos = r.pos+1
  163. while true:
  164. case r.s[pos]
  165. of ')':
  166. if par == 0: break
  167. dec par
  168. of '(': inc par
  169. else: discard
  170. inc pos
  171. r.pos = pos+1 # skip ')'
  172. proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
  173. belongsTo: PSym): PNode =
  174. result = nil
  175. if r.s[r.pos] == '(':
  176. inc(r.pos)
  177. if r.s[r.pos] == ')':
  178. inc(r.pos)
  179. return # nil node
  180. result = newNodeI(TNodeKind(decodeVInt(r.s, r.pos)), fInfo)
  181. decodeLineInfo(r, result.info)
  182. if r.s[r.pos] == '$':
  183. inc(r.pos)
  184. result.flags = cast[TNodeFlags](int32(decodeVInt(r.s, r.pos)))
  185. if r.s[r.pos] == '^':
  186. inc(r.pos)
  187. var id = decodeVInt(r.s, r.pos)
  188. result.typ = rrGetType(r, id, result.info)
  189. case result.kind
  190. of nkCharLit..nkUInt64Lit:
  191. if r.s[r.pos] == '!':
  192. inc(r.pos)
  193. result.intVal = decodeVBiggestInt(r.s, r.pos)
  194. of nkFloatLit..nkFloat64Lit:
  195. if r.s[r.pos] == '!':
  196. inc(r.pos)
  197. var fl = decodeStr(r.s, r.pos)
  198. result.floatVal = parseFloat(fl)
  199. of nkStrLit..nkTripleStrLit:
  200. if r.s[r.pos] == '!':
  201. inc(r.pos)
  202. result.strVal = decodeStr(r.s, r.pos)
  203. else:
  204. result.strVal = "" # BUGFIX
  205. of nkIdent:
  206. if r.s[r.pos] == '!':
  207. inc(r.pos)
  208. var fl = decodeStr(r.s, r.pos)
  209. result.ident = r.cache.getIdent(fl)
  210. else:
  211. internalError(result.info, "decodeNode: nkIdent")
  212. of nkSym:
  213. if r.s[r.pos] == '!':
  214. inc(r.pos)
  215. var id = decodeVInt(r.s, r.pos)
  216. result.sym = rrGetSym(r, id, result.info)
  217. else:
  218. internalError(result.info, "decodeNode: nkSym")
  219. else:
  220. var i = 0
  221. while r.s[r.pos] != ')':
  222. if belongsTo != nil and i == bodyPos:
  223. addSonNilAllowed(result, nil)
  224. belongsTo.offset = r.pos
  225. skipNode(r)
  226. else:
  227. addSonNilAllowed(result, decodeNodeLazyBody(r, result.info, nil))
  228. inc i
  229. if r.s[r.pos] == ')': inc(r.pos)
  230. else: internalError(result.info, "decodeNode: ')' missing")
  231. else:
  232. internalError(fInfo, "decodeNode: '(' missing " & $r.pos)
  233. proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode =
  234. result = decodeNodeLazyBody(r, fInfo, nil)
  235. proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
  236. if r.s[r.pos] == '<':
  237. inc(r.pos)
  238. if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
  239. loc.k = TLocKind(decodeVInt(r.s, r.pos))
  240. else:
  241. loc.k = low(loc.k)
  242. if r.s[r.pos] == '*':
  243. inc(r.pos)
  244. loc.storage = TStorageLoc(decodeVInt(r.s, r.pos))
  245. else:
  246. loc.storage = low(loc.storage)
  247. if r.s[r.pos] == '$':
  248. inc(r.pos)
  249. loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos)))
  250. else:
  251. loc.flags = {}
  252. if r.s[r.pos] == '^':
  253. inc(r.pos)
  254. loc.lode = decodeNode(r, info)
  255. # rrGetType(r, decodeVInt(r.s, r.pos), info)
  256. else:
  257. loc.lode = nil
  258. if r.s[r.pos] == '!':
  259. inc(r.pos)
  260. loc.r = rope(decodeStr(r.s, r.pos))
  261. else:
  262. loc.r = nil
  263. if r.s[r.pos] == '>': inc(r.pos)
  264. else: internalError(info, "decodeLoc " & r.s[r.pos])
  265. proc decodeType(r: PRodReader, info: TLineInfo): PType =
  266. result = nil
  267. if r.s[r.pos] == '[':
  268. inc(r.pos)
  269. if r.s[r.pos] == ']':
  270. inc(r.pos)
  271. return # nil type
  272. new(result)
  273. result.kind = TTypeKind(decodeVInt(r.s, r.pos))
  274. if r.s[r.pos] == '+':
  275. inc(r.pos)
  276. result.id = decodeVInt(r.s, r.pos)
  277. setId(result.id)
  278. if debugIds: registerID(result)
  279. else:
  280. internalError(info, "decodeType: no id")
  281. # here this also avoids endless recursion for recursive type
  282. idTablePut(gTypeTable, result, result)
  283. if r.s[r.pos] == '(': result.n = decodeNode(r, unknownLineInfo())
  284. if r.s[r.pos] == '$':
  285. inc(r.pos)
  286. result.flags = cast[TTypeFlags](int32(decodeVInt(r.s, r.pos)))
  287. if r.s[r.pos] == '?':
  288. inc(r.pos)
  289. result.callConv = TCallingConvention(decodeVInt(r.s, r.pos))
  290. if r.s[r.pos] == '*':
  291. inc(r.pos)
  292. result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), info)
  293. if r.s[r.pos] == '&':
  294. inc(r.pos)
  295. result.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info)
  296. if r.s[r.pos] == '/':
  297. inc(r.pos)
  298. result.size = decodeVInt(r.s, r.pos)
  299. else:
  300. result.size = - 1
  301. if r.s[r.pos] == '=':
  302. inc(r.pos)
  303. result.align = decodeVInt(r.s, r.pos).int16
  304. else:
  305. result.align = 2
  306. if r.s[r.pos] == '\14':
  307. inc(r.pos)
  308. result.lockLevel = decodeVInt(r.s, r.pos).TLockLevel
  309. else:
  310. result.lockLevel = UnspecifiedLockLevel
  311. if r.s[r.pos] == '\15':
  312. inc(r.pos)
  313. result.destructor = rrGetSym(r, decodeVInt(r.s, r.pos), info)
  314. if r.s[r.pos] == '\16':
  315. inc(r.pos)
  316. result.deepCopy = rrGetSym(r, decodeVInt(r.s, r.pos), info)
  317. if r.s[r.pos] == '\17':
  318. inc(r.pos)
  319. result.assignment = rrGetSym(r, decodeVInt(r.s, r.pos), info)
  320. if r.s[r.pos] == '\18':
  321. inc(r.pos)
  322. result.sink = rrGetSym(r, decodeVInt(r.s, r.pos), info)
  323. while r.s[r.pos] == '\19':
  324. inc(r.pos)
  325. let x = decodeVInt(r.s, r.pos)
  326. doAssert r.s[r.pos] == '\20'
  327. inc(r.pos)
  328. let y = rrGetSym(r, decodeVInt(r.s, r.pos), info)
  329. result.methods.safeAdd((x, y))
  330. decodeLoc(r, result.loc, info)
  331. while r.s[r.pos] == '^':
  332. inc(r.pos)
  333. if r.s[r.pos] == '(':
  334. inc(r.pos)
  335. if r.s[r.pos] == ')': inc(r.pos)
  336. else: internalError(info, "decodeType ^(" & r.s[r.pos])
  337. rawAddSon(result, nil)
  338. else:
  339. var d = decodeVInt(r.s, r.pos)
  340. rawAddSon(result, rrGetType(r, d, info))
  341. proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
  342. result = nil
  343. if r.s[r.pos] == '|':
  344. new(result)
  345. inc(r.pos)
  346. result.kind = TLibKind(decodeVInt(r.s, r.pos))
  347. if r.s[r.pos] != '|': internalError("decodeLib: 1")
  348. inc(r.pos)
  349. result.name = rope(decodeStr(r.s, r.pos))
  350. if r.s[r.pos] != '|': internalError("decodeLib: 2")
  351. inc(r.pos)
  352. result.path = decodeNode(r, info)
  353. proc decodeInstantiations(r: PRodReader; info: TLineInfo;
  354. s: var seq[PInstantiation]) =
  355. while r.s[r.pos] == '\15':
  356. inc(r.pos)
  357. var ii: PInstantiation
  358. new ii
  359. ii.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info)
  360. ii.concreteTypes = @[]
  361. while r.s[r.pos] == '\17':
  362. inc(r.pos)
  363. ii.concreteTypes.add rrGetType(r, decodeVInt(r.s, r.pos), info)
  364. if r.s[r.pos] == '\20':
  365. inc(r.pos)
  366. ii.compilesId = decodeVInt(r.s, r.pos)
  367. s.safeAdd ii
  368. proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
  369. var
  370. id: int
  371. ident: PIdent
  372. result = nil
  373. if r.s[r.pos] == '{':
  374. inc(r.pos)
  375. if r.s[r.pos] == '}':
  376. inc(r.pos)
  377. return # nil sym
  378. var k = TSymKind(decodeVInt(r.s, r.pos))
  379. if r.s[r.pos] == '+':
  380. inc(r.pos)
  381. id = decodeVInt(r.s, r.pos)
  382. setId(id)
  383. else:
  384. internalError(info, "decodeSym: no id")
  385. if r.s[r.pos] == '&':
  386. inc(r.pos)
  387. ident = r.cache.getIdent(decodeStr(r.s, r.pos))
  388. else:
  389. internalError(info, "decodeSym: no ident")
  390. #echo "decoding: {", ident.s
  391. result = r.syms.getOrDefault(id)
  392. if result == nil:
  393. new(result)
  394. result.id = id
  395. r.syms[result.id] = result
  396. if debugIds: registerID(result)
  397. elif result.id != id:
  398. internalError(info, "decodeSym: wrong id")
  399. elif result.kind != skStub and not r.inViewMode:
  400. # we already loaded the symbol
  401. return
  402. else:
  403. reset(result[])
  404. result.id = id
  405. result.kind = k
  406. result.name = ident # read the rest of the symbol description:
  407. if r.s[r.pos] == '^':
  408. inc(r.pos)
  409. result.typ = rrGetType(r, decodeVInt(r.s, r.pos), info)
  410. decodeLineInfo(r, result.info)
  411. if r.s[r.pos] == '*':
  412. inc(r.pos)
  413. result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
  414. if r.s[r.pos] == '$':
  415. inc(r.pos)
  416. result.flags = cast[TSymFlags](int32(decodeVInt(r.s, r.pos)))
  417. if r.s[r.pos] == '@':
  418. inc(r.pos)
  419. result.magic = TMagic(decodeVInt(r.s, r.pos))
  420. if r.s[r.pos] == '!':
  421. inc(r.pos)
  422. result.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
  423. else:
  424. result.options = r.options
  425. if r.s[r.pos] == '%':
  426. inc(r.pos)
  427. result.position = decodeVInt(r.s, r.pos)
  428. elif result.kind notin routineKinds + {skModule}:
  429. result.position = 0
  430. # this may have been misused as reader index! But we still
  431. # need it for routines as the body is loaded lazily.
  432. if r.s[r.pos] == '`':
  433. inc(r.pos)
  434. result.offset = decodeVInt(r.s, r.pos)
  435. else:
  436. result.offset = - 1
  437. decodeLoc(r, result.loc, result.info)
  438. result.annex = decodeLib(r, info)
  439. if r.s[r.pos] == '#':
  440. inc(r.pos)
  441. result.constraint = decodeNode(r, unknownLineInfo())
  442. case result.kind
  443. of skType, skGenericParam:
  444. while r.s[r.pos] == '\14':
  445. inc(r.pos)
  446. result.typeInstCache.safeAdd rrGetType(r, decodeVInt(r.s, r.pos), result.info)
  447. of routineKinds:
  448. decodeInstantiations(r, result.info, result.procInstCache)
  449. if r.s[r.pos] == '\16':
  450. inc(r.pos)
  451. result.gcUnsafetyReason = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
  452. of skModule, skPackage:
  453. decodeInstantiations(r, result.info, result.usedGenerics)
  454. of skLet, skVar, skField, skForVar:
  455. if r.s[r.pos] == '\18':
  456. inc(r.pos)
  457. result.guard = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
  458. if r.s[r.pos] == '\19':
  459. inc(r.pos)
  460. result.bitsize = decodeVInt(r.s, r.pos).int16
  461. else: discard
  462. if r.s[r.pos] == '(':
  463. if result.kind in routineKinds:
  464. result.ast = decodeNodeLazyBody(r, result.info, result)
  465. # since we load the body lazily, we need to set the reader to
  466. # be able to reload:
  467. result.position = r.readerIndex
  468. else:
  469. result.ast = decodeNode(r, result.info)
  470. #echo "decoded: ", ident.s, "}"
  471. proc skipSection(r: PRodReader) =
  472. if r.s[r.pos] == ':':
  473. while r.s[r.pos] > '\x0A': inc(r.pos)
  474. elif r.s[r.pos] == '(':
  475. var c = 0 # count () pairs
  476. inc(r.pos)
  477. while true:
  478. case r.s[r.pos]
  479. of '\x0A': inc(r.line)
  480. of '(': inc(c)
  481. of ')':
  482. if c == 0:
  483. inc(r.pos)
  484. break
  485. elif c > 0:
  486. dec(c)
  487. of '\0': break # end of file
  488. else: discard
  489. inc(r.pos)
  490. else:
  491. internalError("skipSection " & $r.line)
  492. proc rdWord(r: PRodReader): string =
  493. result = ""
  494. while r.s[r.pos] in {'A'..'Z', '_', 'a'..'z', '0'..'9'}:
  495. add(result, r.s[r.pos])
  496. inc(r.pos)
  497. proc newStub(r: PRodReader, name: string, id: int): PSym =
  498. new(result)
  499. result.kind = skStub
  500. result.id = id
  501. result.name = r.cache.getIdent(name)
  502. result.position = r.readerIndex
  503. setId(id) #MessageOut(result.name.s);
  504. if debugIds: registerID(result)
  505. proc processInterf(r: PRodReader, module: PSym) =
  506. if r.interfIdx == 0: internalError("processInterf")
  507. r.pos = r.interfIdx
  508. while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
  509. var w = decodeStr(r.s, r.pos)
  510. inc(r.pos)
  511. var key = decodeVInt(r.s, r.pos)
  512. inc(r.pos) # #10
  513. var s = newStub(r, w, key)
  514. s.owner = module
  515. strTableAdd(module.tab, s)
  516. r.syms[s.id] = s
  517. proc processCompilerProcs(r: PRodReader, module: PSym) =
  518. if r.compilerProcsIdx == 0: internalError("processCompilerProcs")
  519. r.pos = r.compilerProcsIdx
  520. while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
  521. var w = decodeStr(r.s, r.pos)
  522. inc(r.pos)
  523. var key = decodeVInt(r.s, r.pos)
  524. inc(r.pos) # #10
  525. var s = r.syms.getOrDefault(key)
  526. if s == nil:
  527. s = newStub(r, w, key)
  528. s.owner = module
  529. r.syms[s.id] = s
  530. strTableAdd(rodCompilerprocs, s)
  531. proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) =
  532. var key, val, tmp: int
  533. inc(r.pos, 2) # skip "(\10"
  534. inc(r.line)
  535. while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
  536. tmp = decodeVInt(r.s, r.pos)
  537. if r.s[r.pos] == ' ':
  538. inc(r.pos)
  539. key = idx.lastIdxKey + tmp
  540. val = decodeVInt(r.s, r.pos) + idx.lastIdxVal
  541. else:
  542. key = idx.lastIdxKey + 1
  543. val = tmp + idx.lastIdxVal
  544. iiTablePut(idx.tab, key, val)
  545. if not outf.isNil: outf.write(key, " ", val, "\n")
  546. idx.lastIdxKey = key
  547. idx.lastIdxVal = val
  548. setId(key) # ensure that this id will not be used
  549. if r.s[r.pos] == '\x0A':
  550. inc(r.pos)
  551. inc(r.line)
  552. if r.s[r.pos] == ')': inc(r.pos)
  553. proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
  554. if old == new: return false
  555. # we use a 'case' statement without 'else' so that addition of a
  556. # new command forces us to consider it here :-)
  557. case old
  558. of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
  559. cmdCompileToJS, cmdCompileToPHP, cmdCompileToLLVM:
  560. if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef,
  561. cmdInteractive}:
  562. return false
  563. of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump,
  564. cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef,
  565. cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun, cmdJsonScript:
  566. discard
  567. # else: trigger recompilation:
  568. result = true
  569. proc processRodFile(r: PRodReader, hash: SecureHash) =
  570. var
  571. w: string
  572. d: int
  573. var inclHash: SecureHash
  574. while r.s[r.pos] != '\0':
  575. var section = rdWord(r)
  576. if r.reason != rrNone:
  577. break # no need to process this file further
  578. case section
  579. of "HASH":
  580. inc(r.pos) # skip ':'
  581. if hash != parseSecureHash(decodeStr(r.s, r.pos)):
  582. r.reason = rrHashChange
  583. of "ID":
  584. inc(r.pos) # skip ':'
  585. r.moduleID = decodeVInt(r.s, r.pos)
  586. setId(r.moduleID)
  587. of "ORIGFILE":
  588. inc(r.pos)
  589. r.origFile = decodeStr(r.s, r.pos)
  590. of "OPTIONS":
  591. inc(r.pos) # skip ':'
  592. r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
  593. if options.gOptions != r.options: r.reason = rrOptions
  594. of "GOPTIONS":
  595. inc(r.pos) # skip ':'
  596. var dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
  597. if gGlobalOptions-harmlessOptions != dep-harmlessOptions:
  598. r.reason = rrOptions
  599. of "CMD":
  600. inc(r.pos) # skip ':'
  601. var dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
  602. if cmdChangeTriggersRecompilation(dep, gCmd): r.reason = rrOptions
  603. of "DEFINES":
  604. inc(r.pos) # skip ':'
  605. d = 0
  606. while r.s[r.pos] > '\x0A':
  607. w = decodeStr(r.s, r.pos)
  608. inc(d)
  609. if not condsyms.isDefined(r.cache.getIdent(w)):
  610. r.reason = rrDefines #MessageOut('not defined, but should: ' + w);
  611. if r.s[r.pos] == ' ': inc(r.pos)
  612. if d != countDefinedSymbols(): r.reason = rrDefines
  613. of "FILES":
  614. inc(r.pos, 2) # skip "(\10"
  615. inc(r.line)
  616. while r.s[r.pos] != ')':
  617. let finalPath = decodeStr(r.s, r.pos)
  618. #let resolvedPath = relativePath.findModule(r.origFile)
  619. #let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
  620. r.files.add(finalPath.fileInfoIdx)
  621. inc(r.pos) # skip #10
  622. inc(r.line)
  623. if r.s[r.pos] == ')': inc(r.pos)
  624. of "INCLUDES":
  625. inc(r.pos, 2) # skip "(\10"
  626. inc(r.line)
  627. while r.s[r.pos] != ')':
  628. w = r.files[decodeVInt(r.s, r.pos)].toFullPath
  629. inc(r.pos) # skip ' '
  630. inclHash = parseSecureHash(decodeStr(r.s, r.pos))
  631. if r.reason == rrNone:
  632. if not existsFile(w) or (inclHash != secureHashFile(w)):
  633. r.reason = rrInclDeps
  634. if r.s[r.pos] == '\x0A':
  635. inc(r.pos)
  636. inc(r.line)
  637. if r.s[r.pos] == ')': inc(r.pos)
  638. of "DEPS":
  639. inc(r.pos) # skip ':'
  640. while r.s[r.pos] > '\x0A':
  641. r.modDeps.add(r.files[int32(decodeVInt(r.s, r.pos))])
  642. if r.s[r.pos] == ' ': inc(r.pos)
  643. of "INTERF":
  644. r.interfIdx = r.pos + 2
  645. skipSection(r)
  646. of "COMPILERPROCS":
  647. r.compilerProcsIdx = r.pos + 2
  648. skipSection(r)
  649. of "INDEX":
  650. processIndex(r, r.index)
  651. of "IMPORTS":
  652. processIndex(r, r.imports)
  653. of "CONVERTERS":
  654. r.convertersIdx = r.pos + 1
  655. skipSection(r)
  656. of "METHODS":
  657. r.methodsIdx = r.pos + 1
  658. skipSection(r)
  659. of "DATA":
  660. r.dataIdx = r.pos + 2 # "(\10"
  661. # We do not read the DATA section here! We read the needed objects on
  662. # demand. And the DATA section comes last in the file, so we stop here:
  663. break
  664. of "INIT":
  665. r.initIdx = r.pos + 2 # "(\10"
  666. skipSection(r)
  667. else:
  668. internalError("invalid section: '" & section &
  669. "' at " & $r.line & " in " & r.filename)
  670. #MsgWriteln("skipping section: " & section &
  671. # " at " & $r.line & " in " & r.filename)
  672. skipSection(r)
  673. if r.s[r.pos] == '\x0A':
  674. inc(r.pos)
  675. inc(r.line)
  676. proc startsWith(buf: cstring, token: string, pos = 0): bool =
  677. var s = 0
  678. while s < token.len and buf[pos+s] == token[s]: inc s
  679. result = s == token.len
  680. proc newRodReader(modfilename: string, hash: SecureHash,
  681. readerIndex: int; cache: IdentCache): PRodReader =
  682. new(result)
  683. result.cache = cache
  684. try:
  685. result.memfile = memfiles.open(modfilename)
  686. except OSError:
  687. return nil
  688. result.files = @[]
  689. result.modDeps = @[]
  690. result.methods = @[]
  691. var r = result
  692. r.reason = rrNone
  693. r.pos = 0
  694. r.line = 1
  695. r.readerIndex = readerIndex
  696. r.filename = modfilename
  697. r.syms = initTable[int, PSym]()
  698. # we terminate the file explicitly with ``\0``, so the cast to `cstring`
  699. # is safe:
  700. r.s = cast[cstring](r.memfile.mem)
  701. if startsWith(r.s, "NIM:"):
  702. initIiTable(r.index.tab)
  703. initIiTable(r.imports.tab) # looks like a ROD file
  704. inc(r.pos, 4)
  705. var version = ""
  706. while r.s[r.pos] notin {'\0', '\x0A'}:
  707. add(version, r.s[r.pos])
  708. inc(r.pos)
  709. if r.s[r.pos] == '\x0A': inc(r.pos)
  710. if version != RodFileVersion:
  711. # since ROD files are only for caching, no backwards compatibility is
  712. # needed
  713. #echo "expected version ", version, " ", RodFileVersion
  714. result.memfile.close
  715. result = nil
  716. else:
  717. result.memfile.close
  718. result = nil
  719. proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType =
  720. result = PType(idTableGet(gTypeTable, id))
  721. if result == nil:
  722. # load the type:
  723. var oldPos = r.pos
  724. var d = iiTableGet(r.index.tab, id)
  725. if d == InvalidKey: internalError(info, "rrGetType")
  726. r.pos = d + r.dataIdx
  727. result = decodeType(r, info)
  728. r.pos = oldPos
  729. type
  730. TFileModuleRec{.final.} = object
  731. filename*: string
  732. reason*: TReasonForRecompile
  733. rd*: PRodReader
  734. hash*: SecureHash
  735. hashDone*: bool
  736. TFileModuleMap = seq[TFileModuleRec]
  737. var gMods*: TFileModuleMap = @[]
  738. proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym =
  739. # all compiled modules
  740. if rd.dataIdx == 0: internalError(info, "dataIdx == 0")
  741. var oldPos = rd.pos
  742. rd.pos = offset + rd.dataIdx
  743. result = decodeSym(rd, info)
  744. rd.pos = oldPos
  745. proc findSomeWhere(id: int) =
  746. for i in countup(0, high(gMods)):
  747. var rd = gMods[i].rd
  748. if rd != nil:
  749. var d = iiTableGet(rd.index.tab, id)
  750. if d != InvalidKey:
  751. echo "found id ", id, " in ", gMods[i].filename
  752. proc getReader(moduleId: int): PRodReader =
  753. # we can't index 'gMods' here as it's indexed by a *file index* which is not
  754. # the module ID! We could introduce a mapping ID->PRodReader but I'll leave
  755. # this for later versions if benchmarking shows the linear search causes
  756. # problems:
  757. for i in 0 .. <gMods.len:
  758. result = gMods[i].rd
  759. if result != nil and result.moduleID == moduleId: return result
  760. return nil
  761. proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
  762. result = r.syms.getOrDefault(id)
  763. if result == nil:
  764. # load the symbol:
  765. var d = iiTableGet(r.index.tab, id)
  766. if d == InvalidKey:
  767. # import from other module:
  768. var moduleID = iiTableGet(r.imports.tab, id)
  769. if moduleID < 0:
  770. var x = ""
  771. encodeVInt(id, x)
  772. internalError(info, "missing from both indexes: +" & x)
  773. var rd = getReader(moduleID)
  774. d = iiTableGet(rd.index.tab, id)
  775. if d != InvalidKey:
  776. result = decodeSymSafePos(rd, d, info)
  777. else:
  778. var x = ""
  779. encodeVInt(id, x)
  780. when false: findSomeWhere(id)
  781. internalError(info, "rrGetSym: no reader found: +" & x)
  782. else:
  783. # own symbol:
  784. result = decodeSymSafePos(r, d, info)
  785. if result != nil and result.kind == skStub: rawLoadStub(result)
  786. proc loadInitSection*(r: PRodReader): PNode =
  787. if r.initIdx == 0 or r.dataIdx == 0: internalError("loadInitSection")
  788. var oldPos = r.pos
  789. r.pos = r.initIdx
  790. result = newNode(nkStmtList)
  791. while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
  792. var d = decodeVInt(r.s, r.pos)
  793. inc(r.pos) # #10
  794. var p = r.pos
  795. r.pos = d + r.dataIdx
  796. addSon(result, decodeNode(r, unknownLineInfo()))
  797. r.pos = p
  798. r.pos = oldPos
  799. proc loadConverters(r: PRodReader) =
  800. # We have to ensure that no exported converter is a stub anymore, and the
  801. # import mechanism takes care of the rest.
  802. if r.convertersIdx == 0 or r.dataIdx == 0:
  803. internalError("importConverters")
  804. r.pos = r.convertersIdx
  805. while r.s[r.pos] > '\x0A':
  806. var d = decodeVInt(r.s, r.pos)
  807. discard rrGetSym(r, d, unknownLineInfo())
  808. if r.s[r.pos] == ' ': inc(r.pos)
  809. proc loadMethods(r: PRodReader) =
  810. if r.methodsIdx == 0 or r.dataIdx == 0:
  811. internalError("loadMethods")
  812. r.pos = r.methodsIdx
  813. while r.s[r.pos] > '\x0A':
  814. var d = decodeVInt(r.s, r.pos)
  815. r.methods.add(rrGetSym(r, d, unknownLineInfo()))
  816. if r.s[r.pos] == ' ': inc(r.pos)
  817. proc getHash*(fileIdx: int32): SecureHash =
  818. internalAssert fileIdx >= 0 and fileIdx < gMods.len
  819. if gMods[fileIdx].hashDone:
  820. return gMods[fileIdx].hash
  821. result = secureHashFile(fileIdx.toFullPath)
  822. gMods[fileIdx].hash = result
  823. template growCache*(cache, pos) =
  824. if cache.len <= pos: cache.setLen(pos+1)
  825. proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile =
  826. assert fileIdx != InvalidFileIDX
  827. growCache gMods, fileIdx
  828. if gMods[fileIdx].reason != rrEmpty:
  829. # reason has already been computed for this module:
  830. return gMods[fileIdx].reason
  831. let filename = fileIdx.toFilename
  832. var hash = getHash(fileIdx)
  833. gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles
  834. result = rrNone
  835. var rodfile = toGeneratedFile(filename.withPackageName, RodExt)
  836. var r = newRodReader(rodfile, hash, fileIdx, cache)
  837. if r == nil:
  838. result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
  839. else:
  840. processRodFile(r, hash)
  841. result = r.reason
  842. if result == rrNone:
  843. # check modules it depends on
  844. # NOTE: we need to process the entire module graph so that no ID will
  845. # be used twice! However, compilation speed does not suffer much from
  846. # this, since results are cached.
  847. var res = checkDep(systemFileIdx, cache)
  848. if res != rrNone: result = rrModDeps
  849. for i in countup(0, high(r.modDeps)):
  850. res = checkDep(r.modDeps[i], cache)
  851. if res != rrNone:
  852. result = rrModDeps
  853. # we cannot break here, because of side-effects of `checkDep`
  854. if result != rrNone:
  855. rawMessage(hintProcessing, reasonToFrmt[result] % filename)
  856. if result != rrNone or optForceFullMake in gGlobalOptions:
  857. # recompilation is necessary:
  858. if r != nil: memfiles.close(r.memfile)
  859. r = nil
  860. gMods[fileIdx].rd = r
  861. gMods[fileIdx].reason = result # now we know better
  862. proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader =
  863. let fileIdx = module.fileIdx
  864. if optSymbolFiles notin gGlobalOptions:
  865. module.id = getID()
  866. return nil
  867. idgen.loadMaxIds(options.gProjectPath / options.gProjectName)
  868. discard checkDep(fileIdx, cache)
  869. if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile")
  870. result = gMods[fileIdx].rd
  871. if result != nil:
  872. module.id = result.moduleID
  873. result.syms[module.id] = module
  874. processInterf(result, module)
  875. processCompilerProcs(result, module)
  876. loadConverters(result)
  877. loadMethods(result)
  878. else:
  879. module.id = getID()
  880. proc rawLoadStub(s: PSym) =
  881. if s.kind != skStub: internalError("loadStub")
  882. var rd = gMods[s.position].rd
  883. var theId = s.id # used for later check
  884. var d = iiTableGet(rd.index.tab, s.id)
  885. if d == InvalidKey: internalError("loadStub: invalid key")
  886. var rs = decodeSymSafePos(rd, d, unknownLineInfo())
  887. if rs != s:
  888. #echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2),
  889. # "\ns: ", toHex(cast[int](s.position), int.sizeof * 2)
  890. internalError(rs.info, "loadStub: wrong symbol")
  891. elif rs.id != theId:
  892. internalError(rs.info, "loadStub: wrong ID")
  893. #MessageOut('loaded stub: ' + s.name.s);
  894. proc loadStub*(s: PSym) =
  895. ## loads the stub symbol `s`.
  896. # deactivate the GC here because we do a deep recursion and generate no
  897. # garbage when restoring parts of the object graph anyway.
  898. # Since we die with internal errors if this fails, no try-finally is
  899. # necessary.
  900. GC_disable()
  901. rawLoadStub(s)
  902. GC_enable()
  903. proc getBody*(s: PSym): PNode =
  904. ## retrieves the AST's body of `s`. If `s` has been loaded from a rod-file
  905. ## it may perform an expensive reload operation. Otherwise it's a simple
  906. ## accessor.
  907. assert s.kind in routineKinds
  908. # prevent crashes due to incorrect macro transformations (bug #2377)
  909. if s.ast.isNil or bodyPos >= s.ast.len: return ast.emptyNode
  910. result = s.ast.sons[bodyPos]
  911. if result == nil:
  912. assert s.offset != 0
  913. var r = gMods[s.position].rd
  914. var oldPos = r.pos
  915. r.pos = s.offset
  916. result = decodeNode(r, s.info)
  917. r.pos = oldPos
  918. s.ast.sons[bodyPos] = result
  919. s.offset = 0
  920. initIdTable(gTypeTable)
  921. initStrTable(rodCompilerprocs)
  922. # viewer:
  923. proc writeNode(f: File; n: PNode) =
  924. f.write("(")
  925. if n != nil:
  926. f.write($n.kind)
  927. if n.typ != nil:
  928. f.write('^')
  929. f.write(n.typ.id)
  930. case n.kind
  931. of nkCharLit..nkUInt64Lit:
  932. if n.intVal != 0:
  933. f.write('!')
  934. f.write(n.intVal)
  935. of nkFloatLit..nkFloat64Lit:
  936. if n.floatVal != 0.0:
  937. f.write('!')
  938. f.write($n.floatVal)
  939. of nkStrLit..nkTripleStrLit:
  940. if n.strVal != "":
  941. f.write('!')
  942. f.write(n.strVal.escape)
  943. of nkIdent:
  944. f.write('!')
  945. f.write(n.ident.s)
  946. of nkSym:
  947. f.write('!')
  948. f.write(n.sym.id)
  949. else:
  950. for i in countup(0, sonsLen(n) - 1):
  951. writeNode(f, n.sons[i])
  952. f.write(")")
  953. proc writeSym(f: File; s: PSym) =
  954. if s == nil:
  955. f.write("{}\n")
  956. return
  957. f.write("{")
  958. f.write($s.kind)
  959. f.write('+')
  960. f.write(s.id)
  961. f.write('&')
  962. f.write(s.name.s)
  963. if s.typ != nil:
  964. f.write('^')
  965. f.write(s.typ.id)
  966. if s.owner != nil:
  967. f.write('*')
  968. f.write(s.owner.id)
  969. if s.flags != {}:
  970. f.write('$')
  971. f.write($s.flags)
  972. if s.magic != mNone:
  973. f.write('@')
  974. f.write($s.magic)
  975. if s.options != gOptions:
  976. f.write('!')
  977. f.write($s.options)
  978. if s.position != 0:
  979. f.write('%')
  980. f.write($s.position)
  981. if s.offset != -1:
  982. f.write('`')
  983. f.write($s.offset)
  984. if s.constraint != nil:
  985. f.write('#')
  986. f.writeNode(s.constraint)
  987. if s.ast != nil:
  988. f.writeNode(s.ast)
  989. f.write("}\n")
  990. proc writeType(f: File; t: PType) =
  991. if t == nil:
  992. f.write("[]\n")
  993. return
  994. f.write('[')
  995. f.write($t.kind)
  996. f.write('+')
  997. f.write($t.id)
  998. if t.n != nil:
  999. f.writeNode(t.n)
  1000. if t.flags != {}:
  1001. f.write('$')
  1002. f.write($t.flags)
  1003. if t.callConv != low(t.callConv):
  1004. f.write('?')
  1005. f.write($t.callConv)
  1006. if t.owner != nil:
  1007. f.write('*')
  1008. f.write($t.owner.id)
  1009. if t.sym != nil:
  1010. f.write('&')
  1011. f.write(t.sym.id)
  1012. if t.size != -1:
  1013. f.write('/')
  1014. f.write($t.size)
  1015. if t.align != 2:
  1016. f.write('=')
  1017. f.write($t.align)
  1018. for i in countup(0, sonsLen(t) - 1):
  1019. if t.sons[i] == nil:
  1020. f.write("^()")
  1021. else:
  1022. f.write('^')
  1023. f.write($t.sons[i].id)
  1024. f.write("]\n")
  1025. proc viewFile(rodfile: string) =
  1026. var r = newRodReader(rodfile, secureHash(""), 0, newIdentCache())
  1027. if r == nil:
  1028. rawMessage(errGenerated, "cannot open file (or maybe wrong version):" &
  1029. rodfile)
  1030. return
  1031. r.inViewMode = true
  1032. var outf = system.open(rodfile.changeFileExt(".rod.txt"), fmWrite)
  1033. while r.s[r.pos] != '\0':
  1034. let section = rdWord(r)
  1035. case section
  1036. of "HASH":
  1037. inc(r.pos) # skip ':'
  1038. outf.writeLine("HASH:", $decodeVInt(r.s, r.pos))
  1039. of "ID":
  1040. inc(r.pos) # skip ':'
  1041. r.moduleID = decodeVInt(r.s, r.pos)
  1042. setId(r.moduleID)
  1043. outf.writeLine("ID:", $r.moduleID)
  1044. of "ORIGFILE":
  1045. inc(r.pos)
  1046. r.origFile = decodeStr(r.s, r.pos)
  1047. outf.writeLine("ORIGFILE:", r.origFile)
  1048. of "OPTIONS":
  1049. inc(r.pos) # skip ':'
  1050. r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
  1051. outf.writeLine("OPTIONS:", $r.options)
  1052. of "GOPTIONS":
  1053. inc(r.pos) # skip ':'
  1054. let dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
  1055. outf.writeLine("GOPTIONS:", $dep)
  1056. of "CMD":
  1057. inc(r.pos) # skip ':'
  1058. let dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
  1059. outf.writeLine("CMD:", $dep)
  1060. of "DEFINES":
  1061. inc(r.pos) # skip ':'
  1062. var d = 0
  1063. outf.write("DEFINES:")
  1064. while r.s[r.pos] > '\x0A':
  1065. let w = decodeStr(r.s, r.pos)
  1066. inc(d)
  1067. outf.write(" ", w)
  1068. if r.s[r.pos] == ' ': inc(r.pos)
  1069. outf.write("\n")
  1070. of "FILES":
  1071. inc(r.pos, 2) # skip "(\10"
  1072. inc(r.line)
  1073. outf.write("FILES(\n")
  1074. while r.s[r.pos] != ')':
  1075. let relativePath = decodeStr(r.s, r.pos)
  1076. let resolvedPath = relativePath.findModule(r.origFile)
  1077. let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
  1078. r.files.add(finalPath.fileInfoIdx)
  1079. inc(r.pos) # skip #10
  1080. inc(r.line)
  1081. outf.writeLine finalPath
  1082. if r.s[r.pos] == ')': inc(r.pos)
  1083. outf.write(")\n")
  1084. of "INCLUDES":
  1085. inc(r.pos, 2) # skip "(\10"
  1086. inc(r.line)
  1087. outf.write("INCLUDES(\n")
  1088. while r.s[r.pos] != ')':
  1089. let w = r.files[decodeVInt(r.s, r.pos)]
  1090. inc(r.pos) # skip ' '
  1091. let inclHash = decodeVInt(r.s, r.pos)
  1092. if r.s[r.pos] == '\x0A':
  1093. inc(r.pos)
  1094. inc(r.line)
  1095. outf.write(w, " ", inclHash, "\n")
  1096. if r.s[r.pos] == ')': inc(r.pos)
  1097. outf.write(")\n")
  1098. of "DEPS":
  1099. inc(r.pos) # skip ':'
  1100. outf.write("DEPS:")
  1101. while r.s[r.pos] > '\x0A':
  1102. let v = int32(decodeVInt(r.s, r.pos))
  1103. r.modDeps.add(r.files[v])
  1104. if r.s[r.pos] == ' ': inc(r.pos)
  1105. outf.write(" ", r.files[v])
  1106. outf.write("\n")
  1107. of "INTERF", "COMPILERPROCS":
  1108. inc r.pos, 2
  1109. if section == "INTERF": r.interfIdx = r.pos
  1110. else: r.compilerProcsIdx = r.pos
  1111. outf.write(section, "(\n")
  1112. while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
  1113. let w = decodeStr(r.s, r.pos)
  1114. inc(r.pos)
  1115. let key = decodeVInt(r.s, r.pos)
  1116. inc(r.pos) # #10
  1117. outf.write(w, " ", key, "\n")
  1118. if r.s[r.pos] == ')': inc r.pos
  1119. outf.write(")\n")
  1120. of "INDEX":
  1121. outf.write(section, "(\n")
  1122. processIndex(r, r.index, outf)
  1123. outf.write(")\n")
  1124. of "IMPORTS":
  1125. outf.write(section, "(\n")
  1126. processIndex(r, r.imports, outf)
  1127. outf.write(")\n")
  1128. of "CONVERTERS", "METHODS":
  1129. inc r.pos
  1130. if section == "METHODS": r.methodsIdx = r.pos
  1131. else: r.convertersIdx = r.pos
  1132. outf.write(section, ":")
  1133. while r.s[r.pos] > '\x0A':
  1134. let d = decodeVInt(r.s, r.pos)
  1135. outf.write(" ", $d)
  1136. if r.s[r.pos] == ' ': inc(r.pos)
  1137. outf.write("\n")
  1138. of "DATA":
  1139. inc(r.pos, 2)
  1140. r.dataIdx = r.pos
  1141. outf.write("DATA(\n")
  1142. while r.s[r.pos] != ')':
  1143. if r.s[r.pos] == '(':
  1144. outf.writeNode decodeNode(r, unknownLineInfo())
  1145. outf.write("\n")
  1146. elif r.s[r.pos] == '[':
  1147. outf.writeType decodeType(r, unknownLineInfo())
  1148. else:
  1149. outf.writeSym decodeSym(r, unknownLineInfo())
  1150. if r.s[r.pos] == '\x0A':
  1151. inc(r.pos)
  1152. inc(r.line)
  1153. if r.s[r.pos] == ')': inc r.pos
  1154. outf.write(")\n")
  1155. of "INIT":
  1156. outf.write("INIT(\n")
  1157. inc r.pos, 2
  1158. r.initIdx = r.pos
  1159. while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
  1160. let d = decodeVInt(r.s, r.pos)
  1161. inc(r.pos) # #10
  1162. #let p = r.pos
  1163. #r.pos = d + r.dataIdx
  1164. #outf.writeNode decodeNode(r, UnknownLineInfo())
  1165. #outf.write("\n")
  1166. #r.pos = p
  1167. if r.s[r.pos] == ')': inc r.pos
  1168. outf.write("<not supported by viewer>)\n")
  1169. else:
  1170. internalError("invalid section: '" & section &
  1171. "' at " & $r.line & " in " & r.filename)
  1172. skipSection(r)
  1173. if r.s[r.pos] == '\x0A':
  1174. inc(r.pos)
  1175. inc(r.line)
  1176. outf.close
  1177. when isMainModule:
  1178. viewFile(paramStr(1).addFileExt(rodExt))