summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorzah <zahary@gmail.com>2017-06-03 13:45:10 +0300
committerAndreas Rumpf <rumpf_a@web.de>2017-06-03 12:45:10 +0200
commit39aef12446b0e7f7dcc2222f358490a7ed2db685 (patch)
tree9f383710bca3790407ab8edc088448981d0e3e31
parenteb8e267ff63eec0dd5279186a57d8af59f26c696 (diff)
downloadNim-39aef12446b0e7f7dcc2222f358490a7ed2db685.tar.gz
review and merge zahary's work (#5849)
* proper indentation for the generated JS code
* improved dead-code elimination for JavaScript
* test the JS dead-code elimination

A new test spec has been added - "maxcodesize". It specifies the
maximum size of the generated code in bytes.
-rw-r--r--compiler/cgen.nim12
-rw-r--r--compiler/jsgen.nim317
-rw-r--r--compiler/options.nim3
-rw-r--r--lib/system.nim130
-rw-r--r--tests/js/tjshello.nim10
-rw-r--r--tests/testament/specs.nim3
-rw-r--r--tests/testament/tester.nim28
7 files changed, 312 insertions, 191 deletions
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 89d293989..c67dd6581 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -138,6 +138,13 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
 template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped =
   ropecg(m, fmt, args)
 
+var indent = "\t".rope
+
+proc indentLine(p: BProc, r: Rope): Rope =
+  result = r
+  for i in countup(0, p.blocks.len-1):
+    prepend(result, indent)
+
 proc appcg(m: BModule, c: var Rope, frmt: FormatStr,
            args: varargs[Rope]) =
   add(c, ropecg(m, frmt, args))
@@ -150,11 +157,6 @@ proc appcg(p: BProc, s: TCProcSection, frmt: FormatStr,
            args: varargs[Rope]) =
   add(p.s(s), ropecg(p.module, frmt, args))
 
-var indent = "\t".rope
-proc indentLine(p: BProc, r: Rope): Rope =
-  result = r
-  for i in countup(0, p.blocks.len-1): prepend(result, indent)
-
 proc line(p: BProc, s: TCProcSection, r: Rope) =
   add(p.s(s), indentLine(p, r))
 
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index ee35356c9..5aa74d292 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -94,12 +94,39 @@ type
     target: TTarget # duplicated here for faster dispatching
     unique: int    # for temp identifier generation
     blocks: seq[TBlock]
+    extraIndent: int
     up: PProc     # up the call chain; required for closure support
     declaredGlobals: IntSet
 
 template `|`(a, b: untyped): untyped {.dirty.} =
   (if p.target == targetJS: a else: b)
 
+var indent = "\t".rope
+
+proc indentLine(p: PProc, r: Rope): Rope =
+  result = r
+  var p = p
+  while true:
+    for i in countup(0, p.blocks.len - 1 + p.extraIndent):
+      prepend(result, indent)
+    if p.up == nil or p.up.prc != p.prc.owner:
+      break
+    p = p.up
+
+template line(p: PProc, added: string) =
+  add(p.body, indentLine(p, rope(added)))
+
+template line(p: PProc, added: Rope) =
+  add(p.body, indentLine(p, added))
+
+template lineF(p: PProc, frmt: FormatStr, args: varargs[Rope]) =
+  add(p.body, indentLine(p, ropes.`%`(frmt, args)))
+
+template nested(p, body) =
+  inc p.extraIndent
+  body
+  dec p.extraIndent
+
 proc newGlobals(): PGlobals =
   new(result)
   result.forwarded = @[]
@@ -129,7 +156,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
     module: module,
     procDef: procDef,
     g: globals,
-    target: module.target)
+    target: module.target,
+    extraIndent: int(procDef != nil))
   if procDef != nil: result.prc = procDef.sons[namePos].sym
   if result.target == targetPHP:
     result.declaredGlobals = initIntSet()
@@ -290,7 +318,7 @@ proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
   if p.target == targetJS:
     result = "Tmp$1" % [rope(p.unique)]
     if defineInLocals:
-      addf(p.locals, "var $1;$n", [result])
+      add(p.locals, p.indentLine("var $1;$n" % [result]))
   else:
     result = "$$Tmp$1" % [rope(p.unique)]
 
@@ -315,9 +343,11 @@ proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
     #     tmp = b
     # tmp
     gen(p, a, x)
-    p.body.addf("if (!$1) $2 = false; else {", [x.rdLoc, r.rdLoc])
-    gen(p, b, y)
-    p.body.addf("$2 = $1; }", [y.rdLoc, r.rdLoc])
+    lineF(p, "if (!$1) $2 = false; else {", [x.rdLoc, r.rdLoc])
+    p.nested:
+      gen(p, b, y)
+      lineF(p, "$2 = $1;", [y.rdLoc, r.rdLoc])
+    line(p, "}")
 
 proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
   assert r.kind == resNone
@@ -331,9 +361,11 @@ proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
     r.res = p.getTemp
     r.kind = resVal
     gen(p, a, x)
