# # # The Nimrod Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This is the EMCAScript (also known as JavaScript) code generator. # **Invariant: each expression occurs only once in the generated # code!** 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 # implementation type 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 TCompRes{.final.} = object kind: TJSTypeKind com: PRope # computation part # address if this is a (address, index)-tuple res: PRope # result part; index if this is an # (address, index)-tuple TBlock{.final.} = object id: int # the ID of the label; positive means that it # has been used (i.e. the label should be emitted) nestedTryStmts: int # how many try statements is it nested into isLoop: bool # whether it's a 'block' or 'while' TGlobals{.final.} = object typeInfo, code: PRope forwarded: seq[PSym] generatedSyms: TIntSet typeInfoGenerated: TIntSet PGlobals = ref TGlobals TProc{.final.} = object procDef: PNode prc: PSym data: PRope options: TOptions module: BModule g: PGlobals BeforeRetNeeded: bool nestedTryStmts: int unique: int blocks: seq[TBlock] proc newGlobals(): PGlobals = new(result) result.forwarded = @[] result.generatedSyms = initIntSet() result.typeInfoGenerated = initIntSet() proc initCompRes(r: var TCompRes) = r.com = nil r.res = nil r.kind = etyNone proc initProc(p: var TProc, globals: PGlobals, module: BModule, procDef: PNode, options: TOptions) = p.blocks = @[] p.options = options p.module = module p.procDef = procDef p.g = globals if procDef != nil: p.prc = procDef.sons[namePos].sym const MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyVar, tyRef, tyPtr, tyBigNum, tyVarargs} proc mapType(typ: PType): TJSTypeKind = var t = skipTypes(typ, abstractInst) case t.kind of tyVar, tyRef, tyPtr: if skipTypes(t.sons[0], 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, tyGenericInvokation, tyNone, tyForward, tyEmpty, tyExpr, tyStmt, tyTypeDesc, tyTypeClass: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString proc mangle(name: string): string = result = "" for i in countup(0, len(name) - 1): case name[i] of 'A'..'Z': add(result, chr(ord(name[i]) - ord('A') + ord('a'))) of '_': nil of 'a'..'z', '0'..'9': add(result, name[i]) else: add(result, 'X' & toHex(ord(name[i]), 2)) proc mangleName(s: PSym): PRope = result = s.loc.r if result == nil: result = toRope(mangle(s.name.s)) app(result, "_") app(result, toRope(s.id)) s.loc.r = result proc makeJSString(s: string): PRope = strutils.escape(s).toRope proc genTypeInfo(p: var TProc, typ: PType): PRope proc genObjectFields(p: var TProc, typ: PType, n: PNode): PRope = var s, u: PRope length: int field: PSym b: PNode result = nil case n.kind of nkRecList: length = sonsLen(n) if length == 1: result = genObjectFields(p, typ, n.sons[0]) else: s = nil for i in countup(0, length - 1): if i > 0: app(s, ", " & tnl) app(s, genObjectFields(p, typ, n.sons[i])) result = ropef("{kind: 2, len: $1, offset: 0, " & "typ: null, name: null, sons: [$2]}", [toRope(length), s]) of nkSym: field = n.sym s = genTypeInfo(p, field.typ) result = ropef("{kind: 1, offset: \"$1\", len: 0, " & "typ: $2, name: $3, sons: null}", [mangleName(field), s, makeJSString(field.name.s)]) of nkRecCase: length = sonsLen(n) if (n.sons[0].kind != nkSym): InternalError(n.info, "genObjectFields") field = n.sons[0].sym s = genTypeInfo(p, field.typ) for i in countup(1, length - 1): b = n.sons[i] # branch u = nil case b.kind of nkOfBranch: if sonsLen(b) < 2: internalError(b.info, "genObjectFields; nkOfBranch broken") for j in countup(0, sonsLen(b) - 2): if u != nil: app(u, ", ") if b.sons[j].kind == nkRange: appf(u, "[$1, $2]", [toRope(getOrdValue(b.sons[j].sons[0])), toRope(getOrdValue(b.sons[j].sons[1]))]) else: app(u, toRope(getOrdValue(b.sons[j]))) of nkElse: u = toRope(lengthOrd(field.typ)) else: internalError(n.info, "genObjectFields(nkRecCase)") if result != nil: app(result, ", " & tnl) appf(result, "[SetConstr($1), $2]", [u, genObjectFields(p, typ, lastSon(b))]) result = ropef("{kind: 3, offset: \"$1\", len: $3, " & "typ: $2, name: $4, sons: [$5]}", [mangleName(field), s, toRope(lengthOrd(field.typ)), makeJSString(field.name.s), result]) else: internalError(n.info, "genObjectFields") proc genObjectInfo(p: var TProc, typ: PType, name: PRope) = var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & "finalizer: null};$n", [name, toRope(ord(typ.kind))]) prepend(p.g.typeInfo, s) appf(p.g.typeInfo, "var NNI$1 = $2;$n", [toRope(typ.id), genObjectFields(p, typ, typ.n)]) appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) if (typ.kind == tyObject) and (typ.sons[0] != nil): appf(p.g.typeInfo, "$1.base = $2;$n", [name, genTypeInfo(p, typ.sons[0])]) proc genTupleFields(p: var TProc, typ: PType): PRope = var s: PRope = nil for i in 0 .. 0: app(s, ", " & tnl) s.appf("{kind: 1, offset: \"Field$1\", len: 0, " & "typ: $2, name: \"Field$1\", sons: null}", [i.toRope, genTypeInfo(p, typ.sons[i])]) result = ropef("{kind: 2, len: $1, offset: 0, " & "typ: null, name: null, sons: [$2]}", [toRope(typ.len), s]) proc genTupleInfo(p: var TProc, typ: PType, name: PRope) = var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & "finalizer: null};$n", [name, toRope(ord(typ.kind))]) prepend(p.g.typeInfo, s) appf(p.g.typeInfo, "var NNI$1 = $2;$n", [toRope(typ.id), genTupleFields(p, typ)]) appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) proc genEnumInfo(p: var TProc, typ: PType, name: PRope) = let length = sonsLen(typ.n) var s: PRope = nil for i in countup(0, length - 1): if (typ.n.sons[i].kind != nkSym): InternalError(typ.n.info, "genEnumInfo") let field = typ.n.sons[i].sym if i > 0: app(s, ", " & tnl) let extName = if field.ast == nil: field.name.s else: field.ast.strVal appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", [toRope(field.position), name, makeJSString(extName)]) var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " & "name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s]) s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & "finalizer: null};$n", [name, toRope(ord(typ.kind))]) prepend(p.g.typeInfo, s) app(p.g.typeInfo, n) appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) if typ.sons[0] != nil: appf(p.g.typeInfo, "$1.base = $2;$n", [name, genTypeInfo(p, typ.sons[0])]) proc genTypeInfo(p: var TProc, typ: PType): PRope = var t = typ if t.kind == tyGenericInst: t = lastSon(t) result = ropef("NTI$1", [toRope(t.id)]) if ContainsOrIncl(p.g.TypeInfoGenerated, t.id): return case t.kind of tyDistinct: result = genTypeInfo(p, typ.sons[0]) of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128: var s = ropef( "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", [result, toRope(ord(t.kind))]) prepend(p.g.typeInfo, s) of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet: var s = ropef( "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", [result, toRope(ord(t.kind))]) prepend(p.g.typeInfo, s) appf(p.g.typeInfo, "$1.base = $2;$n", [result, genTypeInfo(p, typ.sons[0])]) of tyArrayConstr, tyArray: var s = ropef( "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n", [result, toRope(ord(t.kind))]) prepend(p.g.typeInfo, s) appf(p.g.typeInfo, "$1.base = $2;$n", [result, genTypeInfo(p, typ.sons[1])]) of tyEnum: genEnumInfo(p, t, result) of tyObject: genObjectInfo(p, t, result) of tyTuple: genTupleInfo(p, t, result) else: InternalError("genTypeInfo(" & $t.kind & ')') proc gen(p: var TProc, n: PNode, r: var TCompRes) proc genStmt(p: var TProc, n: PNode, r: var TCompRes) proc genProc(oldProc: var TProc, prc: PSym, r: var TCompRes) proc genConstant(p: var TProc, c: PSym, r: var TCompRes) proc mergeExpr(a, b: PRope): PRope = if (a != nil): if b != nil: result = ropef("($1, $2)", [a, b]) else: result = a else: result = b proc mergeExpr(r: TCompRes): PRope = result = mergeExpr(r.com, r.res) proc mergeStmt(r: TCompRes): PRope = if r.res == nil: result = r.com elif r.com == nil: result = r.res else: result = ropef("$1$2", [r.com, r.res]) proc useMagic(p: var TProc, 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): var r: TCompRes genProc(p, s, r) app(p.g.code, mergeStmt(r)) 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 genAnd(p: var TProc, a, b: PNode, r: var TCompRes) = var x, y: TCompRes gen(p, a, x) gen(p, b, y) r.res = ropef("($1 && $2)", [mergeExpr(x), mergeExpr(y)]) proc genOr(p: var TProc, a, b: PNode, r: var TCompRes) = var x, y: TCompRes gen(p, a, x) gen(p, b, y) r.res = ropef("($1 || $2)", [mergeExpr(x), mergeExpr(y)]) type TMagicFrmt = array[0..3, string] const # magic checked op; magic unchecked op; checked op; unchecked op ops: array[mAddi..mStrToStr, TMagicFrmt] = [ ["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 ["", "", "($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)"], # MinI64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64 ["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)"], # EqProc ["", "", "($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 ["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)"], # UnaryPlusI64 ["", "", "~($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: var TProc, 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 = ropef(frmt, [x.res, y.res]) r.com = mergeExpr(x.com, y.com) proc binaryStmt(p: var TProc, 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) if x.com != nil: appf(r.com, "$1;$n", [x.com]) if y.com != nil: appf(r.com, "$1;$n", [y.com]) appf(r.com, frmt, [x.res, y.res]) proc unaryExpr(p: var TProc, n: PNode, r: var TCompRes, magic, frmt: string) = useMagic(p, magic) gen(p, n.sons[1], r) r.res = ropef(frmt, [r.res]) proc arith(p: var TProc, n: PNode, r: var TCompRes, op: TMagic) = var x, y: TCompRes i: int if optOverflowCheck in p.options: i = 0 else: i = 1 useMagic(p, ops[op][i]) if sonsLen(n) > 2: gen(p, n.sons[1], x) gen(p, n.sons[2], y) r.res = ropef(ops[op][i + 2], [x.res, y.res]) r.com = mergeExpr(x.com, y.com) else: gen(p, n.sons[1], r) r.res = ropef(ops[op][i + 2], [r.res]) proc genLineDir(p: var TProc, n: PNode, r: var TCompRes) = var line: int line = toLinenumber(n.info) if optLineDir in p.Options: appf(r.com, "// line $2 \"$1\"$n", [toRope(toFilename(n.info)), toRope(line)]) if ({optStackTrace, optEndb} * p.Options == {optStackTrace, optEndb}) and ((p.prc == nil) or not (sfPure in p.prc.flags)): useMagic(p, "endb") appf(r.com, "endb($1);$n", [toRope(line)]) elif ({optLineTrace, optStackTrace} * p.Options == {optLineTrace, optStackTrace}) and ((p.prc == nil) or not (sfPure in p.prc.flags)): appf(r.com, "F.line = $1;$n", [toRope(line)]) proc finishTryStmt(p: var TProc, r: var TCompRes, howMany: int) = for i in countup(1, howMany): app(r.com, "excHandler = excHandler.prev;" & tnl) proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes length, labl: int genLineDir(p, n, r) inc(p.unique) length = len(p.blocks) setlen(p.blocks, length + 1) p.blocks[length].id = - p.unique p.blocks[length].nestedTryStmts = p.nestedTryStmts p.blocks[length].isLoop = true labl = p.unique gen(p, n.sons[0], cond) genStmt(p, n.sons[1], stmt) if p.blocks[length].id > 0: appf(r.com, "L$3: while ($1) {$n$2}$n", [mergeExpr(cond), mergeStmt(stmt), toRope(labl)]) else: appf(r.com, "while ($1) {$n$2}$n", [mergeExpr(cond), mergeStmt(stmt)]) setlen(p.blocks, length) proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) = # code to generate: # # var sp = {prev: excHandler, exc: null}; # excHandler = sp; # try { # stmts; # } 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; # } # var i, length, blen: int safePoint, orExpr, epart: PRope a: TCompRes genLineDir(p, n, r) inc(p.unique) safePoint = ropef("Tmp$1", [toRope(p.unique)]) appf(r.com, "var $1 = {prev: excHandler, exc: null};$n" & "excHandler = $1;$n", [safePoint]) if optStackTrace in p.Options: app(r.com, "framePtr = F;" & tnl) app(r.com, "try {" & tnl) length = sonsLen(n) inc(p.nestedTryStmts) genStmt(p, n.sons[0], a) app(r.com, mergeStmt(a)) i = 1 epart = nil while (i < length) and (n.sons[i].kind == nkExceptBranch): blen = sonsLen(n.sons[i]) if blen == 1: # general except section: if i > 1: app(epart, "else {" & tnl) genStmt(p, n.sons[i].sons[0], a) app(epart, mergeStmt(a)) if i > 1: app(epart, '}' & tnl) else: orExpr = 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: app(orExpr, "||") appf(orExpr, "isObj($1.exc.m_type, $2)", [safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)]) if i > 1: app(epart, "else ") appf(epart, "if ($1.exc && $2) {$n", [safePoint, orExpr]) genStmt(p, n.sons[i].sons[blen - 1], a) appf(epart, "$1}$n", [mergeStmt(a)]) inc(i) if epart != nil: appf(r.com, "} catch (EXC) {$n$1", [epart]) finishTryStmt(p, r, p.nestedTryStmts) dec(p.nestedTryStmts) app(r.com, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) if (i < length) and (n.sons[i].kind == nkFinally): genStmt(p, n.sons[i].sons[0], a) app(r.com, mergeStmt(a)) app(r.com, '}' & tnl) proc genRaiseStmt(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes typ: PType genLineDir(p, n, r) if n.sons[0].kind != nkEmpty: gen(p, n.sons[0], a) if a.com != nil: appf(r.com, "$1;$n", [a.com]) typ = skipTypes(n.sons[0].typ, abstractPtrs) useMagic(p, "raiseException") appf(r.com, "raiseException($1, $2);$n", [a.res, makeJSString(typ.sym.name.s)]) else: useMagic(p, "reraiseException") app(r.com, "reraiseException();" & tnl) proc genCaseStmt(p: var TProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes it, e, v: PNode stringSwitch: bool genLineDir(p, n, r) gen(p, n.sons[0], cond) if cond.com != nil: appf(r.com, "$1;$n", [cond.com]) stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString if stringSwitch: useMagic(p, "toJSStr") appf(r.com, "switch (toJSStr($1)) {$n", [cond.res]) else: appf(r.com, "switch ($1) {$n", [cond.res]) for i in countup(1, sonsLen(n) - 1): it = n.sons[i] case it.kind of nkOfBranch: for j in countup(0, sonsLen(it) - 2): e = it.sons[j] if e.kind == nkRange: v = copyNode(e.sons[0]) while (v.intVal <= e.sons[1].intVal): gen(p, v, cond) if cond.com != nil: internalError(v.info, "jsgen.genCaseStmt") appf(r.com, "case $1: ", [cond.res]) Inc(v.intVal) else: gen(p, e, cond) if cond.com != nil: internalError(e.info, "jsgen.genCaseStmt") if stringSwitch: case e.kind of nkStrLit..nkTripleStrLit: appf(r.com, "case $1: ", [makeJSString(e.strVal)]) else: InternalError(e.info, "jsgen.genCaseStmt: 2") else: appf(r.com, "case $1: ", [cond.res]) genStmt(p, lastSon(it), stmt) appf(r.com, "$n$1break;$n", [mergeStmt(stmt)]) of nkElse: genStmt(p, it.sons[0], stmt) appf(r.com, "default: $n$1break;$n", [mergeStmt(stmt)]) else: internalError(it.info, "jsgen.genCaseStmt") appf(r.com, "}$n", []) proc genStmtListExpr(p: var TProc, n: PNode, r: var TCompRes) proc genBlock(p: var TProc, n: PNode, r: var TCompRes) = var idx, labl: int sym: PSym inc(p.unique) idx = len(p.blocks) if n.sons[0].kind != nkEmpty: # named block? if (n.sons[0].kind != nkSym): InternalError(n.info, "genBlock") sym = n.sons[0].sym sym.loc.k = locOther sym.loc.a = idx setlen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet p.blocks[idx].nestedTryStmts = p.nestedTryStmts labl = p.unique if n.kind == nkBlockExpr: genStmtListExpr(p, n.sons[1], r) else: genStmt(p, n.sons[1], r) if p.blocks[idx].id > 0: # label has been used: r.com = ropef("L$1: do {$n$2} while(false);$n", [toRope(labl), r.com]) setlen(p.blocks, idx) proc genBreakStmt(p: var TProc, n: PNode, r: var TCompRes) = var idx: int sym: PSym genLineDir(p, n, r) if n.sons[0].kind != nkEmpty: # named break? assert(n.sons[0].kind == nkSym) sym = n.sons[0].sym assert(sym.loc.k == locOther) idx = sym.loc.a 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 finishTryStmt(p, r, p.nestedTryStmts - p.blocks[idx].nestedTryStmts) appf(r.com, "break L$1;$n", [toRope(p.blocks[idx].id)]) proc genAsmStmt(p: var TProc, n: PNode, r: var TCompRes) = genLineDir(p, n, r) assert(n.kind == nkAsmStmt) for i in countup(0, sonsLen(n) - 1): case n.sons[i].Kind of nkStrLit..nkTripleStrLit: app(r.com, n.sons[i].strVal) of nkSym: app(r.com, mangleName(n.sons[i].sym)) else: InternalError(n.sons[i].info, "jsgen: genAsmStmt()") proc genIfStmt(p: var TProc, n: PNode, r: var TCompRes) = var toClose: int cond, stmt: TCompRes it: PNode toClose = 0 for i in countup(0, sonsLen(n) - 1): it = n.sons[i] if sonsLen(it) != 1: gen(p, it.sons[0], cond) genStmt(p, it.sons[1], stmt) if i > 0: appf(r.com, "else {$n", []) inc(toClose) if cond.com != nil: appf(r.com, "$1;$n", [cond.com]) appf(r.com, "if ($1) {$n$2}", [cond.res, mergeStmt(stmt)]) else: # else part: genStmt(p, it.sons[0], stmt) appf(r.com, "else {$n$1}$n", [mergeStmt(stmt)]) app(r.com, repeatChar(toClose, '}') & tnl) proc genIfExpr(p: var TProc, n: PNode, r: var TCompRes) = var toClose: int cond, stmt: TCompRes it: PNode toClose = 0 for i in countup(0, sonsLen(n) - 1): it = n.sons[i] if sonsLen(it) != 1: gen(p, it.sons[0], cond) gen(p, it.sons[1], stmt) if i > 0: app(r.res, ": (") inc(toClose) r.com = mergeExpr(r.com, cond.com) r.com = mergeExpr(r.com, stmt.com) appf(r.res, "($1) ? ($2)", [cond.res, stmt.res]) else: # else part: gen(p, it.sons[0], stmt) r.com = mergeExpr(r.com, stmt.com) appf(r.res, ": ($1)", [stmt.res]) app(r.res, repeatChar(toClose, ')')) proc generateHeader(p: var TProc, typ: PType): PRope = var param: PSym name: PRope result = nil for i in countup(1, sonsLen(typ.n) - 1): if result != nil: app(result, ", ") assert(typ.n.sons[i].kind == nkSym) param = typ.n.sons[i].sym name = mangleName(param) app(result, name) if mapType(param.typ) == etyBaseIndex: app(result, ", ") app(result, name) app(result, "_Idx") const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nkFloatLit..nkFloat64Lit, nkCurly, nkPar, 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: var TProc, x, y: PNode, r: var TCompRes, noCopyNeeded: bool) = var a, b: TCompRes gen(p, x, a) gen(p, y, b) case mapType(x.typ) of etyObject: if a.com != nil: appf(r.com, "$1;$n", [a.com]) if b.com != nil: appf(r.com, "$1;$n", [b.com]) if needsNoCopy(y) or noCopyNeeded: appf(r.com, "$1 = $2;$n", [a.res, b.res]) else: useMagic(p, "NimCopy") appf(r.com, "$1 = NimCopy($2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if (a.kind != etyBaseIndex) or (b.kind != etyBaseIndex): internalError(x.info, "genAsgn") appf(r.com, "$1 = $2; $3 = $4;$n", [a.com, b.com, a.res, b.res]) else: if a.com != nil: appf(r.com, "$1;$n", [a.com]) if b.com != nil: appf(r.com, "$1;$n", [b.com]) appf(r.com, "$1 = $2;$n", [a.res, b.res]) proc genAsgn(p: var TProc, n: PNode, r: var TCompRes) = genLineDir(p, n, r) genAsgnAux(p, n.sons[0], n.sons[1], r, false) proc genFastAsgn(p: var TProc, n: PNode, r: var TCompRes) = genLineDir(p, n, r) genAsgnAux(p, n.sons[0], n.sons[1], r, true) proc genSwap(p: var TProc, n: PNode, r: var TCompRes) = var a, b: TCompRes gen(p, n.sons[1], a) gen(p, n.sons[2], b) inc(p.unique) var tmp = ropef("Tmp$1", [toRope(p.unique)]) case mapType(skipTypes(n.sons[1].typ, abstractVar)) of etyBaseIndex: inc(p.unique) var tmp2 = ropef("Tmp$1", [toRope(p.unique)]) if (a.kind != etyBaseIndex) or (b.kind != etyBaseIndex): internalError(n.info, "genSwap") appf(r.com, "var $1 = $2; $2 = $3; $3 = $1;$n", [tmp, a.com, b.com]) appf(r.com, "var $1 = $2; $2 = $3; $3 = $1", [tmp2, a.res, b.res]) else: if a.com != nil: appf(r.com, "$1;$n", [a.com]) if b.com != nil: appf(r.com, "$1;$n", [b.com]) appf(r.com, "var $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: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes r.kind = etyBaseIndex var 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(ropeToStr(f.loc.r)) r.com = mergeExpr(a) proc genFieldAccess(p: var TProc, n: PNode, r: var TCompRes) = r.kind = etyNone gen(p, n.sons[0], r) if skipTypes(n.sons[0].typ, abstractVarRange).kind == tyTuple: r.res = ropef("$1.Field$2", [r.res, getFieldPosition(n.sons[1]).toRope]) 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 = ropef("$1.$2", [r.res, f.loc.r]) proc genCheckedFieldAddr(p: var TProc, n: PNode, r: var TCompRes) = genFieldAddr(p, n.sons[0], r) # XXX proc genCheckedFieldAccess(p: var TProc, n: PNode, r: var TCompRes) = genFieldAccess(p, n.sons[0], r) # XXX proc genArrayAddr(p: var TProc, n: PNode, r: var TCompRes) = var a, b: TCompRes first: biggestInt r.kind = etyBaseIndex gen(p, n.sons[0], a) gen(p, n.sons[1], b) r.com = mergeExpr(a) var typ = skipTypes(n.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(n.sons[1]): useMagic(p, "chckIndx") b.res = ropef("chckIndx($1, $2, $3.length)-$2", [b.res, toRope(first), a.res]) # XXX: BUG: a.res evaluated twice! elif first != 0: b.res = ropef("($1)-$2", [b.res, toRope(first)]) r.res = mergeExpr(b) proc genArrayAccess(p: var TProc, n: PNode, r: var TCompRes) = var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.sons[0], 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.kind = etyNone r.res = ropef("$1[$2]", [r.com, r.res]) r.com = nil proc genAddr(p: var TProc, n: PNode, r: var TCompRes) = var s: PSym case n.sons[0].kind of nkSym: s = n.sons[0].sym if s.loc.r == nil: InternalError(n.info, "genAddr: 3") case s.kind of skVar, skLet, skResult: if mapType(n.typ) == etyObject: # make addr() a no-op: r.kind = etyNone r.res = s.loc.r r.com = nil elif sfGlobal in s.flags: # globals are always indirect accessible r.kind = etyBaseIndex r.com = toRope("Globals") r.res = makeJSString(ropeToStr(s.loc.r)) elif sfAddrTaken in s.flags: r.kind = etyBaseIndex r.com = s.loc.r r.res = toRope("0") else: InternalError(n.info, "genAddr: 4") else: InternalError(n.info, "genAddr: 2") of nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r) of nkDotExpr: genFieldAddr(p, n, r) of nkBracketExpr: var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.sons[0], 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 & ')') else: InternalError(n.info, "genAddr") proc genSym(p: var TProc, 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) var k = mapType(s.typ) if k == etyBaseIndex: r.kind = etyBaseIndex if {sfAddrTaken, sfGlobal} * s.flags != {}: r.com = ropef("$1[0]", [s.loc.r]) r.res = ropef("$1[1]", [s.loc.r]) else: r.com = s.loc.r r.res = con(s.loc.r, "_Idx") elif (k != etyObject) and (sfAddrTaken in s.flags): r.res = ropef("$1[0]", [s.loc.r]) else: r.res = s.loc.r of skConst: genConstant(p, s, r) 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 isGenericRoutine(s) or {sfImportc, sfInfixCall} * s.flags != {}: nil elif s.kind == skMethod and s.getBody.kind == nkEmpty: # we cannot produce code for the dispatcher yet: nil elif sfForward in s.flags: p.g.forwarded.add(s) elif not p.g.generatedSyms.containsOrIncl(s.id): var r2: TCompRes genProc(p, s, r2) app(p.g.code, mergeStmt(r2)) else: if s.loc.r == nil: InternalError(n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r proc genDeref(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes if mapType(n.sons[0].typ) == etyObject: gen(p, n.sons[0], r) else: gen(p, n.sons[0], a) if a.kind != etyBaseIndex: InternalError(n.info, "genDeref") r.res = ropef("$1[$2]", [a.com, a.res]) proc genArg(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n, a) if a.kind == etyBaseIndex: app(r.res, a.com) app(r.res, ", ") app(r.res, a.res) else: app(r.res, mergeExpr(a)) proc genArgs(p: var TProc, n: PNode, r: var TCompRes) = app(r.res, "(") for i in countup(1, sonsLen(n) - 1): if i > 1: app(r.res, ", ") genArg(p, n.sons[i], r) app(r.res, ")") proc genCall(p: var TProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) genArgs(p, n, r) proc genInfixCall(p: var TProc, n: PNode, r: var TCompRes) = gen(p, n.sons[1], r) if r.kind == etyBaseIndex: if r.com == nil: GlobalError(n.info, "cannot invoke with infix syntax") r.res = ropef("$1[0]", [r.res, r.com]) r.com = nil app(r.res, ".") var op: TCompRes gen(p, n.sons[0], op) app(r.res, mergeExpr(op)) app(r.res, "(") for i in countup(2, sonsLen(n) - 1): if i > 2: app(r.res, ", ") genArg(p, n.sons[i], r) app(r.res, ")") proc genEcho(p: var TProc, n: PNode, r: var TCompRes) = useMagic(p, "rawEcho") app(r.res, "rawEcho") genArgs(p, n, r) proc putToSeq(s: string, indirect: bool): PRope = result = toRope(s) if indirect: result = ropef("[$1]", [result]) proc createVar(p: var TProc, typ: PType, indirect: bool): PRope proc createRecordVarAux(p: var TProc, rec: PNode, c: var int): PRope = result = nil case rec.kind of nkRecList: for i in countup(0, sonsLen(rec) - 1): app(result, createRecordVarAux(p, rec.sons[i], c)) of nkRecCase: app(result, createRecordVarAux(p, rec.sons[0], c)) for i in countup(1, sonsLen(rec) - 1): app(result, createRecordVarAux(p, lastSon(rec.sons[i]), c)) of nkSym: if c > 0: app(result, ", ") app(result, mangleName(rec.sym)) app(result, ": ") app(result, createVar(p, rec.sym.typ, false)) inc(c) else: InternalError(rec.info, "createRecordVarAux") proc createVar(p: var TProc, typ: PType, indirect: bool): PRope = var t = skipTypes(typ, abstractInst) case t.kind of tyInt..tyInt64, 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 = toRope("{}") of tyBool: result = putToSeq("false", indirect) of tyArray, tyArrayConstr: var length = int(lengthOrd(t)) var e = elemType(t) if length > 32: useMagic(p, "ArrayConstr") result = ropef("ArrayConstr($1, $2, $3)", [toRope(length), createVar(p, e, false), genTypeInfo(p, e)]) else: result = toRope("[") var i = 0 while i < length: if i > 0: app(result, ", ") app(result, createVar(p, e, false)) inc(i) app(result, "]") of tyTuple: result = toRope("{") for i in 0.. 0: app(result, ", ") appf(result, "Field$1: $2", i.toRope, createVar(p, t.sons[i], false)) app(result, "}") of tyObject: result = toRope("{") var c = 0 if not (tfFinal in t.flags) or (t.sons[0] != nil): inc(c) appf(result, "m_type: $1", [genTypeInfo(p, t)]) while t != nil: app(result, createRecordVarAux(p, t.n, c)) t = t.sons[0] app(result, "}") of tyVar, tyPtr, tyRef: if mapType(t) == etyBaseIndex: result = putToSeq("[null, 0]", indirect) else: result = putToSeq("null", indirect) of tySequence, tyString, tyCString, tyPointer, tyProc: result = putToSeq("null", indirect) else: internalError("createVar: " & $t.kind) result = nil proc isIndirect(v: PSym): bool = result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and v.kind notin {skProc, skConverter, skMethod, skIterator} proc genVarInit(p: var TProc, v: PSym, n: PNode, r: var TCompRes) = var a: TCompRes s: PRope if n.kind == nkEmpty: appf(r.com, "var $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 a.com != nil: appf(r.com, "$1;$n", [a.com]) if needsNoCopy(n): s = a.res else: useMagic(p, "NimCopy") s = ropef("NimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)]) of etyBaseIndex: if (a.kind != etyBaseIndex): InternalError(n.info, "genVarInit") if {sfAddrTaken, sfGlobal} * v.flags != {}: appf(r.com, "var $1 = [$2, $3];$n", [v.loc.r, a.com, a.res]) else: appf(r.com, "var $1 = $2; var $1_Idx = $3;$n", [v.loc.r, a.com, a.res]) return else: if a.com != nil: appf(r.com, "$1;$n", [a.com]) s = a.res if isIndirect(v): appf(r.com, "var $1 = [$2];$n", [v.loc.r, s]) else: appf(r.com, "var $1 = $2;$n", [v.loc.r, s]) proc genVarStmt(p: var TProc, n: PNode, r: var TCompRes) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue assert(a.kind == nkIdentDefs) assert(a.sons[0].kind == nkSym) var v = a.sons[0].sym if lfNoDecl in v.loc.flags: continue genLineDir(p, a, r) genVarInit(p, v, a.sons[2], r) proc genConstant(p: var TProc, c: PSym, r: var TCompRes) = if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id): genLineDir(p, c.ast, r) genVarInit(p, c, c.ast, r) when false: proc genConstStmt(p: var TProc, n: PNode, r: var TCompRes) = genLineDir(p, n, r) for i in countup(0, sonsLen(n) - 1): if n.sons[i].kind == nkCommentStmt: continue assert(n.sons[i].kind == nkConstDef) var c = n.sons[i].sons[0].sym if c.ast != nil and c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags: genLineDir(p, n.sons[i], r) genVarInit(p, c, c.ast, r) proc genNew(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n.sons[1], a) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] if a.com != nil: appf(r.com, "$1;$n", [a.com]) appf(r.com, "$1 = $2;$n", [a.res, createVar(p, t, true)]) proc genNewSeq(p: var TProc, n: PNode, r: var TCompRes) = var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) if x.com != nil: appf(r.com, "$1;$n", [x.com]) if y.com != nil: appf(r.com, "$1;$n", [y.com]) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] appf(r.com, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ x.res, y.res, createVar(p, t, false)]) proc genOrd(p: var TProc, 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)") else: InternalError(n.info, "genOrd") proc genConStrStr(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n.sons[1], a) r.com = mergeExpr(r.com, a.com) if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar: r.res.app(ropef("[$1].concat(", [a.res])) else: r.res.app(ropef("($1.slice(0,-1)).concat(", [a.res])) for i in countup(2, sonsLen(n) - 2): gen(p, n.sons[i], a) r.com = mergeExpr(r.com, a.com) if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar: r.res.app(ropef("[$1],", [a.res])) else: r.res.app(ropef("$1.slice(0,-1),", [a.res])) gen(p, n.sons[sonsLen(n) - 1], a) r.com = mergeExpr(r.com, a.com) if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar: r.res.app(ropef("[$1, 0])", [a.res])) else: r.res.app(ropef("$1)", [a.res])) proc genRepr(p: var TProc, 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.res = ropef("cstrToNimstr($1.node.sons[$2].name)", [genTypeInfo(p, t), r.res]) else: # XXX: internalError(n.info, "genRepr: Not implemented") proc genOf(p: var TProc, 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 = ropef("($1.m_type == $2)", [x.res, genTypeInfo(p, t)]) else: useMagic(p, "isObj") r.res = ropef("isObj($1.m_type, $2)", [x.res, genTypeInfo(p, t)]) r.com = mergeExpr(r.com, x.com) proc genReset(p: var TProc, n: PNode, r: var TCompRes) = var x: TCompRes useMagic(p, "genericReset") gen(p, n.sons[1], x) r.res = ropef("$1 = genericReset($1, $2)", [x.res, genTypeInfo(p, n.sons[1].typ)]) r.com = mergeExpr(r.com, x.com) proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes line, filen: PRope 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, r) 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 mPred: # XXX: range checking? if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 - $2") else: binaryExpr(p, n, r, "subInt", "subInt($1, $2)") of mSucc: # XXX: range checking? if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 - $2") else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)") of mAppendStrCh: binaryStmt(p, n, r, "addChar", "$1 = addChar($1, $2)") of mAppendStrStr: if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: binaryStmt(p, n, r, "", "$1 += $2") else: binaryStmt(p, n, r, "", "$1 = ($1.slice(0,-1)).concat($2)") # XXX: make a copy of $2, because of Javascript's sucking semantics of mAppendSeqElem: binaryStmt(p, n, r, "", "$1.push($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, r) of mSizeOf: r.res = toRope(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.length-1)") of mLengthSeq, mLengthOpenArray, mLengthArray: unaryExpr(p, n, r, "", "$1.length") of mHigh: if skipTypes(n.sons[0].typ, abstractVar).kind == tyString: unaryExpr(p, n, r, "", "($1.length-2)") else: unaryExpr(p, n, r, "", "($1.length-1)") of mInc: if not (optOverflowCheck in p.Options): binaryStmt(p, n, r, "", "$1 += $2") else: binaryStmt(p, n, r, "addInt", "$1 = addInt($1, $2)") of ast.mDec: if not (optOverflowCheck in p.Options): binaryStmt(p, n, r, "", "$1 -= $2") else: binaryStmt(p, n, r, "subInt", "$1 = subInt($1, $2)") of mSetLengthStr: binaryStmt(p, n, r, "", "$1.length = ($2)-1") of mSetLengthSeq: binaryStmt(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: binaryStmt(p, n, r, "", "$1[$2] = true") of mExcl: binaryStmt(p, n, r, "", "delete $1[$2]") of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)") of mNLen..mNError: localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s) of mNewSeq: genNewSeq(p, n, r) of mOf: genOf(p, n, r) of mReset: genReset(p, n, r) of mEcho: genEcho(p, n, r) of mSlurp, mStaticExec: localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s) else: genCall(p, n, r) #else internalError(e.info, 'genMagic: ' + magicToStr[op]); proc genSetConstr(p: var TProc, n: PNode, r: var TCompRes) = var a, b: TCompRes useMagic(p, "SetConstr") r.res = toRope("SetConstr(") for i in countup(0, sonsLen(n) - 1): if i > 0: app(r.res, ", ") var it = n.sons[i] if it.kind == nkRange: gen(p, it.sons[0], a) gen(p, it.sons[1], b) r.com = mergeExpr(r.com, mergeExpr(a.com, b.com)) appf(r.res, "[$1, $2]", [a.res, b.res]) else: gen(p, it, a) r.com = mergeExpr(r.com, a.com) app(r.res, a.res) app(r.res, ")") proc genArrayConstr(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = toRope("[") for i in countup(0, sonsLen(n) - 1): if i > 0: app(r.res, ", ") gen(p, n.sons[i], a) r.com = mergeExpr(r.com, a.com) app(r.res, a.res) app(r.res, "]") proc genTupleConstr(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = toRope("{") for i in countup(0, sonsLen(n) - 1): if i > 0: app(r.res, ", ") var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] gen(p, it, a) r.com = mergeExpr(r.com, a.com) appf(r.res, "Field$1: $2", [i.toRope, a.res]) r.res.app("}") proc genConv(p: var TProc, 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) and (src.kind == tyBool): r.res = ropef("(($1)? 1:0)", [r.res]) proc upConv(p: var TProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) # XXX proc genRangeChck(p: var TProc, 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) r.com = mergeExpr(r.com, mergeExpr(a.com, b.com)) useMagic(p, "chckRange") r.res = ropef("chckRange($1, $2, $3)", [r.res, a.res, b.res]) proc convStrToCStr(p: var TProc, 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 = ropef("toJSStr($1)", [r.res]) proc convCStrToStr(p: var TProc, 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 = ropef("cstrToNimstr($1)", [r.res]) proc genReturnStmt(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes if p.procDef == nil: InternalError(n.info, "genReturnStmt") p.BeforeRetNeeded = true if (n.sons[0].kind != nkEmpty): genStmt(p, n.sons[0], a) if a.com != nil: appf(r.com, "$1;$n", mergeStmt(a)) else: genLineDir(p, n, r) finishTryStmt(p, r, p.nestedTryStmts) app(r.com, "break BeforeRet;" & tnl) proc genProcBody(p: var TProc, prc: PSym, r: TCompRes): PRope = if optStackTrace in prc.options: result = ropef("var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" & "framePtr = F;$n", [makeJSString(prc.owner.name.s & '.' & prc.name.s), makeJSString(toFilename(prc.info))]) else: result = nil if p.beforeRetNeeded: appf(result, "BeforeRet: do {$n$1} while (false); $n", [mergeStmt(r)]) else: app(result, mergeStmt(r)) if prc.typ.callConv == ccSysCall: result = ropef("try {$n$1} catch (e) {$n" & " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}", [result]) if optStackTrace in prc.options: app(result, "framePtr = framePtr.prev;" & tnl) proc genProc(oldProc: var TProc, prc: PSym, r: var TCompRes) = var p: TProc resultSym: PSym name, returnStmt, resultAsgn, header: PRope a: TCompRes #if gVerbosity >= 3: # echo "BEGIN generating code for: " & prc.name.s initProc(p, oldProc.g, oldProc.module, prc.ast, prc.options) 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 = ropef("var $1 = $2;$n", [mangleName(resultSym), createVar(p, resultSym.typ, isIndirect(resultSym))]) gen(p, prc.ast.sons[resultPos], a) if a.com != nil: appf(returnStmt, "$1;$n", [a.com]) returnStmt = ropef("return $1;$n", [a.res]) genStmt(p, prc.getBody, r) r.com = ropef("function $1($2) {$n$3$4$5}$n", [name, header, resultAsgn, genProcBody(p, prc, r), returnStmt]) r.res = nil #if gVerbosity >= 3: # echo "END generated code for: " & prc.name.s proc genStmtListExpr(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes # watch out this trick: ``function () { stmtList; return expr; }()`` r.res = toRope("function () {") for i in countup(0, sonsLen(n) - 2): genStmt(p, n.sons[i], a) app(r.res, mergeStmt(a)) gen(p, lastSon(n), a) if a.com != nil: appf(r.res, "$1;$n", [a.com]) appf(r.res, "return $1; }()", [a.res]) proc genStmt(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes r.kind = etyNone r.com = nil r.res = nil case n.kind of nkNilLit, nkEmpty: nil of nkStmtList: for i in countup(0, sonsLen(n) - 1): genStmt(p, n.sons[i], a) app(r.com, mergeStmt(a)) of nkBlockStmt: genBlock(p, n, r) of nkIfStmt: genIfStmt(p, n, r) of nkWhileStmt: genWhileStmt(p, n, r) of nkVarSection, nkLetSection: genVarStmt(p, n, r) of nkConstSection: nil of nkForStmt, nkParForStmt: internalError(n.info, "for statement not eliminated") of nkCaseStmt: genCaseStmt(p, n, r) of nkReturnStmt: genReturnStmt(p, n, r) of nkBreakStmt: genBreakStmt(p, n, r) of nkAsgn: genAsgn(p, n, r) of nkFastAsgn: genFastAsgn(p, n, r) of nkDiscardStmt: if n.sons[0].kind != nkEmpty: genLineDir(p, n, r) gen(p, n.sons[0], r) app(r.res, ';' & tnl) of nkAsmStmt: genAsmStmt(p, n, r) of nkTryStmt: genTryStmt(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n, r) of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: nil of nkProcDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: var r2: TCompRes genSym(p, n.sons[namePos], r2) of nkGotoState, nkState: internalError(n.info, "first class iterators not implemented") else: genLineDir(p, n, r) gen(p, n, r) app(r.res, ';' & tnl) proc gen(p: var TProc, n: PNode, r: var TCompRes) = var f: BiggestFloat r.kind = etyNone r.com = nil r.res = nil case n.kind of nkSym: genSym(p, n, r) of nkCharLit..nkInt64Lit: r.res = toRope(n.intVal) of nkNilLit: if mapType(n.typ) == etyBaseIndex: r.kind = etyBaseIndex r.com = toRope"null" r.res = toRope"0" else: r.res = toRope"null" of nkStrLit..nkTripleStrLit: if skipTypes(n.typ, abstractVarRange).kind == tyString: useMagic(p, "cstrToNimstr") r.res = ropef("cstrToNimstr($1)", [makeJSString(n.strVal)]) else: r.res = makeJSString(n.strVal) of nkFloatLit..nkFloat64Lit: f = n.floatVal if f != f: r.res = toRope"NaN" elif f == 0.0: r.res = toRope"0.0" elif f == 0.5 * f: if f > 0.0: r.res = toRope"Infinity" else: r.res = toRope"-Infinity" else: r.res = toRope(f.ToStrMaxPrecision) of nkBlockExpr: genBlock(p, n, r) of nkIfExpr: genIfExpr(p, n, r) 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 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 nkStmtListExpr: genStmtListExpr(p, n, r) of nkEmpty: nil 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 or isGenericRoutine(s): nil elif not p.g.generatedSyms.containsOrIncl(s.id): var r2: TCompRes genProc(p, s, r2) app(r.com, mergeStmt(r2)) of nkMetaNode: gen(p, n.sons[0], r) of nkType: r.res = genTypeInfo(p, n.typ) 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(): PRope = result = ropef("/* Generated by the Nimrod Compiler v$1 */$n" & "/* (c) 2013 Andreas Rumpf */$n$n" & "$nvar Globals = this;$n" & "var framePtr = null;$n" & "var excHandler = null;$n", [toRope(versionAsString)]) proc genModule(p: var TProc, n: PNode, r: var TCompRes) = genStmt(p, n, r) if optStackTrace in p.options: r.com = ropef("var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" & "framePtr = F;$n" & "$3" & "framePtr = framePtr.prev;$n", [ makeJSString("module " & p.module.module.name.s), makeJSString(toFilename(p.module.module.info)), r.com]) proc myProcess(b: PPassContext, n: PNode): PNode = if passes.skipCodegen(n): return n var p: TProc r: TCompRes result = n var m = BModule(b) if m.module == nil: InternalError(n.info, "myProcess") initProc(p, globals, m, nil, m.module.options) genModule(p, n, r) app(p.g.code, p.data) app(p.g.code, mergeStmt(r)) 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: for prc in globals.forwarded: if not globals.generatedSyms.containsOrIncl(prc.id): var p: TProc r: TCompRes initProc(p, globals, m, nil, m.module.options) genProc(p, prc, r) app(p.g.code, mergeStmt(r)) 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: TProc r: TCompRes initProc(p, globals, m, nil, m.module.options) genProc(p, prc, r) app(p.g.code, mergeStmt(r)) # write the file: var code = con(globals.typeInfo, globals.code) var outfile = changeFileExt(completeCFilePath(m.module.filename), "js") discard writeRopeIfNotEqual(con(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)