summary refs log tree commit diff stats
path: root/compiler/jsgen.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/jsgen.nim')
-rwxr-xr-xcompiler/jsgen.nim1669
1 files changed, 1669 insertions, 0 deletions
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
new file mode 100755
index 000000000..94b662b48
--- /dev/null
+++ b/compiler/jsgen.nim
@@ -0,0 +1,1669 @@
+#
+#
+#           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 .. <typ.len:
+    if i > 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.. <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..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)