diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ccgtypes.nim | 29 | ||||
-rw-r--r-- | compiler/commands.nim | 4 | ||||
-rw-r--r-- | compiler/jsgen.nim | 76 | ||||
-rw-r--r-- | compiler/jstypes.nim | 4 | ||||
-rw-r--r-- | compiler/options.nim | 3 | ||||
-rw-r--r-- | compiler/sighashes.nim | 31 |
6 files changed, 91 insertions, 56 deletions
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index cc27bc4e0..56fb379bd 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -44,38 +44,11 @@ when false: assert p.kind == skPackage result = gDebugInfo.register(p.name.s, m.name.s) -proc idOrSig(m: BModule; s: PSym): Rope = - if s.kind in routineKinds and s.typ != nil: - # signatures for exported routines are reliable enough to - # produce a unique name and this means produced C++ is more stable wrt - # Nim changes: - let sig = hashProc(s) - result = rope($sig) - #let m = if s.typ.callConv != ccInline: findPendingModule(m, s) else: m - let counter = m.sigConflicts.getOrDefault(sig) - #if sigs == "_jckmNePK3i2MFnWwZlp6Lg" and s.name.s == "contains": - # echo "counter ", counter, " ", s.id - if counter != 0: - result.add "_" & rope(counter+1) - # this minor hack is necessary to make tests/collections/thashes compile. - # The inlined hash function's original module is ambiguous so we end up - # generating duplicate names otherwise: - if s.typ.callConv == ccInline: - result.add rope(m.module.name.s) - m.sigConflicts.inc(sig) - else: - let sig = hashNonProc(s) - result = rope($sig) - let counter = m.sigConflicts.getOrDefault(sig) - if counter != 0: - result.add "_" & rope(counter+1) - m.sigConflicts.inc(sig) - proc mangleName(m: BModule; s: PSym): Rope = result = s.loc.r if result == nil: result = s.name.s.mangle.rope - add(result, m.idOrSig(s)) + add(result, idOrSig(s, m.module.name.s, m.sigConflicts)) s.loc.r = result writeMangledName(m.ndi, s) diff --git a/compiler/commands.nim b/compiler/commands.nim index 2e9b6f8db..5b8f569ac 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -473,6 +473,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; processOnOffSwitch({optMemTracker}, arg, pass, info) if optMemTracker in gOptions: defineSymbol("memtracker") else: undefSymbol("memtracker") + of "hotreloading": + processOnOffSwitch({optHotReloading}, arg, pass, info) + if optHotReloading in gOptions: defineSymbol("hotreloading") + else: undefSymbol("hotreloading") of "oldnewlines": case arg.normalize of "on": diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 357708bb9..727e9209d 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -31,9 +31,9 @@ implements the required case distinction. import ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, - nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, + nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables, times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, - intsets, cgmeth, lowerings + intsets, cgmeth, lowerings, sighashes from modulegraphs import ModuleGraph @@ -43,6 +43,7 @@ type TJSGen = object of TPassContext module: PSym target: TTarget + sigConflicts: CountTable[SigHash] BModule = ref TJSGen TJSTypeKind = enum # necessary JS "types" @@ -210,7 +211,7 @@ proc mapType(p: PProc; typ: PType): TJSTypeKind = if p.target == targetPHP: result = etyObject else: result = mapType(typ) -proc mangleName(s: PSym; target: TTarget): Rope = +proc mangleName(m: BModule, s: PSym): Rope = proc validJsName(name: string): bool = result = true const reservedWords = ["abstract", "await", "boolean", "break", "byte", @@ -235,7 +236,7 @@ proc mangleName(s: PSym; target: TTarget): Rope = if result == nil: if s.kind == skField and s.name.s.validJsName: result = rope(s.name.s) - elif target == targetJS or s.kind == skTemp: + elif m.target == targetJS or s.kind == skTemp: result = rope(mangle(s.name.s)) else: var x = newStringOfCap(s.name.s.len) @@ -254,8 +255,13 @@ proc mangleName(s: PSym; target: TTarget): Rope = inc i result = rope(x) if s.name.s != "this" and s.kind != skField: - add(result, "_") - add(result, rope(s.id)) + if optHotReloading in gOptions: + # When hot reloading is enabled, we must ensure that the names + # of functions and types will be preserved across rebuilds: + add(result, idOrSig(s, m.module.name.s, m.sigConflicts)) + else: + add(result, "_") + add(result, rope(s.id)) s.loc.r = result proc escapeJSString(s: string): string = @@ -821,7 +827,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = # for backwards compatibility we don't deref syms here :-( if v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}: if p.target == targetPHP: p.body.add "$" - p.body.add mangleName(v, p.target) + p.body.add mangleName(p.module, v) else: var r: TCompRes gen(p, it, r) @@ -862,7 +868,7 @@ proc generateHeader(p: PProc, typ: PType): Rope = var param = typ.n.sons[i].sym if isCompileTimeOnly(param.typ): continue if result != nil: add(result, ", ") - var name = mangleName(param, p.target) + var name = mangleName(p.module, param) if p.target == targetJS: add(result, name) if mapType(param.typ) == etyBaseIndex: @@ -1012,7 +1018,7 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = else: if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr") var f = b.sons[1].sym - if f.loc.r == nil: f.loc.r = mangleName(f, p.target) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) r.res = makeJSString($f.loc.r) internalAssert a.typ != etyBaseIndex r.address = a.res @@ -1028,7 +1034,7 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = else: if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess") var f = n.sons[1].sym - if f.loc.r == nil: f.loc.r = mangleName(f, p.target) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) if p.target == targetJS: r.res = "$1.$2" % [r.res, f.loc.r] else: @@ -1240,7 +1246,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = r.res = "$" & s.loc.r p.declareGlobal(s.id, r.res) of skProc, skFunc, skConverter, skMethod: - discard mangleName(s, p.target) + discard mangleName(p.module, s) if p.target == targetPHP and r.kind != resCallee: r.res = makeJsString($s.loc.r) else: @@ -1396,7 +1402,7 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = # don't call '$' here for efficiency: let f = n[0].sym - if f.loc.r == nil: f.loc.r = mangleName(f, p.target) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) if sfInfixCall in f.flags: let pat = n.sons[0].sym.loc.r.data internalAssert pat != nil @@ -1467,9 +1473,9 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: if rec.sym.id notin excludedFieldIDs: if output.len > 0: output.add(", ") if p.target == targetJS: - output.addf("$#: ", [mangleName(rec.sym, p.target)]) + output.addf("$#: ", [mangleName(p.module, rec.sym)]) else: - output.addf("'$#' => ", [mangleName(rec.sym, p.target)]) + output.addf("'$#' => ", [mangleName(p.module, rec.sym)]) output.add(createVar(p, rec.sym.typ, false)) else: internalError(rec.info, "createRecordVarAux") @@ -1575,18 +1581,25 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = a: TCompRes s: Rope varCode: string + varName = mangleName(p.module, v) + useReloadingGuard = sfGlobal in v.flags and optHotReloading in gOptions + if v.constraint.isNil: - varCode = "var $2" + if useReloadingGuard: + lineF(p, "var $1;$n", varName) + lineF(p, "if ($1 === undefined) {$n", varName) + varCode = $varName + else: + varCode = "var $2" else: varCode = v.constraint.strVal + if n.kind == nkEmpty: - let mname = mangleName(v, p.target) lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", - [returnType, mname, createVar(p, v.typ, isIndirect(v))]) + [returnType, varName, createVar(p, v.typ, isIndirect(v))]) if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex: - lineF(p, "var $1_Idx = 0;$n", [ mname ]) + lineF(p, "var $1_Idx = 0;$n", [varName]) else: - discard mangleName(v, p.target) gen(p, n, a) case mapType(p, v.typ) of etyObject, etySeq: @@ -1619,6 +1632,9 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = else: lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", [returnType, v.loc.r, s]) + if useReloadingGuard: + lineF(p, "}$n") + proc genVarStmt(p: PProc, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] @@ -2031,7 +2047,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = let val = it.sons[1] gen(p, val, a) var f = it.sons[0].sym - if f.loc.r == nil: f.loc.r = mangleName(f, p.target) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) fieldIDs.incl(f.id) let typ = val.typ.skipTypes(abstractInst) @@ -2158,11 +2174,11 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = p.up = oldProc var returnStmt: Rope = nil var resultAsgn: Rope = nil - let name = mangleName(prc, p.target) + var name = mangleName(p.module, prc) let header = generateHeader(p, prc.typ) if prc.typ.sons[0] != nil and sfPure notin prc.flags: resultSym = prc.ast.sons[resultPos].sym - let mname = mangleName(resultSym, p.target) + let mname = mangleName(p.module, resultSym) let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) resultAsgn = p.indentLine(("var $# = $#;$n" | "$$$# = $#;$n") % [mname, resVar]) if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and @@ -2188,6 +2204,18 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = optionaLine(genProcBody(p, prc)), optionaLine(p.indentLine(returnStmt))] else: + result = ~tnl + + if optHotReloading in gOptions: + # Here, we introduce thunks that create the equivalent of a jump table + # for all global functions, because references to them may be stored + # in JavaScript variables. The added indirection ensures that such + # references will end up calling the reloaded code. + var thunkName = name + name = name & "IMLP" + result.add("function $#() { return $#.apply(this, arguments); }$n" % + [thunkName, name]) + def = "function $#($#) {$n$#$#$#$#$#" % [ name, header, @@ -2198,7 +2226,6 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = optionaLine(p.indentLine(returnStmt))] dec p.extraIndent - result = ~tnl result.add p.indentLine(def) result.add p.indentLine(~"}$n") @@ -2332,7 +2359,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkEmpty: discard of nkLambdaKinds: let s = n.sons[namePos].sym - discard mangleName(s, p.target) + discard mangleName(p.module, s) r.res = s.loc.r if lfNoDecl in s.loc.flags or s.magic != mNone: discard elif not p.g.generatedSyms.containsOrIncl(s.id): @@ -2389,6 +2416,7 @@ var globals: PGlobals proc newModule(module: PSym): BModule = new(result) result.module = module + result.sigConflicts = initCountTable[SigHash]() if globals == nil: globals = newGlobals() diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 3768acf27..8bd963a65 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -34,7 +34,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = s = genTypeInfo(p, field.typ) result = ("{kind: 1, offset: \"$1\", len: 0, " & "typ: $2, name: $3, sons: null}") % - [mangleName(field, p.target), s, + [mangleName(p.module, field), s, makeJSString(field.name.s)] of nkRecCase: length = sonsLen(n) @@ -63,7 +63,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = [u, genObjectFields(p, typ, lastSon(b))]) result = ("{kind: 3, offset: \"$1\", len: $3, " & "typ: $2, name: $4, sons: [$5]}") % [ - mangleName(field, p.target), s, + mangleName(p.module, field), s, rope(lengthOrd(field.typ)), makeJSString(field.name.s), result] else: internalError(n.info, "genObjectFields") diff --git a/compiler/options.nim b/compiler/options.nim index be13e15d7..d1428312b 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -36,7 +36,8 @@ type # please make sure we have under 32 options optImplicitStatic, # optimization: implicit at compile time # evaluation optPatterns, # en/disable pattern matching - optMemTracker + optMemTracker, + optHotReloading TOptions* = set[TOption] TGlobalOption* = enum # **keep binary compatible** diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 0fea3bbe3..46b83c386 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -9,7 +9,7 @@ ## Computes hash values for routine (proc, method etc) signatures. -import ast, md5 +import ast, md5, tables, ropes from hashes import Hash from astalgo import debug from types import typeToString, preferDesc @@ -326,3 +326,32 @@ proc hashOwner*(s: PSym): SigHash = c &= m.name.s md5Final c, result.Md5Digest + +proc idOrSig*(s: PSym, currentModule: string, + sigCollisions: var CountTable[SigHash]): Rope = + if s.kind in routineKinds and s.typ != nil: + # signatures for exported routines are reliable enough to + # produce a unique name and this means produced C++ is more stable wrt + # Nim changes: + let sig = hashProc(s) + result = rope($sig) + #let m = if s.typ.callConv != ccInline: findPendingModule(m, s) else: m + let counter = sigCollisions.getOrDefault(sig) + #if sigs == "_jckmNePK3i2MFnWwZlp6Lg" and s.name.s == "contains": + # echo "counter ", counter, " ", s.id + if counter != 0: + result.add "_" & rope(counter+1) + # this minor hack is necessary to make tests/collections/thashes compile. + # The inlined hash function's original module is ambiguous so we end up + # generating duplicate names otherwise: + if s.typ.callConv == ccInline: + result.add rope(currentModule) + sigCollisions.inc(sig) + else: + let sig = hashNonProc(s) + result = rope($sig) + let counter = sigCollisions.getOrDefault(sig) + if counter != 0: + result.add "_" & rope(counter+1) + sigCollisions.inc(sig) + |