diff options
author | zah <zahary@gmail.com> | 2017-06-03 13:45:10 +0300 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-06-03 12:45:10 +0200 |
commit | 39aef12446b0e7f7dcc2222f358490a7ed2db685 (patch) | |
tree | 9f383710bca3790407ab8edc088448981d0e3e31 | |
parent | eb8e267ff63eec0dd5279186a57d8af59f26c696 (diff) | |
download | Nim-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.nim | 12 | ||||
-rw-r--r-- | compiler/jsgen.nim | 317 | ||||
-rw-r--r-- | compiler/options.nim | 3 | ||||
-rw-r--r-- | lib/system.nim | 130 | ||||
-rw-r--r-- | tests/js/tjshello.nim | 10 | ||||
-rw-r--r-- | tests/testament/specs.nim | 3 | ||||
-rw-r--r-- | tests/testament/tester.nim | 28 |
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 |