# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This is the JavaScript code generator. # Soon also a Luajit code generator. ;-) discard """ The JS code generator contains only 2 tricks: Trick 1 ------- Some locations (for example 'var int') require "fat pointers" (``etyBaseIndex``) which are pairs (array, index). The derefence operation is then 'array[index]'. Check ``mapType`` for the details. Trick 2 ------- It is preferable to generate '||' and '&&' if possible since that is more idiomatic and hence should be friendlier for the JS JIT implementation. However code like ``foo and (let bar = baz())`` cannot be translated this way. Instead the expressions need to be transformed into statements. ``isSimpleExpr`` implements the required case distinction. """ import ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os, times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, intsets, cgmeth, lowerings type TTarget = enum targetJS, targetLua TJSGen = object of TPassContext module: PSym BModule = ref TJSGen TJSTypeKind = enum # necessary JS "types" etyNone, # no type etyNull, # null type etyProc, # proc type etyBool, # bool type etyInt, # JavaScript's int etyFloat, # JavaScript's float etyString, # JavaScript's string etyObject, # JavaScript's reference to an object etyBaseIndex # base + index needed TResKind = enum resNone, # not set resExpr, # is some complex expression resVal # is a temporary/value/l-value TCompRes = object kind: TResKind typ: TJSTypeKind res: Rope # result part; index if this is an # (address, index)-tuple address: Rope # address of an (address, index)-tuple TBlock = object id: int # the ID of the label; positive means that it # has been used (i.e. the label should be emitted) isLoop: bool # whether it's a 'block' or 'while' TGlobals = object typeInfo, code: Rope forwarded: seq[PSym] generatedSyms: IntSet typeInfoGenerated: IntSet PGlobals = ref TGlobals PProc = ref TProc TProc = object procDef: PNode prc: PSym locals, body: Rope options: TOptions module: BModule g: PGlobals beforeRetNeeded: bool target: TTarget # duplicated here for faster dispatching unique: int # for temp identifier generation blocks: seq[TBlock] up: PProc # up the call chain; required for closure support template `|`(a, b: expr): expr {.immediate, dirty.} = (if p.target == targetJS: a else: b) proc newGlobals(): PGlobals = new(result) result.forwarded = @[] result.generatedSyms = initIntSet() result.typeInfoGenerated = initIntSet() proc initCompRes(r: var TCompRes) = r.address = nil r.res = nil r.typ = etyNone r.kind = resNone proc rdLoc(a: TCompRes): Rope {.inline.} = result = a.res when false: if a.typ != etyBaseIndex: result = a.res else: result = "$1[$2]" % [a.address, a.res] proc newProc(globals: PGlobals, module: BModule, procDef: PNode, options: TOptions): PProc = result = PProc( blocks: @[], options: options, module: module, procDef: procDef, g: globals) if procDef != nil: result.prc = procDef.sons[namePos].sym const MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyBigNum, tyVarargs} 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: result = etyObject else: result = etyBaseIndex of tyPointer: # treat a tyPointer like a typed pointer to an array of bytes result = etyInt of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy: result = mapType(t.sons[0]) of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt of tyBool: result = etyBool of tyFloat..tyFloat128: result = etyFloat of tySet: result = etyObject # map a set to a table of tyString, tySequence: result = etyInt # little hack to get right semantics of tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tyBigNum, tyVarargs: result = etyObject of tyNil: result = etyNull of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation, tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor, tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString proc mangleName(s: PSym): Rope = result = s.loc.r if result == nil: result = rope(mangle(s.name.s)) add(result, "_") add(result, rope(s.id)) s.loc.r = result proc makeJSString(s: string): Rope = (if s.isNil: "null".rope else: strutils.escape(s).rope) include jstypes proc gen(p: PProc, n: PNode, r: var TCompRes) proc genStmt(p: PProc, n: PNode) proc genProc(oldProc: PProc, prc: PSym): Rope proc genConstant(p: PProc, c: PSym) proc useMagic(p: PProc, name: string) = if name.len == 0: return var s = magicsys.getCompilerProc(name) if s != nil: internalAssert s.kind in {skProc, skMethod, skConverter} if not p.g.generatedSyms.containsOrIncl(s.id): add(p.g.code, genProc(p, s)) else: # we used to exclude the system module from this check, but for DLL # generation support this sloppyness leads to hard to detect bugs, so # we're picky here for the system module too: if p.prc != nil: globalError(p.prc.info, errSystemNeeds, name) else: rawMessage(errSystemNeeds, name) proc isSimpleExpr(n: PNode): bool = # calls all the way down --> can stay expression based if n.kind in nkCallKinds+{nkBracketExpr, nkBracket, nkCurly, nkDotExpr, nkPar, nkObjConstr}: for c in n: if not c.isSimpleExpr: return false result = true elif n.isAtom: result = true proc getTemp(p: PProc): Rope = inc(p.unique) result = "Tmp$1" % [rope(p.unique)] addf(p.locals, "var $1;$n" | "local $1;$n", [result]) proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone var x, y: TCompRes if a.isSimpleExpr and b.isSimpleExpr: gen(p, a, x) gen(p, b, y) r.kind = resExpr r.res = ("($1 && $2)" | "($1 and $2)") % [x.rdLoc, y.rdLoc] else: r.res = p.getTemp r.kind = resVal # while a and b: # --> # while true: # aa # if not a: tmp = false # else: # bb # tmp = b # tmp gen(p, a, x) p.body.addf("if (!$1) $2 = false; else {" | "if not $1 then $2 = false; else", [x.rdLoc, r.rdLoc]) gen(p, b, y) p.body.addf("$2 = $1; }" | "$2 = $1 end", [y.rdLoc, r.rdLoc]) proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone var x, y: TCompRes if a.isSimpleExpr and b.isSimpleExpr: gen(p, a, x) gen(p, b, y) r.kind = resExpr r.res = ("($1 || $2)" | "($1 or $2)") % [x.rdLoc, y.rdLoc] else: r.res = p.getTemp r.kind = resVal gen(p, a, x) p.body.addf("if ($1) $2 = true; else {" | "if $1 then $2 = true; else", [x.rdLoc, r.rdLoc]) gen(p, b, y) p.body.addf("$2 = $1; }" | "$2 = $1 end", [y.rdLoc, r.rdLoc]) type TMagicFrmt = array[0..3, string] TMagicOps = array[mAddI..mStrToStr, TMagicFrmt] const # magic checked op; magic unchecked op; checked op; unchecked op jsOps: TMagicOps = [ ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI ["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64 ["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64 ["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64 ["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64 ["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64 ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred ["", "", "($1 + $2)", "($1 + $2)"], # AddF64 ["", "", "($1 - $2)", "($1 - $2)"], # SubF64 ["", "", "($1 * $2)", "($1 * $2)"], # MulF64 ["", "", "($1 / $2)", "($1 / $2)"], # DivF64 ["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI ["", "", "($1 << $2)", "($1 << $2)"], # ShlI ["", "", "($1 & $2)", "($1 & $2)"], # BitandI ["", "", "($1 | $2)", "($1 | $2)"], # BitorI ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI ["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64 ["", "", "($1 << $2)", "($1 << $2)"], # ShlI64 ["", "", "($1 & $2)", "($1 & $2)"], # BitandI64 ["", "", "($1 | $2)", "($1 | $2)"], # BitorI64 ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU ["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU ["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU ["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU ["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU ["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 < $2)", "($1 < $2)"], # LtI ["", "", "($1 == $2)", "($1 == $2)"], # EqI64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64 ["", "", "($1 < $2)", "($1 < $2)"], # LtI64 ["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64 ["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU ["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU ["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64 ["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64 ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum ["", "", "($1 == $2)", "($1 == $2)"], # EqCh ["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh ["", "", "($1 < $2)", "($1 < $2)"], # LtCh ["", "", "($1 == $2)", "($1 == $2)"], # EqB ["", "", "($1 <= $2)", "($1 <= $2)"], # LeB ["", "", "($1 < $2)", "($1 < $2)"], # LtB ["", "", "($1 == $2)", "($1 == $2)"], # EqRef ["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef ["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr ["", "", "($1 < $2)", "($1 < $2)"], # LtPtr ["", "", "($1 == $2)", "($1 == $2)"], # EqCString ["", "", "($1 != $2)", "($1 != $2)"], # Xor ["", "", "($1 == $2)", "($1 == $2)"], # EqProc ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64 ["", "", "!($1)", "!($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI ["", "", "~($1)", "~($1)"], # BitnotI64 ["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64 ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64 ["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI ["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64 ["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64 ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64 ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64 ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8 ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16 ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32 ["", "", "$1", "$1"], # ToFloat ["", "", "$1", "$1"], # ToBiggestFloat ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt ["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], [ "cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], ["", "", "$1", "$1"]] luaOps: TMagicOps = [ ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI ["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI ["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI ["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64 ["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64 ["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64 ["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64 ["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64 ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred ["", "", "($1 + $2)", "($1 + $2)"], # AddF64 ["", "", "($1 - $2)", "($1 - $2)"], # SubF64 ["", "", "($1 * $2)", "($1 * $2)"], # MulF64 ["", "", "($1 / $2)", "($1 / $2)"], # DivF64 ["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI ["", "", "($1 << $2)", "($1 << $2)"], # ShlI ["", "", "($1 & $2)", "($1 & $2)"], # BitandI ["", "", "($1 | $2)", "($1 | $2)"], # BitorI ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI ["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64 ["", "", "($1 << $2)", "($1 << $2)"], # ShlI64 ["", "", "($1 & $2)", "($1 & $2)"], # BitandI64 ["", "", "($1 | $2)", "($1 | $2)"], # BitorI64 ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU ["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU ["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU ["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU ["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU ["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 < $2)", "($1 < $2)"], # LtI ["", "", "($1 == $2)", "($1 == $2)"], # EqI64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64 ["", "", "($1 < $2)", "($1 < $2)"], # LtI64 ["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64 ["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU ["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU ["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64 ["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64 ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum ["", "", "($1 == $2)", "($1 == $2)"], # EqCh ["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh ["", "", "($1 < $2)", "($1 < $2)"], # LtCh ["", "", "($1 == $2)", "($1 == $2)"], # EqB ["", "", "($1 <= $2)", "($1 <= $2)"], # LeB ["", "", "($1 < $2)", "($1 < $2)"], # LtB ["", "", "($1 == $2)", "($1 == $2)"], # EqRef ["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef ["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr ["", "", "($1 < $2)", "($1 < $2)"], # LtPtr ["", "", "($1 == $2)", "($1 == $2)"], # EqCString ["", "", "($1 != $2)", "($1 != $2)"], # Xor ["", "", "($1 == $2)", "($1 == $2)"], # EqProc ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64 ["", "", "not ($1)", "not ($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI ["", "", "~($1)", "~($1)"], # BitnotI64 ["", "", "+($1)", "+($1)"], # UnaryPlusF64 ["", "", "-($1)", "-($1)"], # UnaryMinusF64 ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64 ["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI ["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64 ["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64 ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64 ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64 ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8 ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16 ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32 ["", "", "$1", "$1"], # ToFloat ["", "", "$1", "$1"], # ToBiggestFloat ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt ["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], [ "cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], ["", "", "$1", "$1"]] proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = var x, y: TCompRes useMagic(p, magic) gen(p, n.sons[1], x) gen(p, n.sons[2], y) r.res = frmt % [x.rdLoc, y.rdLoc] r.kind = resExpr proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = var x, y, z: TCompRes useMagic(p, magic) gen(p, n.sons[1], x) gen(p, n.sons[2], y) gen(p, n.sons[3], z) r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc] r.kind = resExpr proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = useMagic(p, magic) gen(p, n.sons[1], r) r.res = frmt % [r.rdLoc] r.kind = resExpr proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) = var x, y: TCompRes let i = ord(optOverflowCheck notin p.options) useMagic(p, ops[op][i]) if sonsLen(n) > 2: gen(p, n.sons[1], x) gen(p, n.sons[2], y) r.res = ops[op][i + 2] % [x.rdLoc, y.rdLoc] else: gen(p, n.sons[1], r) r.res = ops[op][i + 2] % [r.rdLoc] r.kind = resExpr proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = arithAux(p, n, r, op, jsOps | luaOps) proc genLineDir(p: PProc, n: PNode) = let line = toLinenumber(n.info) if optLineDir in p.options: addf(p.body, "// line $2 \"$1\"$n" | "-- line $2 \"$1\"$n", [rope(toFilename(n.info)), rope(line)]) if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and ((p.prc == nil) or sfPure notin p.prc.flags): useMagic(p, "endb") addf(p.body, "endb($1);$n", [rope(line)]) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and ((p.prc == nil) or not (sfPure in p.prc.flags)): addf(p.body, "F.line = $1;$n", [rope(line)]) proc genWhileStmt(p: PProc, n: PNode) = var cond: TCompRes internalAssert isEmptyType(n.typ) genLineDir(p, n) inc(p.unique) var length = len(p.blocks) setLen(p.blocks, length + 1) p.blocks[length].id = -p.unique p.blocks[length].isLoop = true let labl = p.unique.rope addf(p.body, "L$1: while (true) {$n" | "while true do$n", [labl]) gen(p, n.sons[0], cond) addf(p.body, "if (!$1) break L$2;$n" | "if not $1 then goto ::L$2:: end;$n", [cond.res, labl]) genStmt(p, n.sons[1]) addf(p.body, "}$n" | "end ::L$#::$n", [labl]) setLen(p.blocks, length) proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = if src.kind != resNone: if dest.kind != resNone: p.body.addf("$1 = $2;$n", [dest.rdLoc, src.rdLoc]) else: p.body.addf("$1;$n", [src.rdLoc]) src.kind = resNone src.res = nil proc genTry(p: PProc, n: PNode, r: var TCompRes) = # code to generate: # # var sp = {prev: excHandler, exc: null}; # excHandler = sp; # try { # stmts; # TMP = e # } catch (e) { # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { # stmts; # } else if (e.typ && e.typ == NTI32342) { # stmts; # } else { # stmts; # } # } finally { # stmts; # excHandler = excHandler.prev; # } genLineDir(p, n) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) inc(p.unique) var safePoint = "Tmp$1" % [rope(p.unique)] addf(p.body, "var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" | "local $1 = pcall(", [safePoint]) if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl) addf(p.body, "try {$n" | "function()$n", []) var length = sonsLen(n) var a: TCompRes gen(p, n.sons[0], a) moveInto(p, a, r) var i = 1 if p.target == targetJS and length > 1 and n.sons[i].kind == nkExceptBranch: addf(p.body, "} catch (EXC) {$n lastJSError = EXC;$n", []) elif p.target == targetLua: addf(p.body, "end)$n", []) while i < length and n.sons[i].kind == nkExceptBranch: let blen = sonsLen(n.sons[i]) if blen == 1: # general except section: if i > 1: addf(p.body, "else {$n" | "else$n", []) gen(p, n.sons[i].sons[0], a) moveInto(p, a, r) if i > 1: addf(p.body, "}$n" | "end$n", []) else: var orExpr: Rope = nil useMagic(p, "isObj") for j in countup(0, blen - 2): if n.sons[i].sons[j].kind != nkType: internalError(n.info, "genTryStmt") if orExpr != nil: add(orExpr, "||" | " or ") addf(orExpr, "isObj($1.exc.m_type, $2)", [safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)]) if i > 1: add(p.body, "else ") addf(p.body, "if ($1.exc && ($2)) {$n" | "if $1.exc and ($2) then$n", [safePoint, orExpr]) gen(p, n.sons[i].sons[blen - 1], a) moveInto(p, a, r) addf(p.body, "}$n" | "end$n", []) inc(i) if p.target == targetJS: add(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) if i < length and n.sons[i].kind == nkFinally: genStmt(p, n.sons[i].sons[0]) if p.target == targetJS: add(p.body, "}" & tnl) if p.target == targetLua: # we need to repeat the finally block for Lua ... if i < length and n.sons[i].kind == nkFinally: genStmt(p, n.sons[i].sons[0]) proc genRaiseStmt(p: PProc, n: PNode) = genLineDir(p, n) if n.sons[0].kind != nkEmpty: var a: TCompRes gen(p, n.sons[0], a) let typ = skipTypes(n.sons[0].typ, abstractPtrs) useMagic(p, "raiseException") addf(p.body, "raiseException($1, $2);$n", [a.rdLoc, makeJSString(typ.sym.name.s)]) else: useMagic(p, "reraiseException") add(p.body, "reraiseException();" & tnl) proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes genLineDir(p, n) gen(p, n.sons[0], cond) let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString if stringSwitch: useMagic(p, "toJSStr") addf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc]) else: addf(p.body, "switch ($1) {$n", [cond.rdLoc]) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) for i in countup(1, sonsLen(n) - 1): let it = n.sons[i] case it.kind of nkOfBranch: for j in countup(0, sonsLen(it) - 2): let e = it.sons[j] if e.kind == nkRange: var v = copyNode(e.sons[0]) while v.intVal <= e.sons[1].intVal: gen(p, v, cond) addf(p.body, "case $1: ", [cond.rdLoc]) inc(v.intVal) else: if stringSwitch: case e.kind of nkStrLit..nkTripleStrLit: addf(p.body, "case $1: ", [makeJSString(e.strVal)]) else: internalError(e.info, "jsgen.genCaseStmt: 2") else: gen(p, e, cond) addf(p.body, "case $1: ", [cond.rdLoc]) gen(p, lastSon(it), stmt) moveInto(p, stmt, r) addf(p.body, "$nbreak;$n", []) of nkElse: addf(p.body, "default: $n", []) gen(p, it.sons[0], stmt) moveInto(p, stmt, r) addf(p.body, "break;$n", []) else: internalError(it.info, "jsgen.genCaseStmt") addf(p.body, "}$n", []) proc genCaseLua(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes genLineDir(p, n) gen(p, n.sons[0], cond) let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString if stringSwitch: useMagic(p, "eqStr") let tmp = getTemp(p) addf(p.body, "$1 = $2;$n", [tmp, cond.rdLoc]) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) for i in countup(1, sonsLen(n) - 1): let it = n.sons[i] case it.kind of nkOfBranch: if i != 1: addf(p.body, "$nelsif ", []) else: addf(p.body, "if ", []) for j in countup(0, sonsLen(it) - 2): if j != 0: add(p.body, " or ") let e = it.sons[j] if e.kind == nkRange: var ia, ib: TCompRes gen(p, e.sons[0], ia) gen(p, e.sons[1], ib) addf(p.body, "$1 >= $2 and $1 <= $3", [tmp, ia.rdLoc, ib.rdLoc]) else: if stringSwitch: case e.kind of nkStrLit..nkTripleStrLit: addf(p.body, "eqStr($1, $2)", [tmp, makeJSString(e.strVal)]) else: internalError(e.info, "jsgen.genCaseStmt: 2") else: gen(p, e, cond) addf(p.body, "$1 == $2", [tmp, cond.rdLoc]) addf(p.body, " then$n", []) gen(p, lastSon(it), stmt) moveInto(p, stmt, r) of nkElse: addf(p.body, "else$n", []) gen(p, it.sons[0], stmt) moveInto(p, stmt, r) else: internalError(it.info, "jsgen.genCaseStmt") addf(p.body, "$nend$n", []) proc genBlock(p: PProc, n: PNode, r: var TCompRes) = inc(p.unique) let idx = len(p.blocks) if n.sons[0].kind != nkEmpty: # named block? if (n.sons[0].kind != nkSym): internalError(n.info, "genBlock") var sym = n.sons[0].sym sym.loc.k = locOther sym.position = idx+1 setLen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet let labl = p.unique addf(p.body, "L$1: do {$n" | "", [labl.rope]) gen(p, n.sons[1], r) addf(p.body, "} while(false);$n" | "$n::L$#::$n", [labl.rope]) setLen(p.blocks, idx) proc genBreakStmt(p: PProc, n: PNode) = var idx: int genLineDir(p, n) if n.sons[0].kind != nkEmpty: # named break? assert(n.sons[0].kind == nkSym) let sym = n.sons[0].sym assert(sym.loc.k == locOther) idx = sym.position-1 else: # an unnamed 'break' can only break a loop after 'transf' pass: idx = len(p.blocks) - 1 while idx >= 0 and not p.blocks[idx].isLoop: dec idx if idx < 0 or not p.blocks[idx].isLoop: internalError(n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used addf(p.body, "break L$1;$n" | "goto ::L$1::;$n", [rope(p.blocks[idx].id)]) proc genAsmStmt(p: PProc, n: PNode) = genLineDir(p, n) assert(n.kind == nkAsmStmt) for i in countup(0, sonsLen(n) - 1): case n.sons[i].kind of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal) of nkSym: add(p.body, mangleName(n.sons[i].sym)) else: internalError(n.sons[i].info, "jsgen: genAsmStmt()") proc genIf(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes var toClose = 0 if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] if sonsLen(it) != 1: if i > 0: addf(p.body, "else {$n" | "else$n", []) inc(toClose) gen(p, it.sons[0], cond) addf(p.body, "if ($1) {$n" | "if $# then$n", [cond.rdLoc]) gen(p, it.sons[1], stmt) else: # else part: addf(p.body, "else {$n" | "else$n", []) gen(p, it.sons[0], stmt) moveInto(p, stmt, r) addf(p.body, "}$n" | "end$n", []) if p.target == targetJS: add(p.body, repeat('}', toClose) & tnl) else: for i in 1..toClose: addf(p.body, "end$n", []) proc generateHeader(p: PProc, typ: PType): Rope = result = nil for i in countup(1, sonsLen(typ.n) - 1): assert(typ.n.sons[i].kind == nkSym) var param = typ.n.sons[i].sym if isCompileTimeOnly(param.typ): continue if result != nil: add(result, ", ") var name = mangleName(param) add(result, name) if mapType(param.typ) == etyBaseIndex: add(result, ", ") add(result, name) add(result, "_Idx") const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString, nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} proc needsNoCopy(y: PNode): bool = result = (y.kind in nodeKindsNeedNoCopy) or (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyVar}) proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes gen(p, x, a) gen(p, y, b) case mapType(x.typ) of etyObject: if needsNoCopy(y) or noCopyNeeded: addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") addf(p.body, "$1 = nimCopy($2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(x.info, "genAsgn") addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: addf(p.body, "$1 = $2;$n", [a.res, b.res]) proc genAsgn(p: PProc, n: PNode) = genLineDir(p, n) genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false) proc genFastAsgn(p: PProc, n: PNode) = genLineDir(p, n) genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=true) proc genSwap(p: PProc, n: PNode) = var a, b: TCompRes gen(p, n.sons[1], a) gen(p, n.sons[2], b) inc(p.unique) var tmp = "Tmp$1" % [rope(p.unique)] if mapType(skipTypes(n.sons[1].typ, abstractVar)) == etyBaseIndex: inc(p.unique) let tmp2 = "Tmp$1" % [rope(p.unique)] if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(n.info, "genSwap") addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" | "local $1 = $2; $2 = $3; $3 = $1;$n", [ tmp, a.address, b.address]) tmp = tmp2 addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" | "local $1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res]) proc getFieldPosition(f: PNode): int = case f.kind of nkIntLit..nkUInt64Lit: result = int(f.intVal) of nkSym: result = f.sym.position else: internalError(f.info, "genFieldPosition") proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.typ = etyBaseIndex let b = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, b.sons[0], a) if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple: r.res = makeJSString("Field" & $getFieldPosition(b.sons[1])) 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) r.res = makeJSString($f.loc.r) internalAssert a.typ != etyBaseIndex r.address = a.res r.kind = resExpr proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone gen(p, n.sons[0], r) if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple: r.res = "$1.Field$2" % [r.res, getFieldPosition(n.sons[1]).rope] else: if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAddr") var f = n.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(f) r.res = "$1.$2" % [r.res, f.loc.r] r.kind = resExpr proc genCheckedFieldAddr(p: PProc, n: PNode, r: var TCompRes) = let m = if n.kind == nkHiddenAddr: n.sons[0] else: n internalAssert m.kind == nkCheckedFieldExpr genFieldAddr(p, m.sons[0], r) # XXX proc genCheckedFieldAccess(p: PProc, n: PNode, r: var TCompRes) = genFieldAccess(p, n.sons[0], r) # XXX proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = var a, b: TCompRes first: BiggestInt r.typ = etyBaseIndex let m = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, m.sons[0], a) gen(p, m.sons[1], b) internalAssert a.typ != etyBaseIndex and b.typ != etyBaseIndex r.address = a.res var typ = skipTypes(m.sons[0].typ, abstractPtrs) if typ.kind in {tyArray, tyArrayConstr}: first = firstOrd(typ.sons[0]) else: first = 0 if optBoundsCheck in p.options and not isConstExpr(m.sons[1]): useMagic(p, "chckIndx") r.res = "chckIndx($1, $2, $3.length)-$2" % [b.res, rope(first), a.res] elif first != 0: r.res = "($1)-$2" % [b.res, rope(first)] else: r.res = b.res r.kind = resExpr proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: genArrayAddr(p, n, r) of tyTuple: genFieldAddr(p, n, r) else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') r.typ = etyNone if r.res == nil: internalError(n.info, "genArrayAccess") r.res = "$1[$2]" % [r.address, r.res] r.address = nil r.kind = resExpr proc isIndirect(v: PSym): bool = result = {sfAddrTaken, sfGlobal} * v.flags != {} and #(mapType(v.typ) != etyObject) and {sfImportc, sfVolatile, sfExportc} * v.flags == {} and v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator, skConst, skTemp, skLet} proc genAddr(p: PProc, n: PNode, r: var TCompRes) = case n.sons[0].kind of nkSym: let s = n.sons[0].sym if s.loc.r == nil: internalError(n.info, "genAddr: 3") case s.kind of skVar, skLet, skResult: r.kind = resExpr let jsType = mapType(n.typ) if jsType == etyObject: # make addr() a no-op: r.typ = etyNone if isIndirect(s): r.res = s.loc.r & "[0]" else: r.res = s.loc.r r.address = nil 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.res = rope("0") else: # 'var openArray' for instance produces an 'addr' but this is harmless: gen(p, n.sons[0], r) #internalError(n.info, "genAddr: 4 " & renderTree(n)) else: internalError(n.info, "genAddr: 2") of nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r) of nkDotExpr: genFieldAddr(p, n.sons[0], r) of nkBracketExpr: var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString, tyVarargs, tyChar: genArrayAddr(p, n.sons[0], r) of tyTuple: genFieldAddr(p, n.sons[0], r) else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $ty.kind & ')') else: internalError(n.sons[0].info, "genAddr") proc genSym(p: PProc, n: PNode, r: var TCompRes) = var s = n.sym case s.kind of skVar, skLet, skParam, skTemp, skResult: if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) let k = mapType(s.typ) if k == etyBaseIndex: r.typ = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: r.address = "$1[0]" % [s.loc.r] r.res = "$1[1]" % [s.loc.r] else: r.address = s.loc.r r.res = s.loc.r & "_Idx" elif isIndirect(s): r.res = "$1[0]" % [s.loc.r] else: r.res = s.loc.r of skConst: genConstant(p, s) if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r of skProc, skConverter, skMethod: discard mangleName(s) r.res = s.loc.r if lfNoDecl in s.loc.flags or s.magic != mNone or {sfImportc, sfInfixCall} * s.flags != {}: discard elif s.kind == skMethod and s.getBody.kind == nkEmpty: # we cannot produce code for the dispatcher yet: discard elif sfForward in s.flags: p.g.forwarded.add(s) elif 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: add(owner.locals, newp) else: add(p.g.code, newp) else: if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r r.kind = resVal proc genDeref(p: PProc, n: PNode, r: var TCompRes) = if mapType(n.sons[0].typ) == etyObject: gen(p, n.sons[0], r) else: var a: TCompRes gen(p, n.sons[0], a) if a.typ != etyBaseIndex: internalError(n.info, "genDeref") r.res = "$1[$2]" % [a.address, a.res] proc genArg(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n, a) if a.typ == etyBaseIndex: add(r.res, a.address) add(r.res, ", ") add(r.res, a.res) else: add(r.res, a.res) proc genArgs(p: PProc, n: PNode, r: var TCompRes) = add(r.res, "(") for i in countup(1, sonsLen(n) - 1): let it = n.sons[i] if it.typ.isCompileTimeOnly: continue if i > 1: add(r.res, ", ") genArg(p, it, r) add(r.res, ")") r.kind = resExpr proc genCall(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) genArgs(p, n, r) proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[1], r) if r.typ == etyBaseIndex: if r.address == nil: globalError(n.info, "cannot invoke with infix syntax") r.res = "$1[$2]" % [r.address, r.res] r.address = nil r.typ = etyNone add(r.res, ".") var op: TCompRes gen(p, n.sons[0], op) add(r.res, op.res) add(r.res, "(") for i in countup(2, sonsLen(n) - 1): if i > 2: add(r.res, ", ") genArg(p, n.sons[i], r) add(r.res, ")") r.kind = resExpr proc genEcho(p: PProc, n: PNode, r: var TCompRes) = useMagic(p, "rawEcho") add(r.res, "rawEcho(") let n = n[1].skipConv internalAssert n.kind == nkBracket for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] if it.typ.isCompileTimeOnly: continue if i > 0: add(r.res, ", ") genArg(p, it, r) add(r.res, ")") r.kind = resExpr proc putToSeq(s: string, indirect: bool): Rope = result = rope(s) if indirect: result = "[$1]" % [result] proc createVar(p: PProc, typ: PType, indirect: bool): Rope proc createRecordVarAux(p: PProc, rec: PNode, c: var int): Rope = result = nil case rec.kind of nkRecList: for i in countup(0, sonsLen(rec) - 1): add(result, createRecordVarAux(p, rec.sons[i], c)) of nkRecCase: add(result, createRecordVarAux(p, rec.sons[0], c)) for i in countup(1, sonsLen(rec) - 1): add(result, createRecordVarAux(p, lastSon(rec.sons[i]), c)) of nkSym: if c > 0: add(result, ", ") add(result, mangleName(rec.sym)) add(result, ": ") add(result, createVar(p, rec.sym.typ, false)) inc(c) else: internalError(rec.info, "createRecordVarAux") proc createVar(p: PProc, typ: PType, indirect: bool): Rope = var t = skipTypes(typ, abstractInst) case t.kind of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = putToSeq("0", indirect) of tyFloat..tyFloat128: result = putToSeq("0.0", indirect) of tyRange, tyGenericInst: result = createVar(p, lastSon(typ), indirect) of tySet: result = putToSeq("{}", indirect) of tyBool: result = putToSeq("false", indirect) of tyArray, tyArrayConstr: var length = int(lengthOrd(t)) var e = elemType(t) if length > 32: useMagic(p, "arrayConstr") # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary. useMagic(p, "nimCopy") result = "arrayConstr($1, $2, $3)" % [rope(length), createVar(p, e, false), genTypeInfo(p, e)] else: result = rope("[") var i = 0 while i < length: if i > 0: add(result, ", ") add(result, createVar(p, e, false)) inc(i) add(result, "]") if indirect: result = "[$1]" % [result] of tyTuple: result = rope("{") for i in 0.. 0: add(result, ", ") addf(result, "Field$1: $2" | "Field$# = $#", [i.rope, createVar(p, t.sons[i], false)]) add(result, "}") if indirect: result = "[$1]" % [result] of tyObject: result = rope("{") var c = 0 if tfFinal notin t.flags or t.sons[0] != nil: inc(c) addf(result, "m_type: $1" | "m_type = $#", [genTypeInfo(p, t)]) while t != nil: add(result, createRecordVarAux(p, t.n, c)) t = t.sons[0] add(result, "}") if indirect: result = "[$1]" % [result] of tyVar, tyPtr, tyRef: if mapType(t) == etyBaseIndex: result = putToSeq("[null, 0]" | "{nil, 0}", indirect) else: result = putToSeq("null" | "nil", indirect) of tySequence, tyString, tyCString, tyPointer, tyProc: result = putToSeq("null" | "nil", indirect) else: internalError("createVar: " & $t.kind) result = nil proc genVarInit(p: PProc, v: PSym, n: PNode) = var a: TCompRes s: Rope if n.kind == nkEmpty: addf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [mangleName(v), createVar(p, v.typ, isIndirect(v))]) else: discard mangleName(v) gen(p, n, a) case mapType(v.typ) of etyObject: if needsNoCopy(n): s = a.res else: useMagic(p, "nimCopy") s = "nimCopy($1, $2)" % [a.res, genTypeInfo(p, n.typ)] of etyBaseIndex: if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit") if {sfAddrTaken, sfGlobal} * v.flags != {}: addf(p.body, "var $1 = [$2, $3];$n" | "local $1 = {$2, $3};$n", [v.loc.r, a.address, a.res]) else: addf(p.body, "var $1 = $2; var $1_Idx = $3;$n" | "local $1 = $2; local $1_Idx = $3;$n", [ v.loc.r, a.address, a.res]) return else: s = a.res if isIndirect(v): addf(p.body, "var $1 = /**/[$2];$n" | "local $1 = {$2};$n", [v.loc.r, s]) else: addf(p.body, "var $1 = $2;$n" | "local $1 = $2;$n", [v.loc.r, s]) proc genVarStmt(p: PProc, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind != nkCommentStmt: if a.kind == nkVarTuple: let unpacked = lowerTupleUnpacking(a, p.prc) genStmt(p, unpacked) else: assert(a.kind == nkIdentDefs) assert(a.sons[0].kind == nkSym) var v = a.sons[0].sym if lfNoDecl notin v.loc.flags: genLineDir(p, a) genVarInit(p, v, a.sons[2]) proc genConstant(p: PProc, c: PSym) = if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id): let oldBody = p.body p.body = nil #genLineDir(p, c.ast) genVarInit(p, c, c.ast) add(p.g.code, p.body) p.body = oldBody proc genNew(p: PProc, n: PNode) = var a: TCompRes gen(p, n.sons[1], a) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] addf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, false)]) proc genNewSeq(p: PProc, n: PNode) = var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] addf(p.body, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ x.rdLoc, y.rdLoc, createVar(p, t, false)]) proc genOrd(p: PProc, n: PNode, r: var TCompRes) = case skipTypes(n.sons[1].typ, abstractVar).kind of tyEnum, tyInt..tyInt64, tyChar: gen(p, n.sons[1], r) of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)" | "toBool($#)") else: internalError(n.info, "genOrd") proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n.sons[1], a) r.kind = resExpr if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1].concat(" % [a.res]) else: r.res.add("($1.slice(0,-1)).concat(" % [a.res]) for i in countup(2, sonsLen(n) - 2): gen(p, n.sons[i], a) if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar: r.res.add("[$1]," % [a.res]) else: r.res.add("$1.slice(0,-1)," % [a.res]) gen(p, n.sons[sonsLen(n) - 1], a) if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1, 0])" % [a.res]) else: r.res.add("$1)" % [a.res]) proc genRepr(p: PProc, n: PNode, r: var TCompRes) = var t = skipTypes(n.sons[1].typ, abstractVarRange) case t.kind of tyInt..tyUInt64: unaryExpr(p, n, r, "", "(\"\"+ ($1))") of tyEnum, tyOrdinal: gen(p, n.sons[1], r) useMagic(p, "cstrToNimstr") r.kind = resExpr r.res = "cstrToNimstr($1.node.sons[$2].name)" % [genTypeInfo(p, t), r.res] else: # XXX: internalError(n.info, "genRepr: Not implemented") proc genOf(p: PProc, n: PNode, r: var TCompRes) = var x: TCompRes let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyTypeDesc}) gen(p, n.sons[1], x) if tfFinal in t.flags: r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)] else: useMagic(p, "isObj") r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)] r.kind = resExpr proc genReset(p: PProc, n: PNode) = var x: TCompRes useMagic(p, "genericReset") gen(p, n.sons[1], x) addf(p.body, "$1 = genericReset($1, $2);$n", [x.res, genTypeInfo(p, n.sons[1].typ)]) proc genMagic(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes line, filen: Rope var op = n.sons[0].sym.magic case op of mOr: genOr(p, n.sons[1], n.sons[2], r) of mAnd: genAnd(p, n.sons[1], n.sons[2], r) of mAddI..mStrToStr: arith(p, n, r, op) of mRepr: genRepr(p, n, r) of mSwap: genSwap(p, n) of mUnaryLt: # XXX: range checking? if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1") else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)") of mAppendStrCh: binaryExpr(p, n, r, "addChar", "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }") of mAppendStrStr: if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }") else: binaryExpr(p, n, r, "", "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}") # XXX: make a copy of $2, because of Javascript's sucking semantics of mAppendSeqElem: binaryExpr(p, n, r, "", "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }") of mConStrStr: genConStrStr(p, n, r) of mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") of mLeStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") of mIsNil: unaryExpr(p, n, r, "", "$1 == null") of mEnumToStr: genRepr(p, n, r) of mNew, mNewFinalize: genNew(p, n) of mSizeOf: r.res = rope(getSize(n.sons[1].typ)) of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do of mOrd: genOrd(p, n, r) of mLengthStr: unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)") of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1") of mLengthSeq, mLengthOpenArray, mLengthArray: unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)") of mXLenSeq: unaryExpr(p, n, r, "", "$1.length") of mHigh: if skipTypes(n.sons[1].typ, abstractVar).kind == tyString: unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)") else: unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)") of mInc: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2") else: binaryExpr(p, n, r, "addInt", "$1 = addInt($1, $2)") of ast.mDec: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)") of mSetLengthStr: binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0") of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2") of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)") of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)") of mEqSet: binaryExpr(p, n, r, "SetEq", "SetEq($1, $2)") of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)") of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)") of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)") of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true") of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]") of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)") of mNewSeq: genNewSeq(p, n) of mOf: genOf(p, n, r) of mReset: genReset(p, n) of mEcho: genEcho(p, n, r) of mNLen..mNError, mSlurp, mStaticExec: localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s) of mCopyStr: binaryExpr(p, n, r, "", "($1.slice($2))") of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))") of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)") of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)") else: genCall(p, n, r) #else internalError(e.info, 'genMagic: ' + magicToStr[op]); proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = var a, b: TCompRes useMagic(p, "SetConstr") r.res = rope("SetConstr(") r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: add(r.res, ", ") var it = n.sons[i] if it.kind == nkRange: gen(p, it.sons[0], a) gen(p, it.sons[1], b) addf(r.res, "[$1, $2]", [a.res, b.res]) else: gen(p, it, a) add(r.res, a.res) add(r.res, ")") proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = rope("[") r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: add(r.res, ", ") gen(p, n.sons[i], a) add(r.res, a.res) add(r.res, "]") proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = rope("{") r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: add(r.res, ", ") var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] gen(p, it, a) addf(r.res, "Field$#: $#" | "Field$# = $#", [i.rope, a.res]) r.res.add("}") proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = # XXX inheritance? var a: TCompRes r.res = rope("{") r.kind = resExpr for i in countup(1, sonsLen(n) - 1): if i > 1: add(r.res, ", ") var it = n.sons[i] internalAssert it.kind == nkExprColonExpr gen(p, it.sons[1], a) var f = it.sons[0].sym if f.loc.r == nil: f.loc.r = mangleName(f) addf(r.res, "$#: $#" | "$# = $#" , [f.loc.r, a.res]) r.res.add("}") proc genConv(p: PProc, n: PNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) var src = skipTypes(n.sons[1].typ, abstractVarRange) gen(p, n.sons[1], r) if dest.kind == src.kind: # no-op conversion return case dest.kind: of tyBool: r.res = ("(($1)? 1:0)" | "toBool($#)") % [r.res] r.kind = resExpr of tyInt: r.res = "($1|0)" % [r.res] else: # TODO: What types must we handle here? discard proc upConv(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) # XXX proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = var a, b: TCompRes gen(p, n.sons[0], r) if optRangeCheck in p.options: gen(p, n.sons[1], a) gen(p, n.sons[2], b) useMagic(p, "chckRange") r.res = "chckRange($1, $2, $3)" % [r.res, a.res, b.res] r.kind = resExpr proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down # much of the code otherwise: if n.sons[0].kind == nkCStringToString: gen(p, n.sons[0].sons[0], r) else: gen(p, n.sons[0], r) if r.res == nil: internalError(n.info, "convStrToCStr") useMagic(p, "toJSStr") r.res = "toJSStr($1)" % [r.res] r.kind = resExpr proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down # much of the code otherwise: if n.sons[0].kind == nkStringToCString: gen(p, n.sons[0].sons[0], r) else: gen(p, n.sons[0], r) if r.res == nil: internalError(n.info, "convCStrToStr") useMagic(p, "cstrToNimstr") r.res = "cstrToNimstr($1)" % [r.res] r.kind = resExpr proc genReturnStmt(p: PProc, n: PNode) = if p.procDef == nil: internalError(n.info, "genReturnStmt") p.beforeRetNeeded = true if (n.sons[0].kind != nkEmpty): genStmt(p, n.sons[0]) else: genLineDir(p, n) addf(p.body, "break BeforeRet;$n" | "goto ::BeforeRet::;$n", []) proc genProcBody(p: PProc, prc: PSym): Rope = if optStackTrace in prc.options: result = (("var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" | "local F={procname=$#,prev=framePtr,filename=$#,line=0};$n") & "framePtr = F;$n") % [ makeJSString(prc.owner.name.s & '.' & prc.name.s), makeJSString(toFilename(prc.info))] else: result = nil if p.beforeRetNeeded: addf(result, "BeforeRet: do {$n$1} while (false); $n" | "$#;::BeforeRet::$n", [p.body]) else: add(result, p.body) if prc.typ.callConv == ccSysCall and p.target == targetJS: result = ("try {$n$1} catch (e) {$n" & " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result] if optStackTrace in prc.options: add(result, "framePtr = framePtr.prev;" & tnl) proc genProc(oldProc: PProc, prc: PSym): Rope = var resultSym: PSym name, returnStmt, resultAsgn, header: Rope a: TCompRes #if gVerbosity >= 3: # echo "BEGIN generating code for: " & prc.name.s var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) p.target = oldProc.target p.up = oldProc returnStmt = nil resultAsgn = nil name = mangleName(prc) header = generateHeader(p, prc.typ) if prc.typ.sons[0] != nil and sfPure notin prc.flags: resultSym = prc.ast.sons[resultPos].sym resultAsgn = ("var $# = $#;$n" | "local $# = $#;$n") % [ mangleName(resultSym), createVar(p, resultSym.typ, isIndirect(resultSym))] gen(p, prc.ast.sons[resultPos], a) returnStmt = "return $#;$n" % [a.res] genStmt(p, prc.getBody) result = ("function $#($#) {$n$#$#$#$#}$n" | "function $#($#) $n$#$#$#$#$nend$n") % [name, header, p.locals, resultAsgn, genProcBody(p, prc), returnStmt] #if gVerbosity >= 3: # echo "END generated code for: " & prc.name.s proc genStmt(p: PProc, n: PNode) = var r: TCompRes gen(p, n, r) if r.res != nil: addf(p.body, "$#;$n", [r.res]) proc gen(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone r.kind = resNone #r.address = nil r.res = nil case n.kind of nkSym: genSym(p, n, r) of nkCharLit..nkInt64Lit: r.res = rope(n.intVal) r.kind = resExpr of nkNilLit: if isEmptyType(n.typ): discard elif mapType(n.typ) == etyBaseIndex: r.typ = etyBaseIndex r.address = rope"null" | rope"nil" r.res = rope"0" r.kind = resExpr else: r.res = rope"null" | rope"nil" r.kind = resExpr of nkStrLit..nkTripleStrLit: if skipTypes(n.typ, abstractVarRange).kind == tyString: useMagic(p, "cstrToNimstr") r.res = "cstrToNimstr($1)" % [makeJSString(n.strVal)] else: r.res = makeJSString(n.strVal) r.kind = resExpr of nkFloatLit..nkFloat64Lit: let f = n.floatVal if f != f: r.res = rope"NaN" elif f == 0.0: r.res = rope"0.0" elif f == 0.5 * f: if f > 0.0: r.res = rope"Infinity" else: r.res = rope"-Infinity" else: r.res = rope(f.toStrMaxPrecision) r.kind = resExpr of nkCallKinds: if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): genMagic(p, n, r) elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and n.len >= 2: genInfixCall(p, n, r) else: genCall(p, n, r) of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) of nkPar: genTupleConstr(p, n, r) of nkObjConstr: genObjConstr(p, n, r) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r) of nkAddr, nkHiddenAddr: genAddr(p, n, r) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r) of nkBracketExpr: genArrayAccess(p, n, r) of nkDotExpr: genFieldAccess(p, n, r) of nkCheckedFieldExpr: genCheckedFieldAccess(p, n, r) of nkObjDownConv: gen(p, n.sons[0], r) of nkObjUpConv: upConv(p, n, r) of nkCast: gen(p, n.sons[1], r) of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF") of nkChckRange64: genRangeChck(p, n, r, "chckRange64") of nkChckRange: genRangeChck(p, n, r, "chckRange") of nkStringToCString: convStrToCStr(p, n, r) of nkCStringToString: convCStrToStr(p, n, r) of nkEmpty: discard of nkLambdaKinds: let s = n.sons[namePos].sym discard mangleName(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): add(p.locals, genProc(p, s)) of nkType: r.res = genTypeInfo(p, n.typ) of nkStmtList, nkStmtListExpr: # this shows the distinction is nice for backends and should be kept # in the frontend let isExpr = not isEmptyType(n.typ) for i in countup(0, sonsLen(n) - 1 - isExpr.ord): genStmt(p, n.sons[i]) if isExpr: gen(p, lastSon(n), r) of nkBlockStmt, nkBlockExpr: genBlock(p, n, r) of nkIfStmt, nkIfExpr: genIf(p, n, r) of nkWhileStmt: genWhileStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n) of nkConstSection: discard of nkForStmt, nkParForStmt: internalError(n.info, "for statement not eliminated") of nkCaseStmt: if p.target == targetJS: genCaseJS(p, n, r) else: genCaseLua(p, n, r) of nkReturnStmt: genReturnStmt(p, n) of nkBreakStmt: genBreakStmt(p, n) of nkAsgn: genAsgn(p, n) of nkFastAsgn: genFastAsgn(p, n) of nkDiscardStmt: if n.sons[0].kind != nkEmpty: genLineDir(p, n) gen(p, n.sons[0], r) of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt: genTry(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: discard of nkProcDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: genSym(p, n.sons[namePos], r) r.res = nil of nkGotoState, nkState: internalError(n.info, "first class iterators not implemented") of nkPragmaBlock: gen(p, n.lastSon, r) else: internalError(n.info, "gen: unknown node type: " & $n.kind) var globals: PGlobals proc newModule(module: PSym): BModule = new(result) result.module = module if globals == nil: globals = newGlobals() proc genHeader(): Rope = result = ("/* Generated by the Nim Compiler v$1 */$n" & "/* (c) 2015 Andreas Rumpf */$n$n" & "var framePtr = null;$n" & "var excHandler = null;$n" & "var lastJSError = null;$n") % [rope(VersionAsString)] proc genModule(p: PProc, n: PNode) = if optStackTrace in p.options: addf(p.body, "var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" & "framePtr = F;$n", [ makeJSString("module " & p.module.module.name.s), makeJSString(toFilename(p.module.module.info))]) genStmt(p, n) if optStackTrace in p.options: addf(p.body, "framePtr = framePtr.prev;$n", []) proc myProcess(b: PPassContext, n: PNode): PNode = if passes.skipCodegen(n): return n result = n var m = BModule(b) if m.module == nil: internalError(n.info, "myProcess") var p = newProc(globals, m, nil, m.module.options) genModule(p, n) add(p.g.code, p.locals) add(p.g.code, p.body) proc wholeCode*(m: BModule): Rope = for prc in globals.forwarded: if not globals.generatedSyms.containsOrIncl(prc.id): var p = newProc(globals, m, nil, m.module.options) add(p.g.code, genProc(p, prc)) var disp = generateMethodDispatchers() for i in 0..sonsLen(disp)-1: let prc = disp.sons[i].sym if not globals.generatedSyms.containsOrIncl(prc.id): var p = newProc(globals, m, nil, m.module.options) add(p.g.code, genProc(p, prc)) result = globals.typeInfo & globals.code proc myClose(b: PPassContext, n: PNode): PNode = if passes.skipCodegen(n): return n result = myProcess(b, n) var m = BModule(b) if sfMainModule in m.module.flags: let code = wholeCode(m) let outfile = if options.outFile.len > 0: if options.outFile.isAbsolute: options.outFile else: getCurrentDir() / options.outFile else: changeFileExt(completeCFilePath(m.module.filename), "js") discard writeRopeIfNotEqual(genHeader() & code, outfile) proc myOpenCached(s: PSym, rd: PRodReader): PPassContext = internalError("symbol files are not possible with the JS code generator") result = nil proc myOpen(s: PSym): PPassContext = result = newModule(s) const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)