diff options
Diffstat (limited to 'compiler/jsgen.nim')
-rw-r--r-- | compiler/jsgen.nim | 947 |
1 files changed, 629 insertions, 318 deletions
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index bcabc42ed..713944def 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -31,16 +31,20 @@ implements the required case distinction. import ast, trees, magicsys, options, nversion, msgs, idents, types, - ropes, passes, ccgutils, wordrecg, renderer, - cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, - transf, injectdestructors, sourcemap, astmsgs + ropes, wordrecg, renderer, + cgmeth, lowerings, sighashes, modulegraphs, lineinfos, + transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas, + mangleutils -import json, sets, math, tables, intsets -import strutils except addf +import pipelineutils + +import std/[json, sets, math, tables, intsets] +import std/strutils except addf when defined(nimPreviewSlimSystem): import std/[assertions, syncio] +import std/formatfloat type TJSGen = object of PPassContext @@ -48,6 +52,7 @@ type graph: ModuleGraph config: ConfigRef sigConflicts: CountTable[SigHash] + initProc: PProc BModule = ref TJSGen TJSTypeKind = enum # necessary JS "types" @@ -98,27 +103,33 @@ type prc: PSym globals, locals, body: Rope options: TOptions + optionsStack: seq[(TOptions, TNoteKinds)] module: BModule g: PGlobals - generatedParamCopies: IntSet beforeRetNeeded: bool unique: int # for temp identifier generation blocks: seq[TBlock] extraIndent: int + previousFileName: string # For frameInfo inside templates. + # legacy: generatedParamCopies and up fields are used for jsNoLambdaLifting + generatedParamCopies: IntSet up: PProc # up the call chain; required for closure support - declaredGlobals: IntSet template config*(p: PProc): ConfigRef = p.module.config proc indentLine(p: PProc, r: Rope): Rope = var p = p - var ind = 0 - while true: - inc ind, p.blocks.len + p.extraIndent - if p.up == nil or p.up.prc != p.prc.owner: - break - p = p.up - result = repeat(' ', ind*2) & r + if jsNoLambdaLifting in p.config.legacyFeatures: + var ind = 0 + while true: + inc ind, p.blocks.len + p.extraIndent + if p.up == nil or p.up.prc != p.prc.owner: + break + p = p.up + result = repeat(' ', ind*2) & r + else: + let ind = p.blocks.len + p.extraIndent + result = repeat(' ', ind*2) & r template line(p: PProc, added: string) = p.body.add(indentLine(p, rope(added))) @@ -132,17 +143,15 @@ template nested(p, body) = dec p.extraIndent proc newGlobals(): PGlobals = - new(result) - result.forwarded = @[] - result.generatedSyms = initIntSet() - result.typeInfoGenerated = initIntSet() + result = PGlobals(forwarded: @[], + generatedSyms: initIntSet(), + typeInfoGenerated: initIntSet() + ) -proc initCompRes(r: var TCompRes) = - r.address = "" - r.res = "" - r.tmpLoc = "" - r.typ = etyNone - r.kind = resNone +proc initCompRes(): TCompRes = + result = TCompRes(address: "", res: "", + tmpLoc: "", typ: etyNone, kind: resNone + ) proc rdLoc(a: TCompRes): Rope {.inline.} = if a.typ != etyBaseIndex: @@ -154,6 +163,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode, options: TOptions): PProc = result = PProc( blocks: @[], + optionsStack: if module.initProc != nil: module.initProc.optionsStack + else: @[], options: options, module: module, procDef: procDef, @@ -169,10 +180,6 @@ proc initProcOptions(module: BModule): TOptions = proc newInitProc(globals: PGlobals, module: BModule): PProc = result = newProc(globals, module, nil, initProcOptions(module)) -proc declareGlobal(p: PProc; id: int; r: Rope) = - if p.prc != nil and not p.declaredGlobals.containsOrIncl(id): - p.locals.addf("global $1;$n", [r]) - const MappedToObject = {tyObject, tyArray, tyTuple, tyOpenArray, tySet, tyVarargs} @@ -181,16 +188,16 @@ proc mapType(typ: PType): TJSTypeKind = let t = skipTypes(typ, abstractInst) case t.kind of tyVar, tyRef, tyPtr: - if skipTypes(t.lastSon, abstractInst).kind in MappedToObject: + if skipTypes(t.elementType, abstractInst).kind in MappedToObject: result = etyObject else: result = etyBaseIndex of tyPointer: # treat a tyPointer like a typed pointer to an array of bytes result = etyBaseIndex - of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent: + of tyRange, tyDistinct, tyOrdinal, tyError, tyLent: # tyLent is no-op as JS has pass-by-reference semantics - result = mapType(t[0]) + result = mapType(skipModifier t) of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt of tyBool: result = etyBool of tyFloat..tyFloat128: result = etyFloat @@ -206,13 +213,14 @@ proc mapType(typ: PType): TJSTypeKind = result = etyNone of tyGenericInst, tyInferred, tyAlias, tyUserTypeClass, tyUserTypeClassInst, tySink, tyOwned: - result = mapType(typ.lastSon) + result = mapType(typ.skipModifier) of tyStatic: - if t.n != nil: result = mapType(lastSon t) + if t.n != nil: result = mapType(skipModifier t) else: result = etyNone of tyProc: result = etyProc of tyCstring: result = etyString - of tyConcept, tyIterable: doAssert false + of tyConcept, tyIterable: + raiseAssert "unreachable" proc mapType(p: PProc; typ: PType): TJSTypeKind = result = mapType(typ) @@ -238,7 +246,7 @@ proc mangleName(m: BModule, s: PSym): Rope = for chr in name: if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}: return false - result = s.loc.r + result = s.loc.snippet if result == "": if s.kind == skField and s.name.s.validJsName: result = rope(s.name.s) @@ -261,11 +269,15 @@ proc mangleName(m: BModule, s: PSym): Rope = if m.config.hcrOn: # When hot reloading is enabled, we must ensure that the names # of functions and types will be preserved across rebuilds: - result.add(idOrSig(s, m.module.name.s, m.sigConflicts)) + result.add(idOrSig(s, m.module.name.s, m.sigConflicts, m.config)) + elif s.kind == skParam: + result.add mangleParamExt(s) + elif s.kind in routineKinds: + result.add mangleProcNameExt(m.graph, s) else: result.add("_") result.add(rope(s.id)) - s.loc.r = result + s.loc.snippet = result proc escapeJSString(s: string): string = result = newStringOfCap(s.len + s.len shr 2) @@ -341,8 +353,7 @@ proc isSimpleExpr(p: PProc; n: PNode): bool = if n[i].kind notin {nkCommentStmt, nkEmpty}: return false result = isSimpleExpr(p, n.lastSon) else: - if n.isAtom: - result = true + result = n.isAtom proc getTemp(p: PProc, defineInLocals: bool = true): Rope = inc(p.unique) @@ -352,7 +363,7 @@ proc getTemp(p: PProc, defineInLocals: bool = true): Rope = proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) if p.isSimpleExpr(a) and p.isSimpleExpr(b): gen(p, a, x) gen(p, b, y) @@ -379,7 +390,7 @@ proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) if p.isSimpleExpr(a) and p.isSimpleExpr(b): gen(p, a, x) gen(p, b, y) @@ -458,15 +469,13 @@ const # magic checked op; magic unchecked op; mUnaryMinusF64: ["", ""], mCharToStr: ["nimCharToStr", "nimCharToStr"], mBoolToStr: ["nimBoolToStr", "nimBoolToStr"], - mIntToStr: ["cstrToNimstr", "cstrToNimstr"], - mInt64ToStr: ["cstrToNimstr", "cstrToNimstr"], - mFloatToStr: ["cstrToNimstr", "cstrToNimstr"], mCStrToStr: ["cstrToNimstr", "cstrToNimstr"], mStrToStr: ["", ""]] proc needsTemp(p: PProc; n: PNode): bool = # check if n contains a call to determine # if a temp should be made to prevent multiple evals + result = false if n.kind in nkCallKinds + {nkTupleConstr, nkObjConstr, nkBracket, nkCurly}: return true for c in n: @@ -502,14 +511,14 @@ proc maybeMakeTempAssignable(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rop elif x.tmpLoc != "" and n.kind == nkBracketExpr: # genArrayAddr var - address, index: TCompRes - first: Int128 + address, index: TCompRes = default(TCompRes) + first: Int128 = Zero gen(p, n[0], address) gen(p, n[1], index) let (m1, tmp1) = maybeMakeTemp(p, n[0], address) let typ = skipTypes(n[0].typ, abstractPtrs) if typ.kind == tyArray: - first = firstOrd(p.config, typ[0]) + first = firstOrd(p.config, typ.indexType) if optBoundsCheck in p.options: useMagic(p, "chckIndx") if first == 0: # save a couple chars @@ -535,7 +544,7 @@ template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string, # $1 and $2 in the `frmt` string bind to lhs and rhs of the expr, # if $3 or $4 are present they will be substituted with temps for # lhs and rhs respectively - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) useMagic(p, magic) gen(p, n[1], x) gen(p, n[2], y) @@ -552,28 +561,36 @@ template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string, r.res = frmt % [a, b, tmp, tmp2] r.kind = resExpr -proc unsignedTrimmerJS(size: BiggestInt): Rope = +proc unsignedTrimmer(size: BiggestInt): string = case size - of 1: rope"& 0xff" - of 2: rope"& 0xffff" - of 4: rope">>> 0" - else: rope"" - + of 1: "& 0xff" + of 2: "& 0xffff" + of 4: ">>> 0" + else: "" -template unsignedTrimmer(size: BiggestInt): Rope = - size.unsignedTrimmerJS +proc signedTrimmer(size: BiggestInt): string = + # sign extension is done by shifting to the left and then back to the right + "<< $1 >> $1" % [$(32 - size * 8)] proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign: static[bool] = false) = - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) gen(p, n[1], x) gen(p, n[2], y) - let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) + let size = n[1].typ.skipTypes(abstractRange).size when reassign: let (a, tmp) = maybeMakeTempAssignable(p, n[1], x) - r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp] + if size == 8 and optJsBigInt64 in p.config.globalOptions: + r.res = "$1 = BigInt.asUintN(64, ($4 $2 $3))" % [a, rope op, y.rdLoc, tmp] + else: + let trimmer = unsignedTrimmer(size) + r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp] else: - r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] + if size == 8 and optJsBigInt64 in p.config.globalOptions: + r.res = "BigInt.asUintN(64, ($1 $2 $3))" % [x.rdLoc, rope op, y.rdLoc] + else: + let trimmer = unsignedTrimmer(size) + r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] r.kind = resExpr template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = @@ -594,10 +611,21 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = r.res = frmt % [a, tmp] r.kind = resExpr +proc genBreakState(p: PProc, n: PNode, r: var TCompRes) = + var a: TCompRes = default(TCompRes) + # mangle `:state` properly somehow + if n.kind == nkClosure: + gen(p, n[1], a) + r.res = "(($1).HEX3Astate < 0)" % [rdLoc(a)] + else: + gen(p, n, a) + r.res = "((($1.ClE_0).HEX3Astate) < 0)" % [rdLoc(a)] + r.kind = resExpr + proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = var - x, y: TCompRes - xLoc, yLoc: Rope + x, y: TCompRes = default(TCompRes) + xLoc, yLoc: Rope = "" let i = ord(optOverflowCheck notin p.options) useMagic(p, jsMagics[op][i]) if n.len > 2: @@ -614,32 +642,133 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = template applyFormat(frmtA, frmtB) = if i == 0: applyFormat(frmtA) else: applyFormat(frmtB) + template bitwiseExpr(op: string) = + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind in {tyUInt, tyUInt32}: + r.res = "(($1 $2 $3) >>> 0)" % [xLoc, op, yLoc] + else: + r.res = "($1 $2 $3)" % [xLoc, op, yLoc] + case op - of mAddI: applyFormat("addInt($1, $2)", "($1 + $2)") - of mSubI: applyFormat("subInt($1, $2)", "($1 - $2)") - of mMulI: applyFormat("mulInt($1, $2)", "($1 * $2)") - of mDivI: applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)") - of mModI: applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)") - of mSucc: applyFormat("addInt($1, $2)", "($1 + $2)") - of mPred: applyFormat("subInt($1, $2)", "($1 - $2)") + of mAddI: + if i == 0: + if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions: + useMagic(p, "addInt64") + applyFormat("addInt64($1, $2)") + else: + applyFormat("addInt($1, $2)") + else: + applyFormat("($1 + $2)") + of mSubI: + if i == 0: + if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions: + useMagic(p, "subInt64") + applyFormat("subInt64($1, $2)") + else: + applyFormat("subInt($1, $2)") + else: + applyFormat("($1 - $2)") + of mMulI: + if i == 0: + if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions: + useMagic(p, "mulInt64") + applyFormat("mulInt64($1, $2)") + else: + applyFormat("mulInt($1, $2)") + else: + applyFormat("($1 * $2)") + of mDivI: + if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions: + useMagic(p, "divInt64") + applyFormat("divInt64($1, $2)", "$1 / $2") + else: + applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)") + of mModI: + if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions: + useMagic(p, "modInt64") + applyFormat("modInt64($1, $2)", "$1 % $2") + else: + applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)") + of mSucc: + let typ = n[1].typ.skipTypes(abstractVarRange) + case typ.kind + of tyUInt..tyUInt32: + binaryUintExpr(p, n, r, "+") + of tyUInt64: + if optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asUintN(64, $1 + BigInt($2))") + else: binaryUintExpr(p, n, r, "+") + elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + if optOverflowCheck notin p.options: + applyFormat("BigInt.asIntN(64, $1 + BigInt($2))") + else: binaryExpr(p, n, r, "addInt64", "addInt64($1, BigInt($2))") + else: + if optOverflowCheck notin p.options: applyFormat("$1 + $2") + else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)") + of mPred: + let typ = n[1].typ.skipTypes(abstractVarRange) + case typ.kind + of tyUInt..tyUInt32: + binaryUintExpr(p, n, r, "-") + of tyUInt64: + if optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asUintN(64, $1 - BigInt($2))") + else: binaryUintExpr(p, n, r, "-") + elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + if optOverflowCheck notin p.options: + applyFormat("BigInt.asIntN(64, $1 - BigInt($2))") + else: binaryExpr(p, n, r, "subInt64", "subInt64($1, BigInt($2))") + else: + if optOverflowCheck notin p.options: applyFormat("$1 - $2") + else: binaryExpr(p, n, r, "subInt", "subInt($1, $2)") of mAddF64: applyFormat("($1 + $2)", "($1 + $2)") of mSubF64: applyFormat("($1 - $2)", "($1 - $2)") of mMulF64: applyFormat("($1 * $2)", "($1 * $2)") of mDivF64: applyFormat("($1 / $2)", "($1 / $2)") - of mShrI: applyFormat("", "") + of mShrI: + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))") + elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: + applyFormat("($1 >> BigInt($2))") + else: + if typ.kind in {tyInt..tyInt32}: + let trimmerU = unsignedTrimmer(typ.size) + let trimmerS = signedTrimmer(typ.size) + r.res = "((($1 $2) >>> $3) $4)" % [xLoc, trimmerU, yLoc, trimmerS] + else: + applyFormat("($1 >>> $2)") of mShlI: - if n[1].typ.size <= 4: - applyFormat("($1 << $2)", "($1 << $2)") + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.size == 8: + if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asIntN(64, $1 << BigInt($2))") + elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asUintN(64, $1 << BigInt($2))") + else: + applyFormat("($1 * Math.pow(2, $2))") else: - applyFormat("($1 * Math.pow(2, $2))", "($1 * Math.pow(2, $2))") + if typ.kind in {tyUInt..tyUInt32}: + let trimmer = unsignedTrimmer(typ.size) + r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer] + else: + let trimmer = signedTrimmer(typ.size) + r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer] of mAshrI: - if n[1].typ.size <= 4: - applyFormat("($1 >> $2)", "($1 >> $2)") + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.size == 8: + if optJsBigInt64 in p.config.globalOptions: + applyFormat("($1 >> BigInt($2))") + else: + applyFormat("Math.floor($1 / Math.pow(2, $2))") else: - applyFormat("Math.floor($1 / Math.pow(2, $2))", "Math.floor($1 / Math.pow(2, $2))") - of mBitandI: applyFormat("($1 & $2)", "($1 & $2)") - of mBitorI: applyFormat("($1 | $2)", "($1 | $2)") - of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)") + if typ.kind in {tyUInt..tyUInt32}: + applyFormat("($1 >>> $2)") + else: + applyFormat("($1 >> $2)") + of mBitandI: bitwiseExpr("&") + of mBitorI: bitwiseExpr("|") + of mBitxorI: bitwiseExpr("^") of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)") of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)") of mAddU: applyFormat("", "") @@ -672,16 +801,29 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mEqProc: applyFormat("($1 == $2)", "($1 == $2)") of mUnaryMinusI: applyFormat("negInt($1)", "-($1)") of mUnaryMinusI64: applyFormat("negInt64($1)", "-($1)") - of mAbsI: applyFormat("absInt($1)", "Math.abs($1)") + of mAbsI: + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + useMagic(p, "absInt64") + applyFormat("absInt64($1)", "absInt64($1)") + else: + applyFormat("absInt($1)", "Math.abs($1)") of mNot: applyFormat("!($1)", "!($1)") of mUnaryPlusI: applyFormat("+($1)", "+($1)") - of mBitnotI: applyFormat("~($1)", "~($1)") + of mBitnotI: + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind in {tyUInt..tyUInt64}: + if typ.size == 8 and optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asUintN(64, ~($1))") + else: + let trimmer = unsignedTrimmer(typ.size) + r.res = "(~($1) $2)" % [xLoc, trimmer] + else: + applyFormat("~($1)") of mUnaryPlusF64: applyFormat("+($1)", "+($1)") of mUnaryMinusF64: applyFormat("-($1)", "-($1)") of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)") of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)") - of mIntToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") - of mInt64ToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)") of mStrToStr, mUnown, mIsolate, mFinished: applyFormat("$1", "$1") else: @@ -694,27 +836,29 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mMulU: binaryUintExpr(p, n, r, "*") of mDivU: binaryUintExpr(p, n, r, "/") - if n[1].typ.skipTypes(abstractRange).size == 8: + if optJsBigInt64 notin p.config.globalOptions and + n[1].typ.skipTypes(abstractRange).size == 8: + # bigint / already truncates r.res = "Math.trunc($1)" % [r.res] of mDivI: arithAux(p, n, r, op) of mModI: arithAux(p, n, r, op) - of mShrI: - var x, y: TCompRes - gen(p, n[1], x) - gen(p, n[2], y) - r.res = "($1 >>> $2)" % [x.rdLoc, y.rdLoc] - of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr: + of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr: arithAux(p, n, r, op) of mEqRef: if mapType(n[1].typ) != etyBaseIndex: arithAux(p, n, r, op) else: - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) gen(p, n[1], x) gen(p, n[2], y) r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res] + of mEqProc: + if skipTypes(n[1].typ, abstractInst).callConv == ccClosure: + binaryExpr(p, n, r, "cmpClosures", "cmpClosures($1, $2)") + else: + arithAux(p, n, r, op) else: arithAux(p, n, r, op) r.kind = resExpr @@ -724,20 +868,27 @@ proc hasFrameInfo(p: PProc): bool = ((p.prc == nil) or not (sfPure in p.prc.flags)) proc lineDir(config: ConfigRef, info: TLineInfo, line: int): Rope = - ropes.`%`("/* line $2 \"$1\" */$n", - [rope(toFullPath(config, info)), rope(line)]) + "/* line $2:$3 \"$1\" */$n" % [ + rope(toFullPath(config, info)), rope(line), rope(info.toColumn) + ] proc genLineDir(p: PProc, n: PNode) = let line = toLinenumber(n.info) if line < 0: return + if optEmbedOrigSrc in p.config.globalOptions: + lineF(p, "//$1$n", [sourceLine(p.config, n.info)]) if optLineDir in p.options or optLineDir in p.config.options: lineF(p, "$1", [lineDir(p.config, n.info, line)]) if hasFrameInfo(p): lineF(p, "F.line = $1;$n", [rope(line)]) + let currentFileName = toFilename(p.config, n.info) + if p.previousFileName != currentFileName: + lineF(p, "F.filename = $1;$n", [makeJSString(currentFileName)]) + p.previousFileName = currentFileName proc genWhileStmt(p: PProc, n: PNode) = - var cond: TCompRes + var cond: TCompRes = default(TCompRes) internalAssert p.config, isEmptyType(n.typ) genLineDir(p, n) inc(p.unique) @@ -797,7 +948,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = p.body.add("++excHandler;\L") var tmpFramePtr = rope"F" lineF(p, "try {$n", []) - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, n[0], a) moveInto(p, a, r) var generalCatchBranchExists = false @@ -832,6 +983,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = elif it.kind == nkType: throwObj = it else: + throwObj = nil internalError(p.config, n.info, "genTryStmt") if orExpr != "": orExpr.add("||") @@ -840,7 +992,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = # if isJsObject(throwObj.typ): if isImportedException(throwObj.typ, p.config): orExpr.addf("lastJSError instanceof $1", - [throwObj.typ.sym.loc.r]) + [throwObj.typ.sym.loc.snippet]) else: orExpr.addf("isObj(lastJSError.m_type, $1)", [genTypeInfo(p, throwObj.typ)]) @@ -850,8 +1002,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = # If some branch requires a local alias introduce it here. This is needed # since JS cannot do ``catch x as y``. if excAlias != nil: - excAlias.sym.loc.r = mangleName(p.module, excAlias.sym) - lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r) + excAlias.sym.loc.snippet = mangleName(p.module, excAlias.sym) + lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.snippet) gen(p, n[i][^1], a) moveInto(p, a, r) lineF(p, "}$n", []) @@ -872,7 +1024,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = proc genRaiseStmt(p: PProc, n: PNode) = if n[0].kind != nkEmpty: - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, n[0], a) let typ = skipTypes(n[0].typ, abstractPtrs) genLineDir(p, n) @@ -886,10 +1038,10 @@ proc genRaiseStmt(p: PProc, n: PNode) = proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var - a, b, cond, stmt: TCompRes + a, b, cond, stmt: TCompRes = default(TCompRes) genLineDir(p, n) gen(p, n[0], cond) - let typeKind = skipTypes(n[0].typ, abstractVar).kind + let typeKind = skipTypes(n[0].typ, abstractVar+{tyRange}).kind var transferRange = false let anyString = typeKind in {tyString, tyCstring} case typeKind @@ -957,14 +1109,19 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = lineF(p, "break;$n", []) of nkElse: if transferRange: - lineF(p, "else{$n", []) + if n.len == 2: # a dangling else for a case statement + transferRange = false + lineF(p, "switch ($1) {$n", [cond.rdLoc]) + lineF(p, "default: $n", []) + else: + lineF(p, "else{$n", []) else: lineF(p, "default: $n", []) p.nested: gen(p, it[0], stmt) moveInto(p, stmt, r) if transferRange: - lineF(p, "}$n", []) + lineF(p, "}$n", []) else: lineF(p, "break;$n", []) else: internalError(p.config, it.info, "jsgen.genCaseStmt") @@ -1006,10 +1163,14 @@ proc genBreakStmt(p: PProc, n: PNode) = p.blocks[idx].id = abs(p.blocks[idx].id) # label is used lineF(p, "break Label$1;$n", [rope(p.blocks[idx].id)]) -proc genAsmOrEmitStmt(p: PProc, n: PNode) = +proc genAsmOrEmitStmt(p: PProc, n: PNode; isAsmStmt = false) = genLineDir(p, n) p.body.add p.indentLine("") - for i in 0..<n.len: + let offset = + if isAsmStmt: 1 # first son is pragmas + else: 0 + + for i in offset..<n.len: let it = n[i] case it.kind of nkStrLit..nkTripleStrLit: @@ -1020,7 +1181,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = if false: discard else: - var r: TCompRes + var r = default(TCompRes) gen(p, it, r) if it.typ.kind == tyPointer: @@ -1036,13 +1197,13 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = p.body.add(r.rdLoc) else: - var r: TCompRes + var r: TCompRes = default(TCompRes) gen(p, it, r) p.body.add(r.rdLoc) p.body.add "\L" proc genIf(p: PProc, n: PNode, r: var TCompRes) = - var cond, stmt: TCompRes + var cond, stmt: TCompRes = default(TCompRes) var toClose = 0 if not isEmptyType(n.typ): r.kind = resVal @@ -1064,8 +1225,17 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) = lineF(p, "}$n", []) line(p, repeat('}', toClose) & "\L") -proc generateHeader(p: PProc, typ: PType): Rope = +proc generateHeader(p: PProc, prc: PSym): Rope = result = "" + let typ = prc.typ + if jsNoLambdaLifting notin p.config.legacyFeatures: + if typ.callConv == ccClosure: + # we treat Env as the `this` parameter of the function + # to keep it simple + let env = prc.ast[paramsPos].lastSon + assert env.kind == nkSym, "env is missing" + env.sym.loc.snippet = "this" + for i in 1..<typ.n.len: assert(typ.n[i].kind == nkSym) var param = typ.n[i].sym @@ -1079,6 +1249,7 @@ proc generateHeader(p: PProc, typ: PType): Rope = result.add("_Idx") proc countJsParams(typ: PType): int = + result = 0 for i in 1..<typ.n.len: assert(typ.n[i].kind == nkSym) var param = typ.n[i].sym @@ -1097,12 +1268,13 @@ const proc needsNoCopy(p: PProc; y: PNode): bool = return y.kind in nodeKindsNeedNoCopy or - ((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and + ((mapType(y.typ) != etyBaseIndex or + (jsNoLambdaLifting in p.config.legacyFeatures and y.kind == nkSym and y.sym.kind == skParam)) and (skipTypes(y.typ, abstractInst).kind in - {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned} + IntegralTypes)) + {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned, tyOpenArray} + IntegralTypes)) proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = - var a, b: TCompRes + var a, b: TCompRes = default(TCompRes) var xtyp = mapType(p, x.typ) # disable `[]=` for cstring @@ -1126,7 +1298,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = lineF(p, "$1 = nimCopy(null, $2, $3);$n", [a.rdLoc, b.res, genTypeInfo(p, y.typ)]) of etyObject: - if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: + if x.typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") @@ -1181,28 +1353,21 @@ proc genFastAsgn(p: PProc, n: PNode) = genAsgnAux(p, n[0], n[1], noCopyNeeded=noCopy) proc genSwap(p: PProc, n: PNode) = - var a, b: TCompRes - gen(p, n[1], a) - gen(p, n[2], b) - var tmp = p.getTemp(false) - if mapType(p, skipTypes(n[1].typ, abstractVar)) == etyBaseIndex: - let tmp2 = p.getTemp(false) - if a.typ != etyBaseIndex or b.typ != etyBaseIndex: - internalError(p.config, n.info, "genSwap") - lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n", - [tmp, a.address, b.address]) - tmp = tmp2 - lineF(p, "var $1 = $2; $2 = $3; $3 = $1;", - [tmp, a.res, b.res]) + let stmtList = lowerSwap(p.module.graph, n, p.module.idgen, if p.prc != nil: p.prc else: p.module.module) + assert stmtList.kind == nkStmtList + for i in 0..<stmtList.len: + genStmt(p, stmtList[i]) proc getFieldPosition(p: PProc; f: PNode): int = case f.kind of nkIntLit..nkUInt64Lit: result = int(f.intVal) of nkSym: result = f.sym.position - else: internalError(p.config, f.info, "genFieldPosition") + else: + result = 0 + internalError(p.config, f.info, "genFieldPosition") proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = - var a: TCompRes + var a: TCompRes = default(TCompRes) r.typ = etyBaseIndex let b = if n.kind == nkHiddenAddr: n[0] else: n gen(p, b[0], a) @@ -1211,8 +1376,8 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = else: if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr") var f = b[1].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) - r.res = makeJSString($f.loc.r) + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) + r.res = makeJSString($f.loc.snippet) internalAssert p.config, a.typ != etyBaseIndex r.address = a.res r.kind = resExpr @@ -1239,8 +1404,8 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = else: if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess") var f = n[1].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) - r.res = "$1.$2" % [r.res, f.loc.r] + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) + r.res = "$1.$2" % [r.res, f.loc.snippet] mkTemp(1) r.kind = resExpr @@ -1260,16 +1425,16 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = # Field symbol var field = accessExpr[1].sym internalAssert p.config, field.kind == skField - if field.loc.r == "": field.loc.r = mangleName(p.module, field) + if field.loc.snippet == "": field.loc.snippet = mangleName(p.module, field) # Discriminant symbol let disc = checkExpr[2].sym internalAssert p.config, disc.kind == skField - if disc.loc.r == "": disc.loc.r = mangleName(p.module, disc) + if disc.loc.snippet == "": disc.loc.snippet = mangleName(p.module, disc) - var setx: TCompRes + var setx: TCompRes = default(TCompRes) gen(p, checkExpr[1], setx) - var obj: TCompRes + var obj: TCompRes = default(TCompRes) gen(p, accessExpr[0], obj) # Avoid evaluating the LHS twice (one to read the discriminant and one to read # the field) @@ -1281,22 +1446,22 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen let msg = genFieldDefect(p.config, field.name.s, disc) lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n", - setx.res, tmp, disc.loc.r, if negCheck: "!==" else: "===", + setx.res, tmp, disc.loc.snippet, if negCheck: "!==" else: "===", makeJSString(msg), genTypeInfo(p, disc.typ)) if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex: r.typ = etyBaseIndex - r.res = makeJSString($field.loc.r) + r.res = makeJSString($field.loc.snippet) r.address = tmp else: r.typ = etyNone - r.res = "$1.$2" % [tmp, field.loc.r] + r.res = "$1.$2" % [tmp, field.loc.snippet] r.kind = resExpr proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = var - a, b: TCompRes - first: Int128 + a, b: TCompRes = default(TCompRes) + first: Int128 = Zero r.typ = etyBaseIndex let m = if n.kind == nkHiddenAddr: n[0] else: n gen(p, m[0], a) @@ -1306,7 +1471,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = r.address = x var typ = skipTypes(m[0].typ, abstractPtrs) if typ.kind == tyArray: - first = firstOrd(p.config, typ[0]) + first = firstOrd(p.config, typ.indexType) if optBoundsCheck in p.options: useMagic(p, "chckIndx") if first == 0: # save a couple chars @@ -1321,8 +1486,8 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = - var ty = skipTypes(n[0].typ, abstractVarRange) - if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.lastSon, abstractVarRange) + var ty = skipTypes(n[0].typ, abstractVarRange+tyUserTypeClasses) + if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.elementType, abstractVarRange) case ty.kind of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: genArrayAddr(p, n, r) @@ -1357,10 +1522,10 @@ template isIndirect(x: PSym): bool = proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) = let s = n.sym - if s.loc.r == "": internalError(p.config, n.info, "genAddr: 3") + if s.loc.snippet == "": internalError(p.config, n.info, "genAddr: 3") case s.kind of skParam: - r.res = s.loc.r + r.res = s.loc.snippet r.address = "" r.typ = etyNone of skVar, skLet, skResult: @@ -1374,15 +1539,15 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) = # make addr() a no-op: r.typ = etyNone if isIndirect(s): - r.res = s.loc.r & "[0]" + r.res = s.loc.snippet & "[0]" else: - r.res = s.loc.r + r.res = s.loc.snippet r.address = "" elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex: # for ease of code generation, we do not distinguish between # sfAddrTaken and sfGlobal. r.typ = etyBaseIndex - r.address = s.loc.r + r.address = s.loc.snippet r.res = rope("0") else: # 'var openArray' for instance produces an 'addr' but this is harmless: @@ -1409,7 +1574,7 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = if ty.kind in MappedToObject: gen(p, n[0], r) else: - let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange).kind + let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange+tyUserTypeClasses).kind case kindOfIndexedExpr of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: genArrayAddr(p, n[0], r) @@ -1455,12 +1620,15 @@ proc attachProc(p: PProc; s: PSym) = proc genProcForSymIfNeeded(p: PProc, s: PSym) = if not p.g.generatedSyms.containsOrIncl(s.id): - let newp = genProc(p, s) - var owner = p - while owner != nil and owner.prc != s.owner: - owner = owner.up - if owner != nil: owner.locals.add(newp) - else: attachProc(p, newp, s) + if jsNoLambdaLifting in p.config.legacyFeatures: + let newp = genProc(p, s) + var owner = p + while owner != nil and owner.prc != s.owner: + owner = owner.up + if owner != nil: owner.locals.add(newp) + else: attachProc(p, newp, s) + else: + attachProc(p, s) proc genCopyForParamIfNeeded(p: PProc, n: PNode) = let s = n.sym @@ -1472,7 +1640,7 @@ proc genCopyForParamIfNeeded(p: PProc, n: PNode) = internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s) if owner.prc == s.owner: if not owner.generatedParamCopies.containsOrIncl(s.id): - let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)] + let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.snippet, genTypeInfo(p, s.typ)] owner.locals.add(owner.indentLine(copy)) return owner = owner.up @@ -1483,40 +1651,40 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = var s = n.sym case s.kind of skVar, skLet, skParam, skTemp, skResult, skForVar: - if s.loc.r == "": + if s.loc.snippet == "": internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) if sfCompileTime in s.flags: genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info)) - if s.kind == skParam: + if jsNoLambdaLifting in p.config.legacyFeatures and s.kind == skParam: genCopyForParamIfNeeded(p, n) let k = mapType(p, s.typ) if k == etyBaseIndex: r.typ = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: if isIndirect(s): - r.address = "$1[0][0]" % [s.loc.r] - r.res = "$1[0][1]" % [s.loc.r] + r.address = "$1[0][0]" % [s.loc.snippet] + r.res = "$1[0][1]" % [s.loc.snippet] else: - r.address = "$1[0]" % [s.loc.r] - r.res = "$1[1]" % [s.loc.r] + r.address = "$1[0]" % [s.loc.snippet] + r.res = "$1[1]" % [s.loc.snippet] else: - r.address = s.loc.r - r.res = s.loc.r & "_Idx" + r.address = s.loc.snippet + r.res = s.loc.snippet & "_Idx" elif isIndirect(s): - r.res = "$1[0]" % [s.loc.r] + r.res = "$1[0]" % [s.loc.snippet] else: - r.res = s.loc.r + r.res = s.loc.snippet of skConst: genConstant(p, s) - if s.loc.r == "": + if s.loc.snippet == "": internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) - r.res = s.loc.r - of skProc, skFunc, skConverter, skMethod: + r.res = s.loc.snippet + of skProc, skFunc, skConverter, skMethod, skIterator: if sfCompileTime in s.flags: localError(p.config, n.info, "request to generate code for .compileTime proc: " & s.name.s) discard mangleName(p.module, s) - r.res = s.loc.r + r.res = s.loc.snippet if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or {sfImportc, sfInfixCall} * s.flags != {}: discard @@ -1528,13 +1696,13 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = else: genProcForSymIfNeeded(p, s) else: - if s.loc.r == "": + if s.loc.snippet == "": internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) if mapType(p, s.typ) == etyBaseIndex: - r.address = s.loc.r - r.res = s.loc.r & "_Idx" + r.address = s.loc.snippet + r.res = s.loc.snippet & "_Idx" else: - r.res = s.loc.r + r.res = s.loc.snippet r.kind = resVal proc genDeref(p: PProc, n: PNode, r: var TCompRes) = @@ -1543,7 +1711,7 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) = if t == etyObject or it.typ.kind == tyLent: gen(p, it, r) else: - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, it, a) r.kind = a.kind r.typ = mapType(p, n.typ) @@ -1560,7 +1728,7 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) = internalError(p.config, n.info, "genDeref") proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) = - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, n, a) if a.typ == etyBaseIndex: r.res.add(a.address) @@ -1570,7 +1738,7 @@ proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) = r.res.add(a.res) proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = nil) = - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, n, a) if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and a.typ == etyBaseIndex: @@ -1676,9 +1844,9 @@ 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 == "": f.loc.r = mangleName(p.module, f) + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) if sfInfixCall in f.flags: - let pat = $n[0].sym.loc.r + let pat = $n[0].sym.loc.snippet internalAssert p.config, pat.len > 0 if pat.contains({'#', '(', '@'}): var typ = skipTypes(n[0].typ, abstractInst) @@ -1694,7 +1862,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = r.address = "" r.typ = etyNone r.res.add(".") - var op: TCompRes + var op: TCompRes = default(TCompRes) gen(p, n[0], op) r.res.add(op.res) genArgs(p, n, r, 2) @@ -1756,17 +1924,27 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v while t != nil: t = t.skipTypes(skipPtrs) createRecordVarAux(p, t.n, excludedFieldIDs, output) - t = t[0] + t = t.baseClass -proc arrayTypeForElemType(typ: PType): string = +proc arrayTypeForElemType(conf: ConfigRef; typ: PType): string = let typ = typ.skipTypes(abstractRange) case typ.kind of tyInt, tyInt32: "Int32Array" of tyInt16: "Int16Array" of tyInt8: "Int8Array" + of tyInt64: + if optJsBigInt64 in conf.globalOptions: + "BigInt64Array" + else: + "" of tyUInt, tyUInt32: "Uint32Array" of tyUInt16: "Uint16Array" of tyUInt8, tyChar, tyBool: "Uint8Array" + of tyUInt64: + if optJsBigInt64 in conf.globalOptions: + "BigUint64Array" + else: + "" of tyFloat32: "Float32Array" of tyFloat64, tyFloat: "Float64Array" of tyEnum: @@ -1780,15 +1958,22 @@ proc arrayTypeForElemType(typ: PType): string = proc createVar(p: PProc, typ: PType, indirect: bool): Rope = var t = skipTypes(typ, abstractInst) case t.kind - of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: - if $t.sym.loc.r == "bigint": + of tyInt8..tyInt32, tyUInt8..tyUInt32, tyEnum, tyChar: + result = putToSeq("0", indirect) + of tyInt, tyUInt: + if $t.sym.loc.snippet == "bigint": + result = putToSeq("0n", indirect) + else: + result = putToSeq("0", indirect) + of tyInt64, tyUInt64: + if optJsBigInt64 in p.config.globalOptions: result = putToSeq("0n", indirect) else: result = putToSeq("0", indirect) of tyFloat..tyFloat128: result = putToSeq("0.0", indirect) of tyRange, tyGenericInst, tyAlias, tySink, tyOwned, tyLent: - result = createVar(p, lastSon(typ), indirect) + result = createVar(p, skipModifier(typ), indirect) of tySet: result = putToSeq("{}", indirect) of tyBool: @@ -1798,7 +1983,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = of tyArray: let length = toInt(lengthOrd(p.config, t)) let e = elemType(t) - let jsTyp = arrayTypeForElemType(e) + let jsTyp = arrayTypeForElemType(p.config, e) if jsTyp.len > 0: result = "new $1($2)" % [rope(jsTyp), rope(length)] elif length > 32: @@ -1825,7 +2010,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result.add("}") if indirect: result = "[$1]" % [result] of tyObject: - var initList: Rope + var initList: Rope = "" createObjInitList(p, t, initIntSet(), initList) result = ("({$1})") % [initList] if indirect: result = "[$1]" % [result] @@ -1836,11 +2021,11 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("null", indirect) of tySequence, tyString: result = putToSeq("[]", indirect) - of tyCstring, tyProc: + of tyCstring, tyProc, tyOpenArray: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: - result = createVar(p, lastSon t, indirect) + result = createVar(p, skipModifier t, indirect) else: internalError(p.config, "createVar: " & $t.kind) result = "" @@ -1852,7 +2037,7 @@ template returnType: untyped = "" proc genVarInit(p: PProc, v: PSym, n: PNode) = var - a: TCompRes + a: TCompRes = default(TCompRes) s: Rope varCode: string varName = mangleName(p.module, v) @@ -1888,7 +2073,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = gen(p, n, a) case mapType(p, v.typ) of etyObject, etySeq: - if needsNoCopy(p, n): + if v.typ.kind in {tyOpenArray, tyVarargs} or needsNoCopy(p, n): s = a.res else: useMagic(p, "nimCopy") @@ -1898,33 +2083,44 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = if a.typ == etyBaseIndex: if targetBaseIndex: line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n", - [returnType, v.loc.r, a.address, a.res])) + [returnType, v.loc.snippet, a.address, a.res])) else: if isIndirect(v): line(p, runtimeFormat(varCode & " = [[$3, $4]];$n", - [returnType, v.loc.r, a.address, a.res])) + [returnType, v.loc.snippet, a.address, a.res])) else: line(p, runtimeFormat(varCode & " = [$3, $4];$n", - [returnType, v.loc.r, a.address, a.res])) + [returnType, v.loc.snippet, a.address, a.res])) else: if targetBaseIndex: let tmp = p.getTemp lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n", - [tmp, a.res, v.loc.r]) + [tmp, a.res, v.loc.snippet]) else: - line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res])) + line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, a.res])) return else: s = a.res if isIndirect(v): - line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s])) + line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.snippet, s])) else: - line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s])) + line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, s])) if useReloadingGuard or useGlobalPragmas: dec p.extraIndent lineF(p, "}$n") +proc genClosureVar(p: PProc, n: PNode) = + # assert n[2].kind != nkEmpty + # TODO: fixme transform `var env.x` into `var env.x = default()` after + # the order of transf and lambdalifting is fixed + if n[2].kind != nkEmpty: + genAsgnAux(p, n[0], n[2], false) + else: + var a: TCompRes = default(TCompRes) + gen(p, n[0], a) + line(p, runtimeFormat("$1 = $2;$n", [rdLoc(a), createVar(p, n[0].typ, false)])) + proc genVarStmt(p: PProc, n: PNode) = for i in 0..<n.len: var a = n[i] @@ -1934,15 +2130,17 @@ proc genVarStmt(p: PProc, n: PNode) = genStmt(p, unpacked) else: assert(a.kind == nkIdentDefs) - assert(a[0].kind == nkSym) - var v = a[0].sym - if lfNoDecl notin v.loc.flags and sfImportc notin v.flags: - genLineDir(p, a) - if sfCompileTime notin v.flags: - genVarInit(p, v, a[2]) - else: - # lazy emit, done when it's actually used. - if v.ast == nil: v.ast = a[2] + if a[0].kind == nkSym: + var v = a[0].sym + if lfNoDecl notin v.loc.flags and sfImportc notin v.flags: + genLineDir(p, a) + if sfCompileTime notin v.flags: + genVarInit(p, v, a[2]) + else: + # lazy emit, done when it's actually used. + if v.ast == nil: v.ast = a[2] + else: # closure + genClosureVar(p, a) proc genConstant(p: PProc, c: PSym) = if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id): @@ -1953,7 +2151,7 @@ proc genConstant(p: PProc, c: PSym) = p.body = oldBody proc genNew(p: PProc, n: PNode) = - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, n[1], a) var t = skipTypes(n[1].typ, abstractVar)[0] if mapType(t) == etyObject: @@ -1964,7 +2162,7 @@ proc genNew(p: PProc, n: PNode) = lineF(p, "$1 = [[$2], 0];$n", [a.rdLoc, createVar(p, t, false)]) proc genNewSeq(p: PProc, n: PNode) = - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) gen(p, n[1], x) gen(p, n[2], y) let t = skipTypes(n[1].typ, abstractVar)[0] @@ -1973,37 +2171,41 @@ proc genNewSeq(p: PProc, n: PNode) = proc genOrd(p: PProc, n: PNode, r: var TCompRes) = case skipTypes(n[1].typ, abstractVar + abstractRange).kind - of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n[1], r) + of tyEnum, tyInt..tyInt32, tyUInt..tyUInt32, tyChar: gen(p, n[1], r) + of tyInt64, tyUInt64: + if optJsBigInt64 in p.config.globalOptions: + unaryExpr(p, n, r, "", "Number($1)") + else: gen(p, n[1], r) of tyBool: unaryExpr(p, n, r, "", "($1 ? 1 : 0)") else: internalError(p.config, n.info, "genOrd") proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, n[1], a) r.kind = resExpr if skipTypes(n[1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1].concat(" % [a.res]) else: - r.res.add("($1 || []).concat(" % [a.res]) + r.res.add("($1).concat(" % [a.res]) for i in 2..<n.len - 1: gen(p, n[i], a) if skipTypes(n[i].typ, abstractVarRange).kind == tyChar: r.res.add("[$1]," % [a.res]) else: - r.res.add("$1 || []," % [a.res]) + r.res.add("$1," % [a.res]) gen(p, n[^1], a) if skipTypes(n[^1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1])" % [a.res]) else: - r.res.add("$1 || [])" % [a.res]) + r.res.add("$1)" % [a.res]) proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = "") = useMagic(p, magic) r.res.add(magic & "(") - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, n[1], a) if magic == "reprAny": @@ -2050,7 +2252,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr proc genOf(p: PProc, n: PNode, r: var TCompRes) = - var x: TCompRes + var x: TCompRes = default(TCompRes) let t = skipTypes(n[2].typ, abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc, tyOwned}) gen(p, n[1], x) @@ -2065,28 +2267,36 @@ proc genDefault(p: PProc, n: PNode; r: var TCompRes) = r.res = createVar(p, n.typ, indirect = false) r.kind = resExpr -proc genReset(p: PProc, n: PNode) = - var x: TCompRes - useMagic(p, "genericReset") +proc genWasMoved(p: PProc, n: PNode) = + # TODO: it should be done by nir + var x: TCompRes = default(TCompRes) gen(p, n[1], x) if x.typ == etyBaseIndex: lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res]) else: - let (a, tmp) = maybeMakeTempAssignable(p, n[1], x) - lineF(p, "$1 = genericReset($3, $2);$n", [a, - genTypeInfo(p, n[1].typ), tmp]) + var y: TCompRes = default(TCompRes) + genDefault(p, n[1], y) + let (a, _) = maybeMakeTempAssignable(p, n[1], x) + lineF(p, "$1 = $2;$n", [a, y.rdLoc]) proc genMove(p: PProc; n: PNode; r: var TCompRes) = - var a: TCompRes + var a: TCompRes = default(TCompRes) r.kind = resVal r.res = p.getTemp() gen(p, n[1], a) lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc]) - genReset(p, n) + genWasMoved(p, n) #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) +proc genDup(p: PProc; n: PNode; r: var TCompRes) = + var a: TCompRes = default(TCompRes) + r.kind = resVal + r.res = p.getTemp() + gen(p, n[1], a) + lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc]) + proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) = - var a: TCompRes + var a: TCompRes = default(TCompRes) r.res = rope("[") r.kind = resExpr for i in 0 ..< n.len: @@ -2117,7 +2327,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = binaryExpr(p, n, r, "addChar", "addChar($1, $2);") of mAppendStrStr: - var lhs, rhs: TCompRes + var lhs, rhs: TCompRes = default(TCompRes) gen(p, n[1], lhs) gen(p, n[2], rhs) @@ -2130,7 +2340,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp] r.kind = resExpr of mAppendSeqElem: - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) gen(p, n[1], x) gen(p, n[2], y) if mapType(n[2].typ) == etyBaseIndex: @@ -2158,7 +2368,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = if mapType(n[1].typ) != etyBaseIndex: unaryExpr(p, n, r, "", "($1 == null)") else: - var x: TCompRes + var x: TCompRes = default(TCompRes) gen(p, n[1], x) r.res = "($# == null && $# === 0)" % [x.address, x.res] of mEnumToStr: genRepr(p, n, r) @@ -2169,7 +2379,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = if n[1].kind == nkBracket: genJSArrayConstr(p, n[1], r) else: - var x: TCompRes + var x: TCompRes = default(TCompRes) gen(p, n[1], x) useMagic(p, "nimCopy") r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)] @@ -2178,7 +2388,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mDestroy, mTrace: discard "ignore calls to the default destructor" of mOrd: genOrd(p, n, r) of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray: - var x: TCompRes + var x: TCompRes = default(TCompRes) gen(p, n[1], x) if skipTypes(n[1].typ, abstractInst).kind == tyCstring: let (a, tmp) = maybeMakeTemp(p, n[1], x) @@ -2187,7 +2397,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = r.res = "($1).length" % [x.rdLoc] r.kind = resExpr of mHigh: - var x: TCompRes + var x: TCompRes = default(TCompRes) gen(p, n[1], x) if skipTypes(n[1].typ, abstractInst).kind == tyCstring: let (a, tmp) = maybeMakeTemp(p, n[1], x) @@ -2196,21 +2406,43 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = r.res = "($1).length - 1" % [x.rdLoc] r.kind = resExpr of mInc: - if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}: + let typ = n[1].typ.skipTypes(abstractVarRange) + case typ.kind + of tyUInt..tyUInt32: binaryUintExpr(p, n, r, "+", true) + of tyUInt64: + if optJsBigInt64 in p.config.globalOptions: + binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 + BigInt($2))", true) + else: binaryUintExpr(p, n, r, "+", true) + elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + if optOverflowCheck notin p.options: + binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 + BigInt($2))", true) + else: binaryExpr(p, n, r, "addInt64", "$1 = addInt64($3, BigInt($2))", true) else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2") else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)", true) of ast.mDec: - if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}: + let typ = n[1].typ.skipTypes(abstractVarRange) + case typ.kind + of tyUInt..tyUInt32: binaryUintExpr(p, n, r, "-", true) + of tyUInt64: + if optJsBigInt64 in p.config.globalOptions: + binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 - BigInt($2))", true) + else: binaryUintExpr(p, n, r, "-", true) + elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + if optOverflowCheck notin p.options: + binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 - BigInt($2))", true) + else: binaryExpr(p, n, r, "subInt64", "$1 = subInt64($3, BigInt($2))", true) else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)", true) of mSetLengthStr: - binaryExpr(p, n, r, "mnewString", "($1.length = $2)") + binaryExpr(p, n, r, "mnewString", + """if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); } + else {$3.length = $4; }""") of mSetLengthSeq: - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) gen(p, n[1], x) gen(p, n[2], y) let t = skipTypes(n[1].typ, abstractVar)[0] @@ -2234,7 +2466,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]") of mOf: genOf(p, n, r) of mDefault, mZeroDefault: genDefault(p, n, r) - of mReset, mWasMoved: genReset(p, n) + of mWasMoved: genWasMoved(p, n) of mEcho: genEcho(p, n, r) of mNLen..mNError, mSlurp, mStaticExec: localError(p.config, n.info, errXMustBeCompileTime % n[0].sym.name.s) @@ -2249,7 +2481,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = genCall(p, n, r) of mSlice: # arr.slice([begin[, end]]): 'end' is exclusive - var x, y, z: TCompRes + var x, y, z: TCompRes = default(TCompRes) gen(p, n[1], x) gen(p, n[2], y) gen(p, n[3], z) @@ -2257,13 +2489,17 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr of mMove: genMove(p, n, r) + of mDup: + genDup(p, n, r) + of mEnsureMove: + gen(p, n[1], r) else: genCall(p, n, r) #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]); proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = var - a, b: TCompRes + a, b: TCompRes = default(TCompRes) useMagic(p, "setConstr") r.res = rope("setConstr(") r.kind = resExpr @@ -2295,12 +2531,12 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = ## Nim sequence maps to JS array. var t = skipTypes(n.typ, abstractInst) let e = elemType(t) - let jsTyp = arrayTypeForElemType(e) + let jsTyp = arrayTypeForElemType(p.config, e) if skipTypes(n.typ, abstractVarRange).kind != tySequence and jsTyp.len > 0: # generate typed array # for example Nim generates `new Uint8Array([1, 2, 3])` for `[byte(1), 2, 3]` # TODO use `set` or loop to initialize typed array which improves performances in some situations - var a: TCompRes + var a: TCompRes = default(TCompRes) r.res = "new $1([" % [rope(jsTyp)] r.kind = resExpr for i in 0 ..< n.len: @@ -2312,7 +2548,7 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = genJSArrayConstr(p, n, r) proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = - var a: TCompRes + var a: TCompRes = default(TCompRes) r.res = rope("{") r.kind = resExpr for i in 0..<n.len: @@ -2331,9 +2567,9 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = r.res.add("}") proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = - var a: TCompRes + var a: TCompRes = default(TCompRes) r.kind = resExpr - var initList : Rope + var initList : Rope = "" var fieldIDs = initIntSet() let nTyp = n.typ.skipTypes(abstractInst) for i in 1..<n.len: @@ -2343,17 +2579,17 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = let val = it[1] gen(p, val, a) var f = it[0].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) + if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f) fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id) let typ = val.typ.skipTypes(abstractInst) if a.typ == etyBaseIndex: - initList.addf("$#: [$#, $#]", [f.loc.r, a.address, a.res]) + initList.addf("$#: [$#, $#]", [f.loc.snippet, a.address, a.res]) else: if not needsNoCopy(p, val): useMagic(p, "nimCopy") a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] - initList.addf("$#: $#", [f.loc.r, a.res]) + initList.addf("$#: $#", [f.loc.snippet, a.res]) let t = skipTypes(n.typ, abstractInst + skipPtrs) createObjInitList(p, t, fieldIDs, initList) r.res = ("{$1}") % [initList] @@ -2376,7 +2612,29 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) = r.res = "(!!($1))" % [r.res] r.kind = resExpr elif toInt: - r.res = "(($1) | 0)" % [r.res] + if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + r.res = "Number($1)" % [r.res] + else: + r.res = "(($1) | 0)" % [r.res] + elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + if fromInt or fromUint or src.kind in {tyBool, tyChar, tyEnum}: + r.res = "BigInt($1)" % [r.res] + elif src.kind in {tyFloat..tyFloat64}: + r.res = "BigInt(Math.trunc($1))" % [r.res] + elif src.kind == tyUInt64: + r.res = "BigInt.asIntN(64, $1)" % [r.res] + elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: + if fromUint or src.kind in {tyBool, tyChar, tyEnum}: + r.res = "BigInt($1)" % [r.res] + elif fromInt: # could be negative + r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res] + elif src.kind in {tyFloat..tyFloat64}: + r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res] + elif src.kind == tyInt64: + r.res = "BigInt.asUintN(64, $1)" % [r.res] + elif toUint or dest.kind in tyFloat..tyFloat64: + if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + r.res = "Number($1)" % [r.res] else: # TODO: What types must we handle here? discard @@ -2385,12 +2643,29 @@ proc upConv(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[0], r) # XXX proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = - var a, b: TCompRes + var a, b: TCompRes = default(TCompRes) gen(p, n[0], r) - if optRangeCheck notin p.options or (skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64} and - checkUnsignedConversions notin p.config.legacyFeatures): - discard "XXX maybe emit masking instructions here" + let src = skipTypes(n[0].typ, abstractVarRange) + let dest = skipTypes(n.typ, abstractVarRange) + if optRangeCheck notin p.options: + if optJsBigInt64 in p.config.globalOptions and + dest.kind in {tyUInt..tyUInt32, tyInt..tyInt32} and + src.kind in {tyInt64, tyUInt64}: + # conversions to Number are kept + r.res = "Number($1)" % [r.res] + else: + discard + elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures: + if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res] + else: + r.res = "BigInt.asUintN($1, BigInt($2))" % [$(dest.size * 8), r.res] + if not (dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions): + r.res = "Number($1)" % [r.res] else: + if src.kind in {tyInt64, tyUInt64} and dest.kind notin {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + # we do a range check anyway, so it's ok if the number gets rounded + r.res = "Number($1)" % [r.res] gen(p, n[1], a) gen(p, n[2], b) useMagic(p, "chckRange") @@ -2467,9 +2742,10 @@ proc optionalLine(p: Rope): Rope = proc genProc(oldProc: PProc, prc: PSym): Rope = ## Generate a JS procedure ('function'). + result = "" var resultSym: PSym - a: TCompRes + a: TCompRes = default(TCompRes) #if gVerbosity >= 3: # echo "BEGIN generating code for: " & prc.name.s var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) @@ -2477,26 +2753,27 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = var returnStmt: Rope = "" var resultAsgn: Rope = "" var name = mangleName(p.module, prc) - let header = generateHeader(p, prc.typ) - if prc.typ[0] != nil and sfPure notin prc.flags: + let header = generateHeader(p, prc) + if prc.typ.returnType != nil and sfPure notin prc.flags: resultSym = prc.ast[resultPos].sym let mname = mangleName(p.module, resultSym) - let returnAddress = not isIndirect(resultSym) and + # otherwise uses "fat pointers" + let useRawPointer = not isIndirect(resultSym) and resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and mapType(p, resultSym.typ) == etyBaseIndex - if returnAddress: + if useRawPointer: resultAsgn = p.indentLine(("var $# = null;$n") % [mname]) resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname]) else: let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar]) gen(p, prc.ast[resultPos], a) - if returnAddress: + if mapType(p, resultSym.typ) == etyBaseIndex: returnStmt = "return [$#, $#];$n" % [a.address, a.res] else: returnStmt = "return $#;$n" % [a.res] - var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, dontUseCache) + var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, {}) if sfInjectDestructors in prc.flags: transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody) @@ -2548,14 +2825,19 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = # echo "END generated code for: " & prc.name.s proc genStmt(p: PProc, n: PNode) = - var r: TCompRes + var r: TCompRes = default(TCompRes) gen(p, n, r) if r.res != "": lineF(p, "$#;$n", [r.res]) proc genPragma(p: PProc, n: PNode) = - for it in n.sons: + for i in 0..<n.len: + let it = n[i] case whichPragma(it) of wEmit: genAsmOrEmitStmt(p, it[1]) + of wPush: + processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1) + of wPop: + processPopBackendOption(p.config, p.optionsStack, p.options) else: discard proc genCast(p: PProc, n: PNode, r: var TCompRes) = @@ -2570,24 +2852,35 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = let fromInt = (src.kind in tyInt..tyInt32) let fromUint = (src.kind in tyUInt..tyUInt32) - if toUint and (fromInt or fromUint): - let trimmer = unsignedTrimmer(dest.size) - r.res = "($1 $2)" % [r.res, trimmer] + if toUint: + if fromInt or fromUint: + r.res = "Number(BigInt.asUintN($1, BigInt($2)))" % [$(dest.size * 8), r.res] + elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + r.res = "Number(BigInt.asUintN($1, $2))" % [$(dest.size * 8), r.res] elif toInt: - if fromInt: - return - elif fromUint: - if src.size == 4 and dest.size == 4: - # XXX prevent multi evaluations - r.res = "($1 | 0)" % [r.res] - else: - let trimmer = unsignedTrimmer(dest.size) - let minuend = case dest.size - of 1: "0xfe" - of 2: "0xfffe" - of 4: "0xfffffffe" - else: "" - r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer] + if fromInt or fromUint: + r.res = "Number(BigInt.asIntN($1, BigInt($2)))" % [$(dest.size * 8), r.res] + elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + r.res = "Number(BigInt.asIntN($1, $2))" % [$(dest.size * 8), r.res] + elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + if fromInt or fromUint or src.kind in {tyBool, tyChar, tyEnum}: + r.res = "BigInt($1)" % [r.res] + elif src.kind in {tyFloat..tyFloat64}: + r.res = "BigInt(Math.trunc($1))" % [r.res] + elif src.kind == tyUInt64: + r.res = "BigInt.asIntN(64, $1)" % [r.res] + elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: + if fromUint or src.kind in {tyBool, tyChar, tyEnum}: + r.res = "BigInt($1)" % [r.res] + elif fromInt: # could be negative + r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res] + elif src.kind in {tyFloat..tyFloat64}: + r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res] + elif src.kind == tyInt64: + r.res = "BigInt.asUintN(64, $1)" % [r.res] + elif dest.kind in tyFloat..tyFloat64: + if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + r.res = "Number($1)" % [r.res] elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer: r.address = r.res r.res = "null" @@ -2606,10 +2899,25 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkSym: genSym(p, n, r) of nkCharLit..nkUInt64Lit: - if n.typ.kind == tyBool: + case n.typ.skipTypes(abstractVarRange).kind + of tyBool: r.res = if n.intVal == 0: rope"false" else: rope"true" + of tyUInt64: + r.res = rope($cast[BiggestUInt](n.intVal)) + if optJsBigInt64 in p.config.globalOptions: + r.res.add('n') + of tyInt64: + let wrap = n.intVal < 0 # wrap negative integers with parens + if wrap: r.res.add '(' + r.res.addInt n.intVal + if optJsBigInt64 in p.config.globalOptions: + r.res.add('n') + if wrap: r.res.add ')' else: - r.res = rope(n.intVal) + let wrap = n.intVal < 0 # wrap negative integers with parens + if wrap: r.res.add '(' + r.res.addInt n.intVal + if wrap: r.res.add ')' r.kind = resExpr of nkNilLit: if isEmptyType(n.typ): @@ -2648,7 +2956,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = r.res = rope"Infinity" of fcNegInf: r.res = rope"-Infinity" - else: r.res = rope(f.toStrMaxPrecision) + else: + if n.typ.skipTypes(abstractVarRange).kind == tyFloat32: + r.res.addFloatRoundtrip(f.float32) + else: + r.res.addFloatRoundtrip(f) r.kind = resExpr of nkCallKinds: if isEmptyType(n.typ): @@ -2660,7 +2972,18 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = genInfixCall(p, n, r) else: genCall(p, n, r) - of nkClosure: gen(p, n[0], r) + of nkClosure: + if jsNoLambdaLifting in p.config.legacyFeatures: + gen(p, n[0], r) + else: + let tmp = getTemp(p) + var a: TCompRes = default(TCompRes) + var b: TCompRes = default(TCompRes) + gen(p, n[0], a) + gen(p, n[1], b) + lineF(p, "$1 = $2.bind($3); $1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp, a.rdLoc, b.rdLoc]) + r.res = tmp + r.kind = resVal of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) of nkPar, nkTupleConstr: genTupleConstr(p, n, r) @@ -2691,7 +3014,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkLambdaKinds: let s = n[namePos].sym discard mangleName(p.module, s) - r.res = s.loc.r + r.res = s.loc.snippet if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard elif not p.g.generatedSyms.containsOrIncl(s.id): p.locals.add(genProc(p, s)) @@ -2723,17 +3046,16 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = if n[0].kind != nkEmpty: genLineDir(p, n) gen(p, n[0], r) - r.res = "var _ = " & r.res - of nkAsmStmt: genAsmOrEmitStmt(p, n) + r.res = "(" & r.res & ")" + of nkAsmStmt: + warningDeprecated(p.config, n.info, "'asm' for the JS target is deprecated, use the 'emit' pragma") + genAsmOrEmitStmt(p, n, true) of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection, nkCommentStmt, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt, + nkFromStmt, nkTemplateDef, nkMacroDef, nkIteratorDef, nkStaticStmt, nkMixinStmt, nkBindStmt: discard - of nkIteratorDef: - if n[0].sym.typ.callConv == TCallingConvention.ccClosure: - globalError(p.config, n.info, "Closure iterators are not supported by JS backend!") of nkPragma: genPragma(p, n) of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: var s = n[namePos].sym @@ -2741,7 +3063,9 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = genSym(p, n[namePos], r) r.res = "" of nkGotoState, nkState: - globalError(p.config, n.info, "First class iterators not implemented") + globalError(p.config, n.info, "not implemented") + of nkBreakState: + genBreakState(p, n[0], r) of nkPragmaBlock: gen(p, n.lastSon, r) of nkComesFrom: discard "XXX to implement for better stack traces" @@ -2749,13 +3073,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = proc newModule(g: ModuleGraph; module: PSym): BModule = ## Create a new JS backend module node. - new(result) - result.module = module - result.sigConflicts = initCountTable[SigHash]() if g.backend == nil: g.backend = newGlobals() - result.graph = g - result.config = g.config + result = BModule(module: module, sigConflicts: initCountTable[SigHash](), + graph: g, config: g.config + ) if sfSystemModule in module.flags: PGlobals(g.backend).inSystem = true @@ -2803,7 +3125,7 @@ proc genModule(p: PProc, n: PNode) = if p.config.hcrOn and n.kind == nkStmtList: let moduleSym = p.module.module var moduleLoadedVar = rope(moduleSym.name.s) & "_loaded" & - idOrSig(moduleSym, moduleSym.name.s, p.module.sigConflicts) + idOrSig(moduleSym, moduleSym.name.s, p.module.sigConflicts, p.config) lineF(p, "var $1;$n", [moduleLoadedVar]) var inGuardedBlock = false @@ -2820,14 +3142,15 @@ proc genModule(p: PProc, n: PNode) = if optStackTrace in p.options: p.body.add(frameDestroy(p)) -proc myProcess(b: PPassContext, n: PNode): PNode = +proc processJSCodeGen*(b: PPassContext, n: PNode): PNode = ## Generate JS code for a node. result = n let m = BModule(b) - if passes.skipCodegen(m.config, n): return n + if pipelineutils.skipCodegen(m.config, n): return n if m.module == nil: internalError(m.config, n.info, "myProcess") let globals = PGlobals(m.graph.backend) var p = newInitProc(globals, m) + m.initProc = p p.unique = globals.unique genModule(p, n) p.g.code.add(p.locals) @@ -2841,25 +3164,15 @@ proc wholeCode(graph: ModuleGraph; m: BModule): Rope = var p = newInitProc(globals, m) attachProc(p, prc) - var disp = generateMethodDispatchers(graph) - for i in 0..<disp.len: - let prc = disp[i].sym + generateIfMethodDispatchers(graph, m.idgen) + for prc in getDispatchers(graph): if not globals.generatedSyms.containsOrIncl(prc.id): var p = newInitProc(globals, m) attachProc(p, prc) result = globals.typeInfo & globals.constants & globals.code -proc getClassName(t: PType): Rope = - var s = t.sym - if s.isNil or sfAnon in s.flags: - s = skipTypes(t, abstractPtrs).sym - if s.isNil or sfAnon in s.flags: - doAssert(false, "cannot retrieve class name") - if s.loc.r != "": result = s.loc.r - else: result = rope(s.name.s) - -proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = +proc finalJSCodeGen*(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = ## Finalize JS code generation of a Nim module. ## Param `n` may contain nodes returned from the last module close call. var m = BModule(b) @@ -2869,14 +3182,14 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = 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) + result = processJSCodeGen(b, n) # Some codegen is different (such as no stacktraces; see `initProcOptions`) # when `std/system` is being processed. if sfSystemModule in m.module.flags: 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 pipelineutils.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: @@ -2885,7 +3198,8 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = # Generate an optional source map. if optSourcemap in m.config.globalOptions: var map: SourceMap - (code, map) = genSourceMap($(code), outFile.string) + map = genSourceMap($code, outFile.string) + code &= "\n//# sourceMappingURL=$#.map" % [outFile.string] writeFile(outFile.string & ".map", $(%map)) # Check if the generated JS code matches the output file, or else # write it to the file. @@ -2893,9 +3207,6 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = if not writeRope(code, outFile): rawMessage(m.config, errCannotOpenFile, outFile.string) -proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = - ## Create the JS backend pass context `BModule` for a Nim module. +proc setupJSgen*(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = result = newModule(graph, s) result.idgen = idgen - -const JSgenPass* = makePass(myOpen, myProcess, myClose) |