-    p.body.addf("if ($1) $2 = true; else {", [x.rdLoc, r.rdLoc])
-    gen(p, b, y)
-    p.body.addf("$2 = $1; }", [y.rdLoc, r.rdLoc])
+    lineF(p, "if ($1) $2 = true; else {", [x.rdLoc, r.rdLoc])
+    p.nested:
+      gen(p, b, y)
+      lineF(p, "$2 = $1;", [y.rdLoc, r.rdLoc])
+    line(p, "}")
 
 type
   TMagicFrmt = array[0..3, string]
@@ -415,12 +447,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["", "", "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)+\"\")"],
+    ["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"]]
 
@@ -475,18 +505,18 @@ proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   r.res = frmt % [r.rdLoc]
   r.kind = resExpr
 
-proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) =
+proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   var
     x, y: TCompRes
   let i = ord(optOverflowCheck notin p.options)
-  useMagic(p, ops[op][i])
+  useMagic(p, jsOps[op][i])
   if sonsLen(n) > 2:
     gen(p, n.sons[1], x)
     gen(p, n.sons[2], y)
-    r.res = ops[op][i + 2] % [x.rdLoc, y.rdLoc]
+    r.res = jsOps[op][i + 2] % [x.rdLoc, y.rdLoc]
   else:
     gen(p, n.sons[1], r)
-    r.res = ops[op][i + 2] % [r.rdLoc]
+    r.res = jsOps[op][i + 2] % [r.rdLoc]
 
 proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   case op
@@ -501,7 +531,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
       gen(p, n.sons[2], y)
       r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc]
     else:
-      arithAux(p, n, r, op, jsOps)
+      arithAux(p, n, r, op)
   of mModI:
     if p.target == targetPHP:
       var x, y: TCompRes
@@ -509,7 +539,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
       gen(p, n.sons[2], y)
       r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc]
     else:
-      arithAux(p, n, r, op, jsOps)
+      arithAux(p, n, r, op)
   of mShrI:
     var x, y: TCompRes
     gen(p, n.sons[1], x)
@@ -534,9 +564,9 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
       else:
         gen(p, n.sons[1], r)
     else:
-      arithAux(p, n, r, op, jsOps)
+      arithAux(p, n, r, op)
   else:
-    arithAux(p, n, r, op, jsOps)
+    arithAux(p, n, r, op)
   r.kind = resExpr
 
 proc hasFrameInfo(p: PProc): bool =
@@ -546,14 +576,14 @@ proc hasFrameInfo(p: PProc): bool =
 proc genLineDir(p: PProc, n: PNode) =
   let line = toLinenumber(n.info)
   if optLineDir in p.options:
-    addf(p.body, "// line $2 \"$1\"$n",
+    lineF(p, "// line $2 \"$1\"$n",
          [rope(toFilename(n.info)), rope(line)])
   if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and
       ((p.prc == nil) or sfPure notin p.prc.flags):
     useMagic(p, "endb")
-    addf(p.body, "endb($1);$n", [rope(line)])
+    lineF(p, "endb($1);$n", [rope(line)])
   elif hasFrameInfo(p):
-    addf(p.body, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)])
+    lineF(p, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)])
 
 proc genWhileStmt(p: PProc, n: PNode) =
   var
@@ -566,20 +596,20 @@ proc genWhileStmt(p: PProc, n: PNode) =
   p.blocks[length].id = -p.unique
   p.blocks[length].isLoop = true
   let labl = p.unique.rope
-  addf(p.body, "L$1: while (true) {$n" | "while (true) {$n", [labl])
-  gen(p, n.sons[0], cond)
-  addf(p.body, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n",
+  lineF(p, "L$1: while (true) {$n" | "while (true) {$n", [labl])
+  p.nested: gen(p, n.sons[0], cond)
+  lineF(p, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n",
        [cond.res, labl])
-  genStmt(p, n.sons[1])
-  addf(p.body, "}$n" | "}L$#:;$n", [labl])
+  p.nested: genStmt(p, n.sons[1])
+  lineF(p, "}$n" | "}L$#:;$n", [labl])
   setLen(p.blocks, length)
 
 proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
   if src.kind != resNone:
     if dest.kind != resNone:
-      p.body.addf("$1 = $2;$n", [dest.rdLoc, src.rdLoc])
+      lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
     else:
-      p.body.addf("$1;$n", [src.rdLoc])
+      lineF(p, "$1;$n", [src.rdLoc])
     src.kind = resNone
     src.res = nil
 
@@ -620,8 +650,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   var tmpFramePtr = rope"F"
   if optStackTrace notin p.options:
     tmpFramePtr = p.getTemp(true)
-    add(p.body, tmpFramePtr & " = framePtr;" & tnl)
-  addf(p.body, "try {$n", [])
+    line(p, tmpFramePtr & " = framePtr;" & tnl)
+  lineF(p, "try {$n", [])
   if p.target == targetPHP and p.globals == nil:
       p.globals = "global $lastJSError; global $prevJSError;".rope
   var a: TCompRes
@@ -632,18 +662,18 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   if p.target == targetJS and catchBranchesExist:
     addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" &
         " lastJSError = EXC;$n --excHandler;$n", [])
