Fix global destructor injection for JS backend (#19797)
* Fix global destructor injection for JS backend * Moved global destructors injection before the final call to transform and generate JS code. It had previously been after and thus not no JS was generated for them. * Added some internal documentation of `jsgen`. * Enable a current destructor test to cover the JS backend as well. * Fixes the JS aspect of #17237. * Fixed global destructor injection order for JS backend Co-authored-by: quantimnot <quantimnot@users.noreply.github.com>
This commit is contained in:
parent
a8426fc789
commit
05c0419658
2 changed files with 27 additions and 6 deletions
|
|
@ -2392,6 +2392,7 @@ proc optionalLine(p: Rope): Rope =
|
||||||
return p & "\L"
|
return p & "\L"
|
||||||
|
|
||||||
proc genProc(oldProc: PProc, prc: PSym): Rope =
|
proc genProc(oldProc: PProc, prc: PSym): Rope =
|
||||||
|
## Generate a JS procedure ('function').
|
||||||
var
|
var
|
||||||
resultSym: PSym
|
resultSym: PSym
|
||||||
a: TCompRes
|
a: TCompRes
|
||||||
|
|
@ -2673,6 +2674,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind)
|
else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind)
|
||||||
|
|
||||||
proc newModule(g: ModuleGraph; module: PSym): BModule =
|
proc newModule(g: ModuleGraph; module: PSym): BModule =
|
||||||
|
## Create a new JS backend module node.
|
||||||
new(result)
|
new(result)
|
||||||
result.module = module
|
result.module = module
|
||||||
result.sigConflicts = initCountTable[SigHash]()
|
result.sigConflicts = initCountTable[SigHash]()
|
||||||
|
|
@ -2684,6 +2686,7 @@ proc newModule(g: ModuleGraph; module: PSym): BModule =
|
||||||
PGlobals(g.backend).inSystem = true
|
PGlobals(g.backend).inSystem = true
|
||||||
|
|
||||||
proc genHeader(): Rope =
|
proc genHeader(): Rope =
|
||||||
|
## Generate the JS header.
|
||||||
result = rope("""/* Generated by the Nim Compiler v$1 */
|
result = rope("""/* Generated by the Nim Compiler v$1 */
|
||||||
var framePtr = null;
|
var framePtr = null;
|
||||||
var excHandler = 0;
|
var excHandler = 0;
|
||||||
|
|
@ -2714,6 +2717,8 @@ proc addHcrInitGuards(p: PProc, n: PNode,
|
||||||
genStmt(p, n)
|
genStmt(p, n)
|
||||||
|
|
||||||
proc genModule(p: PProc, n: PNode) =
|
proc genModule(p: PProc, n: PNode) =
|
||||||
|
## Generate the JS module code.
|
||||||
|
## Called for each top level node in a Nim module.
|
||||||
if optStackTrace in p.options:
|
if optStackTrace in p.options:
|
||||||
p.body.add(frameCreate(p,
|
p.body.add(frameCreate(p,
|
||||||
makeJSString("module " & p.module.module.name.s),
|
makeJSString("module " & p.module.module.name.s),
|
||||||
|
|
@ -2742,6 +2747,7 @@ proc genModule(p: PProc, n: PNode) =
|
||||||
p.body.add(frameDestroy(p))
|
p.body.add(frameDestroy(p))
|
||||||
|
|
||||||
proc myProcess(b: PPassContext, n: PNode): PNode =
|
proc myProcess(b: PPassContext, n: PNode): PNode =
|
||||||
|
## Generate JS code for a node.
|
||||||
result = n
|
result = n
|
||||||
let m = BModule(b)
|
let m = BModule(b)
|
||||||
if passes.skipCodegen(m.config, n): return n
|
if passes.skipCodegen(m.config, n): return n
|
||||||
|
|
@ -2754,6 +2760,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
|
||||||
p.g.code.add(p.body)
|
p.g.code.add(p.body)
|
||||||
|
|
||||||
proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
|
proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
|
||||||
|
## Combine source code from all nodes.
|
||||||
let globals = PGlobals(graph.backend)
|
let globals = PGlobals(graph.backend)
|
||||||
for prc in globals.forwarded:
|
for prc in globals.forwarded:
|
||||||
if not globals.generatedSyms.containsOrIncl(prc.id):
|
if not globals.generatedSyms.containsOrIncl(prc.id):
|
||||||
|
|
@ -2779,28 +2786,41 @@ proc getClassName(t: PType): Rope =
|
||||||
else: result = rope(s.name.s)
|
else: result = rope(s.name.s)
|
||||||
|
|
||||||
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||||
result = myProcess(b, n)
|
## Finalize JS code generation of a Nim module.
|
||||||
|
## Param `n` may contain nodes returned from the last module close call.
|
||||||
var m = BModule(b)
|
var m = BModule(b)
|
||||||
if sfMainModule in m.module.flags:
|
if sfMainModule in m.module.flags:
|
||||||
for destructorCall in graph.globalDestructors:
|
# Add global destructors to the module.
|
||||||
n.add destructorCall
|
# This must come before the last call to `myProcess`.
|
||||||
|
for i in countdown(high(graph.globalDestructors), 0):
|
||||||
|
n.add graph.globalDestructors[i]
|
||||||
|
# Process any nodes left over from the last call to `myClose`.
|
||||||
|
result = myProcess(b, n)
|
||||||
|
# Some codegen is different (such as no stacktraces; see `initProcOptions`)
|
||||||
|
# when `std/system` is being processed.
|
||||||
if sfSystemModule in m.module.flags:
|
if sfSystemModule in m.module.flags:
|
||||||
PGlobals(graph.backend).inSystem = false
|
PGlobals(graph.backend).inSystem = false
|
||||||
|
# Check if codegen should continue before any files are generated.
|
||||||
|
# It may bail early is if too many errors have been raised.
|
||||||
if passes.skipCodegen(m.config, n): return n
|
if passes.skipCodegen(m.config, n): return n
|
||||||
|
# Nim modules are compiled into a single JS file.
|
||||||
|
# If this is the main module, then this is the final call to `myClose`.
|
||||||
if sfMainModule in m.module.flags:
|
if sfMainModule in m.module.flags:
|
||||||
var code = genHeader() & wholeCode(graph, m)
|
var code = genHeader() & wholeCode(graph, m)
|
||||||
let outFile = m.config.prepareToWriteOutput()
|
let outFile = m.config.prepareToWriteOutput()
|
||||||
|
# Generate an optional source map.
|
||||||
if optSourcemap in m.config.globalOptions:
|
if optSourcemap in m.config.globalOptions:
|
||||||
var map: SourceMap
|
var map: SourceMap
|
||||||
(code, map) = genSourceMap($(code), outFile.string)
|
(code, map) = genSourceMap($(code), outFile.string)
|
||||||
writeFile(outFile.string & ".map", $(%map))
|
writeFile(outFile.string & ".map", $(%map))
|
||||||
|
# Check if the generated JS code matches the output file, or else
|
||||||
|
# write it to the file.
|
||||||
if not equalsFile(code, outFile):
|
if not equalsFile(code, outFile):
|
||||||
if not writeRope(code, outFile):
|
if not writeRope(code, outFile):
|
||||||
rawMessage(m.config, errCannotOpenFile, outFile.string)
|
rawMessage(m.config, errCannotOpenFile, outFile.string)
|
||||||
|
|
||||||
|
|
||||||
proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
|
proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
|
||||||
|
## Create the JS backend pass context `BModule` for a Nim module.
|
||||||
result = newModule(graph, s)
|
result = newModule(graph, s)
|
||||||
result.idgen = idgen
|
result.idgen = idgen
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
matrix: "--gc:refc; --gc:arc"
|
matrix: "--mm:refc; --mm:arc"
|
||||||
|
targets: "c js"
|
||||||
output: '''
|
output: '''
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue