#
#
# The Nimrod Compiler
# (c) Copyright 2012 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
proc ecmasgenPass*(): TPass
# implementation
type
TEcmasGen = object of TPassContext
filename: string
module: PSym
BModule = ref TEcmasGen
TEcmasTypeKind = enum # necessary JS "types"
etyNone, # no type
etyNull, # null type
etyProc, # proc type
etyBool, # bool type
etyInt, # Ecmascript's int
etyFloat, # Ecmascript's float
etyString, # Ecmascript's string
etyObject, # Ecmascript's reference to an object
etyBaseIndex # base + index needed
TCompRes{.final.} = object
kind: TEcmasTypeKind
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): TEcmasTypeKind =
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 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, makeCString(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)), makeCString(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 genEnumInfo(p: var TProc, typ: PType, name: PRope) =
var
s, n: PRope
length: int
field: PSym
length = sonsLen(typ.n)
s = nil
for i in countup(0, length - 1):
if (typ.n.sons[i].kind != nkSym): InternalError(typ.n.info, "genEnumInfo")
field = typ.n.sons[i].sym
if i > 0: app(s, ", " & tnl)
appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
[toRope(field.position), name, makeCString(field.name.s)])
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, tyTuple: genObjectInfo(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, makeCString(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, "toEcmaStr")
appf(r.com, "switch (toEcmaStr($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, "ecmasgen.genCaseStmt")
appf(r.com, "case $1: ", [cond.res])
Inc(v.intVal)
else:
gen(p, e, cond)
if cond.com != nil: internalError(e.info, "ecmasgen.genCaseStmt")
if stringSwitch:
case e.kind
of nkStrLit..nkTripleStrLit: appf(r.com, "case $1: ",
[makeCString(e.strVal)])
else: InternalError(e.info, "ecmasgen.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, "ecmasgen.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, "ecmasgen: 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 = makeCString("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 = makeCString(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 = makeCString(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): 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 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, ", ")
var a: TCompRes
gen(p, n.sons[i], a)
if a.kind == etyBaseIndex:
app(r.res, a.com)
app(r.res, ", ")
app(r.res, a.res)
else:
app(r.res, mergeExpr(a))
app(r.res, ")")
proc genCall(p: var TProc, n: PNode, r: var TCompRes) =
gen(p, n.sons[0], r)
genArgs(p, n, r)
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.. <t.sonslen:
if i > 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..tyInt64:
unaryExpr(p, n, r, "", "reprInt($1)")
of tyEnum, tyOrdinal:
binaryExpr(p, n, r, "", "reprEnum($1, $2)")
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 ECMAScript'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, "toEcmaStr")
r.res = ropef("toEcmaStr($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", [makeCString(prc.owner.name.s & '.' & prc.name.s),
makeCString(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,
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)", [makeCString(n.strVal)])
else:
r.res = makeCString(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)
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 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, filename: string): BModule =
new(result)
result.filename = filename
result.module = module
if globals == nil: globals = newGlobals()
proc genHeader(): PRope =
result = ropef("/* Generated by the Nimrod Compiler v$1 */$n" &
"/* (c) 2012 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", [
makeCString("module " & p.module.module.name.s),
makeCString(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.filename), "js")
discard writeRopeIfNotEqual(con(genHeader(), code), outfile)
proc myOpenCached(s: PSym, filename: string, rd: PRodReader): PPassContext =
InternalError("symbol files are not possible with the Ecmas code generator")
result = nil
proc myOpen(s: PSym, filename: string): PPassContext =
result = newModule(s, filename)
proc ecmasgenPass(): TPass =
InitPass(result)
result.open = myOpen
result.close = myClose
result.openCached = myOpenCached
result.process = myProcess