-    add(p.body, "framePtr = $1;$n" % [tmpFramePtr])
+    line(p, "framePtr = $1;$n" % [tmpFramePtr])
   elif p.target == targetPHP:
-    addf(p.body, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", [])
+    lineF(p, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", [])
   while i < length and n.sons[i].kind == nkExceptBranch:
     let blen = sonsLen(n.sons[i])
     if blen == 1:
       # general except section:
       generalCatchBranchExists = true
-      if i > 1: addf(p.body, "else {$n", [])
+      if i > 1: lineF(p, "else {$n", [])
       gen(p, n.sons[i].sons[0], a)
       moveInto(p, a, r)
-      if i > 1: addf(p.body, "}$n", [])
+      if i > 1: lineF(p, "}$n", [])
     else:
       var orExpr: Rope = nil
       useMagic(p, "isObj")
@@ -653,30 +683,32 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
         if orExpr != nil: add(orExpr, "||")
         addf(orExpr, "isObj($2lastJSError.m_type, $1)",
              [genTypeInfo(p, n.sons[i].sons[j].typ), dollar])
-      if i > 1: add(p.body, "else ")
-      addf(p.body, "if ($1lastJSError && ($2)) {$n", [dollar, orExpr])
+      if i > 1: line(p, "else ")
+      lineF(p, "if ($1lastJSError && ($2)) {$n", [dollar, orExpr])
       gen(p, n.sons[i].sons[blen - 1], a)
       moveInto(p, a, r)
-      addf(p.body, "}$n", [])
+      lineF(p, "}$n", [])
     inc(i)
   if catchBranchesExist:
     if not generalCatchBranchExists:
       useMagic(p, "reraiseException")
-      add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl)
+      line(p, "else {" & tnl)
+      line(p, indent & "reraiseException();" & tnl)
+      line(p, "}" & tnl)
     addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
   if p.target == targetJS:
-    add(p.body, "} finally {" & tnl)
-    add(p.body, "framePtr = $1;$n" % [tmpFramePtr])
+    line(p, "} finally {" & tnl)
+    line(p, "framePtr = $1;$n" % [tmpFramePtr])
   if p.target == targetPHP:
     # XXX ugly hack for PHP codegen
-    add(p.body, "}" & tnl)
+    line(p, "}" & tnl)
   if i < length and n.sons[i].kind == nkFinally:
     genStmt(p, n.sons[i].sons[0])
   if p.target == targetPHP:
     # XXX ugly hack for PHP codegen
-    add(p.body, "if($lastJSError) throw($lastJSError);" & tnl)
+    line(p, "if($lastJSError) throw($lastJSError);" & tnl)
   if p.target == targetJS:
-    add(p.body, "}" & tnl)
+    line(p, "}" & tnl)
 
 proc genRaiseStmt(p: PProc, n: PNode) =
   genLineDir(p, n)
@@ -685,11 +717,11 @@ proc genRaiseStmt(p: PProc, n: PNode) =
     gen(p, n.sons[0], a)
     let typ = skipTypes(n.sons[0].typ, abstractPtrs)
     useMagic(p, "raiseException")
-    addf(p.body, "raiseException($1, $2);$n",
-         [a.rdLoc, makeJSString(typ.sym.name.s)])
+    lineF(p, "raiseException($1, $2);$n",
+             [a.rdLoc, makeJSString(typ.sym.name.s)])
   else:
     useMagic(p, "reraiseException")
-    add(p.body, "reraiseException();" & tnl)
+    line(p, "reraiseException();" & tnl)
 
 proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
   var
@@ -699,9 +731,9 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
   let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
   if stringSwitch and p.target == targetJS:
     useMagic(p, "toJSStr")
-    addf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc])
+    lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc])
   else:
-    addf(p.body, "switch ($1) {$n", [cond.rdLoc])
+    lineF(p, "switch ($1) {$n", [cond.rdLoc])
   if not isEmptyType(n.typ):
     r.kind = resVal
     r.res = getTemp(p)
@@ -715,27 +747,29 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
           var v = copyNode(e.sons[0])
           while v.intVal <= e.sons[1].intVal:
             gen(p, v, cond)
-            addf(p.body, "case $1: ", [cond.rdLoc])
+            lineF(p, "case $1:$n", [cond.rdLoc])
             inc(v.intVal)
         else:
           if stringSwitch:
             case e.kind
