126126 moduleToNifSuffix: Table [FileIndex , string ]
127127 locals: HashSet [ItemId ] # track proc-local symbols
128128 inProc: int
129+ writtenTypes: seq [PType ] # types written in this module, to be unloaded later
130+ writtenSyms: seq [PSym ] # symbols written in this module, to be unloaded later
129131
130132proc toNifSymName (w: var Writer ; sym: PSym ): string =
131133 # # Generate NIF name for a symbol: local names are `ident.disamb`,
@@ -238,6 +240,8 @@ proc writeType(w: var Writer; dest: var TokenBuf; typ: PType) =
238240 elif typ.itemId.module == w.currentModule and typ.state == Complete :
239241 typ.state = Sealed
240242 writeTypeDef (w, dest, typ)
243+ # Collect for later unloading after entire module is written
244+ w.writtenTypes.add typ
241245 else :
242246 dest.addSymUse pool.syms.getOrIncl (w.typeToNifSym (typ)), NoLineInfo
243247
@@ -291,6 +295,11 @@ proc writeSymDef(w: var Writer; dest: var TokenBuf; sym: PSym) =
291295 writeSym (w, dest, sym.instantiatedFromImpl)
292296 dest.addParRi
293297
298+ # Collect for later unloading after entire module is written
299+ if sym.kindImpl notin {skModule, skPackage}:
300+ # do not unload modules
301+ w.writtenSyms.add sym
302+
294303proc writeSym (w: var Writer ; dest: var TokenBuf ; sym: PSym ) =
295304 if sym == nil :
296305 dest.addDotToken ()
@@ -453,14 +462,19 @@ proc writeToplevelNode(w: var Writer; outer, inner: var TokenBuf; n: PNode) =
453462 else :
454463 writeNode w, outer, n
455464
465+ proc createStmtList (buf: var TokenBuf ; info: PackedLineInfo ) {.inline .} =
466+ buf.addParLe pool.tags.getOrIncl (toNifTag (nkStmtList)), info
467+ buf.addDotToken # flags
468+ buf.addDotToken # type
469+
456470proc writeNifModule * (config: ConfigRef ; thisModule: int32 ; n: PNode ) =
457471 var w = Writer (infos: LineInfoWriter (config: config), currentModule: thisModule)
458472 var outer = createTokenBuf (300 )
459473 var inner = createTokenBuf (300 )
460474
461475 let rootInfo = trLineInfo (w, n.info)
462- outer. addParLe pool.tags. getOrIncl ( toNifTag (nkStmtList)) , rootInfo
463- inner. addParLe pool.tags. getOrIncl ( toNifTag (nkStmtList)) , rootInfo
476+ createStmtList (outer , rootInfo)
477+ createStmtList (inner , rootInfo)
464478
465479 w.writeToplevelNode outer, inner, n
466480
@@ -472,7 +486,7 @@ proc writeNifModule*(config: ConfigRef; thisModule: int32; n: PNode) =
472486 let d = completeGeneratedFilePath (config, nifFilename).string
473487
474488 var dest = createTokenBuf (600 )
475- dest. addParLe pool.tags. getOrIncl ( toNifTag (nkStmtList)) , rootInfo
489+ createStmtList (dest , rootInfo)
476490 dest.add w.deps
477491 dest.add outer
478492 dest.add inner
@@ -481,6 +495,13 @@ proc writeNifModule*(config: ConfigRef; thisModule: int32; n: PNode) =
481495 writeFile (dest, d)
482496 createIndex (d, false , dest[0 ].info)
483497
498+ # Unload all written types and symbols from memory after the entire module is written
499+ # This handles cyclic references correctly since everything is written before unloading
500+ for typ in w.writtenTypes:
501+ forcePartial (typ)
502+ for sym in w.writtenSyms:
503+ forcePartial (sym)
504+
484505
485506# --------------------------- Loader (lazy!) -----------------------------------------------
486507
548569 syms: Table [ItemId , (PSym , NifIndexEntry )]
549570 mods: seq [NifModule ]
550571 cache: IdentCache
551- # moduleToNifSuffix: Table[FileIndex, string]
572+ moduleToNifSuffix: Table [FileIndex , string ]
552573
553574proc createDecodeContext * (config: ConfigRef ; cache: IdentCache ): DecodeContext =
554575 # # Supposed to be a global variable
@@ -567,7 +588,7 @@ proc moduleId(c: var DecodeContext; suffix: string): FileIndex =
567588 result = c.infos.config.registerNifSuffix (suffix, isKnownFile)
568589 if not isKnownFile:
569590 let modFile = (getNimcacheDir (c.infos.config) / RelativeFile (suffix & " .nif" )).string
570- let idxFile = (getNimcacheDir (c.infos.config) / RelativeFile (suffix & " .idx.nif" )).string
591+ let idxFile = (getNimcacheDir (c.infos.config) / RelativeFile (suffix & " .s. idx.nif" )).string
571592 if result .int >= c.mods.len:
572593 c.mods.setLen (result .int + 1 )
573594 c.mods[result .int ] = NifModule (stream: nifstreams.open (modFile), index: readIndex (idxFile))
@@ -580,7 +601,7 @@ proc getOffset(c: var DecodeContext; module: FileIndex; nifName: string): NifInd
580601 if result .offset == 0 :
581602 raiseAssert " symbol has no offset: " & nifName
582603
583- proc loadNode (c: var DecodeContext ; n: var Cursor ): PNode
604+ proc loadNode (c: var DecodeContext ; n: var Cursor ; thisModule: string ): PNode
584605
585606proc loadTypeStub (c: var DecodeContext ; t: SymId ): PType =
586607 let name = pool.syms[t]
@@ -619,10 +640,10 @@ proc loadTypeStub(c: var DecodeContext; n: var Cursor): PType =
619640 else :
620641 raiseAssert " type expected but got " & $ n.kind
621642
622- proc loadSymStub (c: var DecodeContext ; t: SymId ): PSym =
643+ proc loadSymStub (c: var DecodeContext ; t: SymId ; thisModule: string ): PSym =
623644 let symAsStr = pool.syms[t]
624645 let sn = parseSymName (symAsStr)
625- let module = moduleId (c, sn.module)
646+ let module = moduleId (c, if sn.module.len > 0 : sn.module else : thisModule )
626647 let val = addr c.mods[module.int32 ].symCounter
627648 inc val[]
628649
@@ -632,19 +653,20 @@ proc loadSymStub(c: var DecodeContext; t: SymId): PSym =
632653 let offs = c.getOffset (module, symAsStr)
633654 result = PSym (itemId: id, kindImpl: skStub, name: c.cache.getIdent (sn.name), disamb: sn.count.int32 , state: Partial )
634655 c.syms[id] = (result , offs)
656+ c.moduleToNifSuffix[module] = (if sn.module.len > 0 : sn.module else : thisModule)
635657
636- proc loadSymStub (c: var DecodeContext ; n: var Cursor ): PSym =
658+ proc loadSymStub (c: var DecodeContext ; n: var Cursor ; thisModule: string ): PSym =
637659 if n.kind == DotToken :
638660 result = nil
639661 inc n
640662 elif n.kind == Symbol :
641663 let s = n.symId
642- result = loadSymStub (c, s)
664+ result = loadSymStub (c, s, thisModule )
643665 inc n
644666 elif n.kind == ParLe and n.tagId == sdefTag:
645667 let s = n.firstSon.symId
646668 skip n
647- result = loadSymStub (c, s)
669+ result = loadSymStub (c, s, thisModule )
648670 else :
649671 raiseAssert " sym expected but got " & $ n.kind
650672
@@ -700,6 +722,7 @@ proc loadType*(c: var DecodeContext; t: PType) =
700722 inc n
701723 expect n, SymbolDef
702724 # ignore the type's name, we have already used it to create this PType's itemId!
725+ let typesModule = parseSymName (pool.syms[n.symId]).module
703726 inc n
704727 # loadField t.kind
705728 loadField t.flagsImpl
@@ -710,17 +733,17 @@ proc loadType*(c: var DecodeContext; t: PType) =
710733 loadField t.itemId.item # nonUniqueId
711734
712735 t.typeInstImpl = loadTypeStub (c, n)
713- t.nImpl = loadNode (c, n)
714- t.ownerFieldImpl = loadSymStub (c, n)
715- t.symImpl = loadSymStub (c, n)
736+ t.nImpl = loadNode (c, n, typesModule )
737+ t.ownerFieldImpl = loadSymStub (c, n, typesModule )
738+ t.symImpl = loadSymStub (c, n, typesModule )
716739 loadLoc c, n, t.locImpl
717740
718741 while n.kind != ParRi :
719742 t.sonsImpl.add loadTypeStub (c, n)
720743
721744 skipParRi n
722745
723- proc loadAnnex (c: var DecodeContext ; n: var Cursor ): PLib =
746+ proc loadAnnex (c: var DecodeContext ; n: var Cursor ; thisModule: string ): PLib =
724747 if n.kind == DotToken :
725748 result = nil
726749 inc n
@@ -732,7 +755,7 @@ proc loadAnnex(c: var DecodeContext; n: var Cursor): PLib =
732755 expect n, StringLit
733756 result .name = pool.strings[n.litId]
734757 inc n
735- result .path = loadNode (c, n)
758+ result .path = loadNode (c, n, thisModule )
736759 skipParRi n
737760 else :
738761 raiseAssert " `lib/annex` information expected"
@@ -741,7 +764,8 @@ proc loadSym*(c: var DecodeContext; s: PSym) =
741764 if s.state != Partial : return
742765 s.state = Sealed
743766 var buf = createTokenBuf (30 )
744- var n = cursorFromIndexEntry (c, s.itemId.module.FileIndex , c.syms[s.itemId][1 ], buf)
767+ let symsModule = s.itemId.module.FileIndex
768+ var n = cursorFromIndexEntry (c, symsModule, c.syms[s.itemId][1 ], buf)
745769
746770 expect n, ParLe
747771 if n.tagId != sdefTag:
@@ -772,7 +796,7 @@ proc loadSym*(c: var DecodeContext; s: PSym) =
772796
773797 case s.kindImpl
774798 of skLet, skVar, skField, skForVar:
775- s.guardImpl = loadSymStub (c, n)
799+ s.guardImpl = loadSymStub (c, n, c.moduleToNifSuffix[symsModule] )
776800 loadField s.bitsizeImpl
777801 loadField s.alignmentImpl
778802 else :
@@ -785,62 +809,68 @@ proc loadSym*(c: var DecodeContext; s: PSym) =
785809 else :
786810 loadField s.positionImpl
787811 s.typImpl = loadTypeStub (c, n)
788- s.ownerFieldImpl = loadSymStub (c, n)
812+ s.ownerFieldImpl = loadSymStub (c, n, c.moduleToNifSuffix[symsModule] )
789813 # We do not store `sym.ast` here but instead set it in the deserializer
790814 # writeNode(w, sym.ast)
791815 loadLoc c, n, s.locImpl
792- s.constraintImpl = loadNode (c, n)
793- s.instantiatedFromImpl = loadSymStub (c, n)
816+ s.constraintImpl = loadNode (c, n, c.moduleToNifSuffix[symsModule] )
817+ s.instantiatedFromImpl = loadSymStub (c, n, c.moduleToNifSuffix[symsModule] )
794818 skipParRi n
795819
796820
797821template withNode (c: var DecodeContext ; n: var Cursor ; result: PNode ; kind: TNodeKind ; body: untyped ) =
798822 let info = c.infos.oldLineInfo (n.info)
823+ inc n
799824 let flags = loadAtom (TNodeFlags , n)
800825 result = newNodeI (kind, info)
801826 result .flags = flags
802827 result .typField = c.loadTypeStub n
803828 body
804829 skipParRi n
805830
806- proc loadNode (c: var DecodeContext ; n: var Cursor ): PNode =
831+ proc loadNode (c: var DecodeContext ; n: var Cursor ; thisModule: string ): PNode =
807832 result = nil
808- case n.kind:
833+ case n.kind
834+ of Symbol :
835+ let info = c.infos.oldLineInfo (n.info)
836+ result = newSymNode (c.loadSymStub (n, thisModule), info)
809837 of DotToken :
810838 result = nil
811839 inc n
812840 of ParLe :
813841 let kind = n.nodeKind
814- case kind:
842+ case kind
815843 of nkNone:
816844 # special NIF introduced tag?
817845 case pool.tags[n.tagId]
818846 of hiddenTypeTagName:
819847 inc n
820848 let typ = c.loadTypeStub n
821849 let info = c.infos.oldLineInfo (n.info)
822- result = newSymNode (c.loadSymStub n , info)
850+ result = newSymNode (c.loadSymStub (n, thisModule) , info)
823851 result .typField = typ
824852 skipParRi n
825853 of symDefTagName:
826854 let name = n.firstSon
827855 assert name.kind == SymbolDef
828- result = newSymNode (c.loadSymStub name.symId, c.infos.oldLineInfo (n.info))
856+ result = newSymNode (c.loadSymStub ( name.symId, thisModule) , c.infos.oldLineInfo (n.info))
829857 skip n
830858 of typeDefTagName:
831859 raiseAssert " `td` tag in invalid context"
832860 of " none" :
833861 result = newNodeI (nkNone, c.infos.oldLineInfo (n.info))
862+ inc n
834863 result .flags = loadAtom (TNodeFlags , n)
835864 skipParRi n
836865 else :
837866 raiseAssert " Unknown NIF tag " & pool.tags[n.tagId]
838867 of nkEmpty:
839868 result = newNodeI (nkEmpty, c.infos.oldLineInfo (n.info))
840- result .flags = loadAtom ( TNodeFlags , n)
869+ inc n
841870 skipParRi n
842871 of nkIdent:
843872 let info = c.infos.oldLineInfo (n.info)
873+ inc n
844874 let flags = loadAtom (TNodeFlags , n)
845875 let typ = c.loadTypeStub n
846876 expect n, Ident
@@ -850,8 +880,9 @@ proc loadNode(c: var DecodeContext; n: var Cursor): PNode =
850880 result .typField = typ
851881 skipParRi n
852882 of nkSym:
853- let info = c.infos.oldLineInfo (n.info)
854- result = newSymNode (c.loadSymStub n, info)
883+ # let info = c.infos.oldLineInfo(n.info)
884+ # result = newSymNode(c.loadSymStub n, info)
885+ raiseAssert " nkSym should be mapped to a NIF symbol, not a tag"
855886 of nkCharLit:
856887 c.withNode n, result , kind:
857888 expect n, CharLit
@@ -897,24 +928,71 @@ proc loadNode(c: var DecodeContext; n: var Cursor): PNode =
897928 else :
898929 c.withNode n, result , kind:
899930 while n.kind != ParRi :
900- result .sons.add c.loadNode (n)
931+ result .sons.add c.loadNode (n, thisModule )
901932 else :
902933 raiseAssert " Not yet implemented " & $ n.kind
903934
904- when false :
905- proc loadNifModule * (c: var DecodeContext ; f: FileIndex ): PNode =
906- let moduleSuffix = moduleSuffix (c.infos.config, f)
907- let modFile = toGeneratedFile (c.infos.config, AbsoluteFile (moduleSuffix), " .nif" ).string
908-
909- var buf = createTokenBuf (300 )
910- var s = nifstreams.open (modFile)
911- # XXX We can optimize this here and only load the top level entries!
912- try :
913- nifcursors.parse (s, buf, NoLineInfo )
914- finally :
915- nifstreams.close (s)
916- var n = cursorAt (buf, 0 )
917- result = loadNode (c, n)
935+ proc moduleSuffix (conf: ConfigRef ; f: FileIndex ): string =
936+ moduleSuffix (toFullPath (conf, f), cast [seq [string ]](conf.searchPaths))
937+
938+ proc loadSymFromIndexEntry (c: var DecodeContext ; module: FileIndex ;
939+ nifName: string ; entry: NifIndexEntry ; thisModule: string ): PSym =
940+ # # Loads a symbol from the NIF index entry.
941+ # # Creates a symbol stub and loads its full definition.
942+ result = loadSymStub (c, pool.syms.getOrIncl nifName, thisModule)
943+
944+ proc populateInterfaceTablesFromIndex (c: var DecodeContext ; module: FileIndex ;
945+ interf, interfHidden: var TStrTable ; thisModule: string ) =
946+ # # Populates interface tables from the NIF index structure.
947+ # # Uses the index's public/private tables instead of traversing AST.
948+ let idx = addr c.mods[module.int32 ].index
949+
950+ # Add all public symbols to interf (exported interface) and interfHidden
951+ for nifName, entry in idx.public:
952+ if not nifName.startsWith (" `t" ):
953+ # do not load types, they are not part of an interface but an implementation detail!
954+ # echo "LOADING SYM ", nifName, " ", entry.offset
955+ let sym = loadSymFromIndexEntry (c, module, nifName, entry, thisModule)
956+ if sym != nil :
957+ strTableAdd (interf, sym)
958+ strTableAdd (interfHidden, sym)
959+
960+ when false :
961+ # Add private symbols to interfHidden only
962+ for nifName, entry in idx.private:
963+ let sym = loadSymFromIndexEntry (c, module, nifName, entry, thisModule)
964+ if sym != nil :
965+ strTableAdd (interfHidden, sym)
966+
967+ proc toNifFilename * (conf: ConfigRef ; f: FileIndex ): string =
968+ let suffix = moduleSuffix (conf, f)
969+ result = toGeneratedFile (conf, AbsoluteFile (suffix), " .nif" ).string
970+
971+ proc toNifIndexFilename * (conf: ConfigRef ; f: FileIndex ): string =
972+ let suffix = moduleSuffix (conf, f)
973+ result = toGeneratedFile (conf, AbsoluteFile (suffix), " .s.idx.nif" ).string
974+
975+ proc loadNifModule * (c: var DecodeContext ; f: FileIndex ; interf, interfHidden: var TStrTable ): PNode =
976+ let suffix = moduleSuffix (c.infos.config, f)
977+ let modFile = toGeneratedFile (c.infos.config, AbsoluteFile (suffix), " .nif" ).string
978+
979+ # Ensure module index is loaded - moduleId returns the FileIndex for this suffix
980+ let module = moduleId (c, suffix)
981+
982+ # Populate interface tables from the NIF index structure
983+ # Use the FileIndex returned by moduleId to ensure we access the correct index
984+ populateInterfaceTablesFromIndex (c, module, interf, interfHidden, suffix)
985+
986+ var buf = createTokenBuf (300 )
987+ var s = nifstreams.open (modFile)
988+ discard processDirectives (s.r)
989+ # XXX We can optimize this here and only load the top level entries!
990+ try :
991+ nifcursors.parse (s, buf, NoLineInfo )
992+ finally :
993+ nifstreams.close (s)
994+ var n = cursorAt (buf, 0 )
995+ result = loadNode (c, n, suffix)
918996
919997when isMainModule :
920998 import std / syncio
0 commit comments