-            of nkStrLit..nkTripleStrLit: addf(p.body, "case $1: ",
+            of nkStrLit..nkTripleStrLit: lineF(p, "case $1:$n",
                 [makeJSString(e.strVal, false)])
             else: internalError(e.info, "jsgen.genCaseStmt: 2")
           else:
             gen(p, e, cond)
-            addf(p.body, "case $1: ", [cond.rdLoc])
-      gen(p, lastSon(it), stmt)
-      moveInto(p, stmt, r)
-      addf(p.body, "$nbreak;$n", [])
+            lineF(p, "case $1:$n", [cond.rdLoc])
+      p.nested:
+        gen(p, lastSon(it), stmt)
+        moveInto(p, stmt, r)
+        lineF(p, "break;$n", [])
     of nkElse:
-      addf(p.body, "default: $n", [])
-      gen(p, it.sons[0], stmt)
-      moveInto(p, stmt, r)
-      addf(p.body, "break;$n", [])
+      lineF(p, "default: $n", [])
+      p.nested:
+        gen(p, it.sons[0], stmt)
+        moveInto(p, stmt, r)
+        lineF(p, "break;$n", [])
     else: internalError(it.info, "jsgen.genCaseStmt")
-  addf(p.body, "}$n", [])
+  lineF(p, "}$n", [])
 
 proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
   inc(p.unique)
@@ -746,13 +780,13 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
     var sym = n.sons[0].sym
     sym.loc.k = locOther
     sym.position = idx+1
+  let labl = p.unique
+  lineF(p, "L$1: do {$n" | "", [labl.rope])
   setLen(p.blocks, idx + 1)
   p.blocks[idx].id = - p.unique # negative because it isn't used yet
-  let labl = p.unique
-  addf(p.body, "L$1: do {$n" | "", [labl.rope])
   gen(p, n.sons[1], r)
-  addf(p.body, "} while(false);$n" | "$nL$#:;$n", [labl.rope])
   setLen(p.blocks, idx)
+  lineF(p, "} while(false);$n" | "$nL$#:;$n", [labl.rope])
 
 proc genBreakStmt(p: PProc, n: PNode) =
   var idx: int
@@ -770,19 +804,23 @@ proc genBreakStmt(p: PProc, n: PNode) =
     if idx < 0 or not p.blocks[idx].isLoop:
       internalError(n.info, "no loop to break")
   p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
-  addf(p.body, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)])
+  lineF(p, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)])
 
 proc genAsmOrEmitStmt(p: PProc, n: PNode) =
   genLineDir(p, n)
+  p.body.add p.indentLine(nil)
   for i in countup(0, sonsLen(n) - 1):
     case n.sons[i].kind
-    of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal)
+    of nkStrLit..nkTripleStrLit:
+      p.body.add(n.sons[i].strVal)
     of nkSym:
       let v = n.sons[i].sym
       if p.target == targetPHP and v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}:
-        add(p.body, "$")
-      add(p.body, mangleName(v, p.target))
-    else: internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()")
+        p.body.add "$"
+      p.body.add mangleName(v, p.target)
+    else:
+      internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()")
+  p.body.add tnl
 
 proc genIf(p: PProc, n: PNode, r: var TCompRes) =
   var cond, stmt: TCompRes
@@ -794,18 +832,18 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) =
     let it = n.sons[i]
     if sonsLen(it) != 1:
       if i > 0:
-        addf(p.body, "else {$n", [])
+        lineF(p, "else {$n", [])
         inc(toClose)
-      gen(p, it.sons[0], cond)
-      addf(p.body, "if ($1) {$n", [cond.rdLoc])
+      p.nested: gen(p, it.sons[0], cond)
+      lineF(p, "if ($1) {$n", [cond.rdLoc])
       gen(p, it.sons[1], stmt)
     else:
       # else part:
-      addf(p.body, "else {$n", [])
-      gen(p, it.sons[0], stmt)
+      lineF(p, "else {$n", [])
+      p.nested: gen(p, it.sons[0], stmt)
     moveInto(p, stmt, r)
-    addf(p.body, "}$n", [])
-  add(p.body, repeat('}', toClose) & tnl)
+    lineF(p, "}$n", [])
+  line(p, repeat('}', toClose) & tnl)
 
 proc generateHeader(p: PProc, typ: PType): Rope =
   result = nil
@@ -854,7 +892,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
     gen(p, x[0], a)
     gen(p, x[1], b)
     gen(p, y, c)
-    addf(p.body, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc])
+    lineF(p, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc])
     return
 
   var xtyp = mapType(p, x.typ)
@@ -862,7 +900,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
   if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject:
     gen(p, x.sons[0], a)
     let tmp = p.getTemp(false)
-    addf(p.body, "var $1 = $2;$n", [tmp, a.rdLoc])
+    lineF(p, "var $1 = $2;$n", [tmp, a.rdLoc])
     a.res = "$1[0][$1[1]]" % [tmp]
   else:
     gen(p, x, a)
@@ -876,29 +914,29 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
   case xtyp
   of etySeq:
     if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
-      addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
+      lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
     else:
       useMagic(p, "nimCopy")
-      addf(p.body, "$1 = nimCopy(null, $2, $3);$n",
-           [a.rdLoc, b.res, genTypeInfo(p, y.typ)])
+      lineF(p, "$1 = nimCopy(null, $2, $3);$n",
+               [a.rdLoc, b.res, genTypeInfo(p, y.typ)])
   of etyObject:
     if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
-      addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
+      lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
     else:
       useMagic(p, "nimCopy")
-      addf(p.body, "nimCopy($1, $2, $3);$n",
-           [a.res, b.res, genTypeInfo(p, y.typ)])
+      lineF(p, "nimCopy($1, $2, $3);$n",
+               [a.res, b.res, genTypeInfo(p, y.typ)])
   of etyBaseIndex:
     if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
       if y.kind == nkCall:
         let tmp = p.getTemp(false)
-        addf(p.body, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
+        lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
       else:
         internalError(x.info, "genAsgn")
     else:
-      addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
+      lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
   else:
-    addf(p.body, "$1 = $2;$n", [a.res, b.res])
+    lineF(p, "$1 = $2;$n", [a.res, b.res])
 
 proc genAsgn(p: PProc, n: PNode) =
   genLineDir(p, n)
@@ -917,12 +955,13 @@ proc genSwap(p: PProc, n: PNode) =
     let tmp2 = p.getTemp(false)
     if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
       internalError(n.info, "genSwap")
-    addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" |
-                 "$1 = $2; $2 = $3; $3 = $1;$n", [
-                 tmp, a.address, b.address])
+    lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n" |
+             "$1 = $2; $2 = $3; $3 = $1;$n",
+             [tmp, a.address, b.address])
     tmp = tmp2
-  addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" |
-               "$1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res])
+  lineF(p, "var $1 = $2; $2 = $3; $3 = $1;" |
+           "$1 = $2; $2 = $3; $3 = $1;",
+           [tmp, a.res, b.res])
 
 proc getFieldPosition(f: PNode): int =
   case f.kind
@@ -1483,10 +1522,10 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
     s: Rope
   if n.kind == nkEmpty:
     let mname = mangleName(v, p.target)
-    addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n",
-         [mname, createVar(p, v.typ, isIndirect(v))])
+    lineF(p, "var $1 = $2;$n" | "$$$1 = $2;$n",
+             [mname, createVar(p, v.typ, isIndirect(v))])
     if v.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, v.typ) == etyBaseIndex:
-      addf(p.body, "var $1_Idx = 0;$n", [ mname ])
+      lineF(p, "var $1_Idx = 0;$n", [ mname ])
   else:
     discard mangleName(v, p.target)
     gen(p, n, a)
@@ -1501,25 +1540,25 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
       let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
       if a.typ == etyBaseIndex:
         if targetBaseIndex:
-          addf(p.body, "var $1 = $2, $1_Idx = $3;$n", [
-              v.loc.r, a.address, a.res])
+          lineF(p, "var $1 = $2, $1_Idx = $3;$n",
+                   [v.loc.r, a.address, a.res])
         else:
-          addf(p.body, "var $1 = [$2, $3];$n",
-              [v.loc.r, a.address, a.res])
+          lineF(p, "var $1 = [$2, $3];$n",
+                   [v.loc.r, a.address, a.res])
       else:
         if targetBaseIndex:
           let tmp = p.getTemp
-          addf(p.body, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
-              [tmp, a.res, v.loc.r])
+          lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
+                   [tmp, a.res, v.loc.r])
         else:
-          addf(p.body, "var $1 = $2;$n", [v.loc.r, a.res])
+          lineF(p, "var $1 = $2;$n", [v.loc.r, a.res])
       return
     else:
       s = a.res
     if isIndirect(v):
-      addf(p.body, "var $1 = /**/[$2];$n", [v.loc.r, s])
+      lineF(p, "var $1 = [$2];$n", [v.loc.r, s])
     else:
-      addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n", [v.loc.r, s])
+      lineF(p, "var $1 = $2;$n" | "$$$1 = $2;$n", [v.loc.r, s])
 
 proc genVarStmt(p: PProc, n: PNode) =
   for i in countup(0, sonsLen(n) - 1):
@@ -1550,17 +1589,17 @@ proc genNew(p: PProc, n: PNode) =
   gen(p, n.sons[1], a)
   var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
   if p.target == targetJS:
-    addf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, false)])
+    lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)])
   else:
-    addf(p.body, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)])
+    lineF(p, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)])
 
 proc genNewSeq(p: PProc, n: PNode) =
   var x, y: TCompRes
   gen(p, n.sons[1], x)
   gen(p, n.sons[2], y)
   let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
-  addf(p.body, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" |
-               "$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [
+  lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" |
+           "$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [
     x.rdLoc, y.rdLoc, createVar(p, t, false)])
 
 proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
@@ -1986,15 +2025,18 @@ proc genReturnStmt(p: PProc, n: PNode) =
     genStmt(p, n.sons[0])
   else:
     genLineDir(p, n)
-  addf(p.body, "break BeforeRet;$n" | "goto BeforeRet;$n", [])
+  lineF(p, "break BeforeRet;$n" | "goto BeforeRet;$n", [])
 
 proc frameCreate(p: PProc; procname, filename: Rope): Rope =
-  result = (("var F={procname:$1,prev:framePtr,filename:$2,line:0};$nframePtr = F;$n" |
-             "global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n$$framePtr = &$$F;$n")) % [
-            procname, filename]
+  let frameFmt =
+    "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" |
+    "global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n"
+
+  result = p.indentLine(frameFmt % [procname, filename])
+  result.add p.indentLine(ropes.`%`("framePtr = F;$n" | "$$framePtr = &$$F;$n", []))
 
 proc frameDestroy(p: PProc): Rope =
-  result = rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl)
+  result = p.indentLine rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl)
 
 proc genProcBody(p: PProc, prc: PSym): Rope =
   if hasFrameInfo(p):
@@ -2004,8 +2046,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
   else:
     result = nil
   if p.beforeRetNeeded:
-    addf(result, "BeforeRet: do {$n$1} while (false); $n" |
-                 "$# BeforeRet:;$n", [p.body])
+    if p.target == targetJS:
+      result.add p.indentLine(~"BeforeRet: do {$n")
+      result.add p.body
+      result.add p.indentLine(~"} while (false);$n")
+    else:
+      addF(result, "$# BeforeRet:;$n", [p.body])
   else:
     add(result, p.body)
   if prc.typ.callConv == ccSysCall and p.target == targetJS:
@@ -2014,6 +2060,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
   if hasFrameInfo(p):
     add(result, frameDestroy(p))
 
+proc optionaLine(p: Rope): Rope =
+  if p == nil:
+    return nil
+  else:
+    return p & tnl
+
 proc genProc(oldProc: PProc, prc: PSym): Rope =
   var
     resultSym: PSym
@@ -2029,29 +2081,37 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
   if prc.typ.sons[0] != nil and sfPure notin prc.flags:
     resultSym = prc.ast.sons[resultPos].sym
     let mname = mangleName(resultSym, p.target)
-    resultAsgn = ("var $# = $#;$n" | "$$$# = $#;$n") % [
-        mname,
-        createVar(p, resultSym.typ, isIndirect(resultSym))]
+    let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
+    resultAsgn = p.indentLine(("var $# = $#;$n" | "$$$# = $#;$n") % [mname, resVar])
     if resultSym.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, resultSym.typ) == etyBaseIndex:
-      resultAsgn.add "var $#_Idx = 0;$n" % [
-        mname ];
+      resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname])
     gen(p, prc.ast.sons[resultPos], a)
     if mapType(p, resultSym.typ) == etyBaseIndex:
       returnStmt = "return [$#, $#];$n" % [a.address, a.res]
     else:
       returnStmt = "return $#;$n" % [a.res]
-  genStmt(p, prc.getBody)
 
-  result = "function $#($#) {$n$#$n$#$#$#$#}$n" %
-            [name, header, p.globals, p.locals, resultAsgn,
-             genProcBody(p, prc), returnStmt]
+  p.nested: genStmt(p, prc.getBody)
+  let def = "function $#($#) {$n$#$#$#$#$#" %
+            [name, header,
+              optionaLine(p.globals),
+              optionaLine(p.locals),
+              optionaLine(resultAsgn),
+              optionaLine(genProcBody(p, prc)),
+              optionaLine(p.indentLine(returnStmt))]
+
+  dec p.extraIndent
+  result = ~tnl
+  result.add p.indentLine(def)
+  result.add p.indentLine(~"}$n")
+
   #if gVerbosity >= 3:
   #  echo "END   generated code for: " & prc.name.s
 
 proc genStmt(p: PProc, n: PNode) =
   var r: TCompRes
   gen(p, n, r)
-  if r.res != nil: addf(p.body, "$#;$n", [r.res])
+  if r.res != nil: lineF(p, "$#;$n", [r.res])
 
 proc genPragma(p: PProc, n: PNode) =
   for it in n.sons:
@@ -2211,7 +2271,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkPragma: genPragma(p, n)
   of nkProcDef, nkMethodDef, nkConverterDef:
     var s = n.sons[namePos].sym
-    if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
+    if sfExportc in s.flags and compilingLib:
       genSym(p, n.sons[namePos], r)
       r.res = nil
   of nkGotoState, nkState:
@@ -2342,3 +2402,4 @@ proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext =
   result = r
 
 const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
+
diff --git a/compiler/options.nim b/compiler/options.nim
index c4a57f41c..5c39faf3b 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -186,6 +186,9 @@ var
 
 const oKeepVariableNames* = true
 
+template compilingLib*: bool =
+  gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}
+
 proc mainCommandArg*: string =
   ## This is intended for commands like check or parse
   ## which will work on the main project file unless
diff --git a/lib/system.nim b/lib/system.nim
index 225c032e4..49f24c973 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2449,18 +2449,6 @@ when false:
 # ----------------- GC interface ---------------------------------------------
 
 when not defined(nimscript) and hasAlloc:
-  proc GC_disable*() {.rtl, inl, benign.}
-    ## disables the GC. If called n-times, n calls to `GC_enable` are needed to
-    ## reactivate the GC. Note that in most circumstances one should only disable
-    ## the mark and sweep phase with `GC_disableMarkAndSweep`.
-
-  proc GC_enable*() {.rtl, inl, benign.}
-    ## enables the GC again.
-
-  proc GC_fullCollect*() {.rtl, benign.}
-    ## forces a full garbage collection pass.
-    ## Ordinary code does not need to call this (and should not).
-
   type
     GC_Strategy* = enum ## the strategy the GC should use for the application
       gcThroughput,      ## optimize for throughput
@@ -2470,33 +2458,87 @@ when not defined(nimscript) and hasAlloc:
 
   {.deprecated: [TGC_Strategy: GC_Strategy].}
 
-  proc GC_setStrategy*(strategy: GC_Strategy) {.rtl, deprecated, benign.}
-    ## tells the GC the desired strategy for the application.
-    ## **Deprecated** since version 0.8.14. This has always been a nop.
-
-  proc GC_enableMarkAndSweep*() {.rtl, benign.}
-  proc GC_disableMarkAndSweep*() {.rtl, benign.}
-    ## the current implementation uses a reference counting garbage collector
-    ## with a seldomly run mark and sweep phase to free cycles. The mark and
-    ## sweep phase may take a long time and is not needed if the application
-    ## does not create cycles. Thus the mark and sweep phase can be deactivated
-    ## and activated separately from the rest of the GC.
-
-  proc GC_getStatistics*(): string {.rtl, benign.}
-    ## returns an informative string about the GC's activity. This may be useful
-    ## for tweaking.
-
-  proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
-  proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
-  proc GC_ref*(x: string) {.magic: "GCref", benign.}
-    ## marks the object `x` as referenced, so that it will not be freed until
-    ## it is unmarked via `GC_unref`. If called n-times for the same object `x`,
-    ## n calls to `GC_unref` are needed to unmark `x`.
-
-  proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.}
-  proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
-  proc GC_unref*(x: string) {.magic: "GCunref", benign.}
-    ## see the documentation of `GC_ref`.
+  when not defined(JS):
+    proc GC_disable*() {.rtl, inl, benign.}
+      ## disables the GC. If called n-times, n calls to `GC_enable` are needed to
+      ## reactivate the GC. Note that in most circumstances one should only disable
+      ## the mark and sweep phase with `GC_disableMarkAndSweep`.
+
+    proc GC_enable*() {.rtl, inl, benign.}
+      ## enables the GC again.
+
+    proc GC_fullCollect*() {.rtl, benign.}
+      ## forces a full garbage collection pass.
+      ## Ordinary code does not need to call this (and should not).
+
+    proc GC_setStrategy*(strategy: GC_Strategy) {.rtl, deprecated, benign.}
+      ## tells the GC the desired strategy for the application.
+      ## **Deprecated** since version 0.8.14. This has always been a nop.
+
+    proc GC_enableMarkAndSweep*() {.rtl, benign.}
+    proc GC_disableMarkAndSweep*() {.rtl, benign.}
+      ## the current implementation uses a reference counting garbage collector
+      ## with a seldomly run mark and sweep phase to free cycles. The mark and
+      ## sweep phase may take a long time and is not needed if the application
+      ## does not create cycles. Thus the mark and sweep phase can be deactivated
+      ## and activated separately from the rest of the GC.
+
+    proc GC_getStatistics*(): string {.rtl, benign.}
+      ## returns an informative string about the GC's activity. This may be useful
+      ## for tweaking.
+
+    proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
+    proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
+    proc GC_ref*(x: string) {.magic: "GCref", benign.}
+      ## marks the object `x` as referenced, so that it will not be freed until
+      ## it is unmarked via `GC_unref`. If called n-times for the same object `x`,
+      ## n calls to `GC_unref` are needed to unmark `x`.
+
+    proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.}
+    proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
+    proc GC_unref*(x: string) {.magic: "GCunref", benign.}
+      ## see the documentation of `GC_ref`.
+
+  else:
+    template GC_disable* =
+      {.warning: "GC_disable is a no-op in JavaScript".}
+
+    template GC_enable* =
+      {.warning: "GC_enable is a no-op in JavaScript".}
+
+    template GC_fullCollect* =
+      {.warning: "GC_fullCollect is a no-op in JavaScript".}
+
+    template GC_setStrategy* =
+      {.warning: "GC_setStrategy is a no-op in JavaScript".}
+
+    template GC_enableMarkAndSweep* =
+      {.warning: "GC_enableMarkAndSweep is a no-op in JavaScript".}
+
+    template GC_disableMarkAndSweep* =
+      {.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".}
+
+    template GC_ref*[T](x: ref T) =
+      {.warning: "GC_ref is a no-op in JavaScript".}
+
+    template GC_ref*[T](x: seq[T]) =
+      {.warning: "GC_ref is a no-op in JavaScript".}
+
+    template GC_ref*(x: string) =
+      {.warning: "GC_ref is a no-op in JavaScript".}
+
+    template GC_unref*[T](x: ref T) =
+      {.warning: "GC_unref is a no-op in JavaScript".}
+
+    template GC_unref*[T](x: seq[T]) =
+      {.warning: "GC_unref is a no-op in JavaScript".}
+
+    template GC_unref*(x: string) =
+      {.warning: "GC_unref is a no-op in JavaScript".}
+
+    template GC_getStatistics*(): string =
+      {.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".}
+      ""
 
 template accumulateResult*(iter: untyped) =
   ## helps to convert an iterator to a proc.
@@ -3231,16 +3273,6 @@ when not defined(JS): #and not defined(nimscript):
 
 elif defined(JS):
   # Stubs:
-  proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = discard
-
-  proc GC_disable() = discard
-  proc GC_enable() = discard
-  proc GC_fullCollect() = discard
-  proc GC_setStrategy(strategy: GC_Strategy) = discard
-  proc GC_enableMarkAndSweep() = discard
-  proc GC_disableMarkAndSweep() = discard
-  proc GC_getStatistics(): string = return ""
-
   proc getOccupiedMem(): int = return -1
   proc getFreeMem(): int = return -1
   proc getTotalMem(): int = return -1
diff --git a/tests/js/tjshello.nim b/tests/js/tjshello.nim
new file mode 100644
index 000000000..19e0b90ae
--- /dev/null
+++ b/tests/js/tjshello.nim
@@ -0,0 +1,10 @@
+discard """
+  output: "Hello World"
+  maxcodesize: 1000
+  ccodecheck: "!@'function'"
+"""
+
+import jsconsole
+
+console.log "Hello World"
+
diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim
index ab24acc70..ed435465a 100644
--- a/tests/testament/specs.nim
+++ b/tests/testament/specs.nim
@@ -52,6 +52,7 @@ type
     exitCode*: int
     msg*: string
     ccodeCheck*: string
+    maxCodeSize*: int
     err*: TResultEnum
     substr*, sortoutput*: bool
     targets*: set[TTarget]
@@ -109,6 +110,7 @@ proc specDefaults*(result: var TSpec) =
   result.tfile = ""
   result.tline = 0
   result.tcolumn = 0
+  result.maxCodeSize = 0
 
 proc parseTargets*(value: string): set[TTarget] =
   for v in value.normalize.split:
@@ -180,6 +182,7 @@ proc parseSpec*(filename: string): TSpec =
       else:
         result.cmd = e.value
     of "ccodecheck": result.ccodeCheck = e.value
+    of "maxcodesize": discard parseInt(e.value, result.maxCodeSize)
     of "target", "targets":
       for v in e.value.normalize.split:
         case v
diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim
index 0f74de013..b83eb668a 100644
--- a/tests/testament/tester.nim
+++ b/tests/testament/tester.nim
@@ -212,20 +212,31 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) =
 proc generatedFile(path, name: string, target: TTarget): string =
   let ext = targetToExt[target]
   result = path / "nimcache" /
-    (if target == targetJS: path.splitPath.tail & "_" else: "compiler_") &
+    (if target == targetJS: "" else: "compiler_") &
     name.changeFileExt(ext)
 
-proc codegenCheck(test: TTest, check: string, given: var TSpec) =
+proc needsCodegenCheck(spec: TSpec): bool =
+  result = spec.maxCodeSize > 0 or spec.ccodeCheck.len > 0
+
+proc codegenCheck(test: TTest, spec: TSpec, expectedMsg: var string,
+                  given: var TSpec) =
   try:
     let (path, name, _) = test.name.splitFile
     let genFile = generatedFile(path, name, test.target)
     let contents = readFile(genFile).string
-    if check[0] == '\\':
-      # little hack to get 'match' support:
-      if not contents.match(check.peg):
+    let check = spec.ccodeCheck
+    if check.len > 0:
+      if check[0] == '\\':
+        # little hack to get 'match' support:
+        if not contents.match(check.peg):
+          given.err = reCodegenFailure
+      elif contents.find(check.peg) < 0:
         given.err = reCodegenFailure
-    elif contents.find(check.peg) < 0:
+      expectedMsg = check
+    if spec.maxCodeSize > 0 and contents.len > spec.maxCodeSize:
       given.err = reCodegenFailure
+      given.msg = "generated code size: " & $contents.len
+      expectedMsg = "max allowed size: " & $spec.maxCodeSize
   except ValueError:
     given.err = reInvalidPeg
     echo getCurrentExceptionMsg()
@@ -248,9 +259,8 @@ proc compilerOutputTests(test: TTest, given: var TSpec, expected: TSpec;
   var expectedmsg: string = ""
   var givenmsg: string = ""
   if given.err == reSuccess:
-    if expected.ccodeCheck.len > 0:
-      codegenCheck(test, expected.ccodeCheck, given)
-      expectedmsg = expected.ccodeCheck
+    if expected.needsCodegenCheck:
+      codegenCheck(test, expected, expectedmsg, given)
       givenmsg = given.msg
     if expected.nimout.len > 0:
       expectedmsg = expected.nimout