diff options
author | Araq <rumpf_a@web.de> | 2012-07-01 19:35:19 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2012-07-01 19:35:19 +0200 |
commit | fe285b354dee2c8148cd66207d8ed528c7845e5d (patch) | |
tree | 01f799a3905ede0ffe6fb5af49d063733203051b | |
parent | ee1bcb6414a50642811632a92a803840c1c67036 (diff) | |
download | Nim-fe285b354dee2c8148cd66207d8ed528c7845e5d.tar.gz |
JS codegen enhancements; still unusable
-rwxr-xr-x | compiler/ecmasgen.nim | 98 | ||||
-rwxr-xr-x | compiler/main.nim | 4 | ||||
-rwxr-xr-x | compiler/nimrod.nim | 9 | ||||
-rwxr-xr-x | doc/nimrodc.txt | 1 | ||||
-rwxr-xr-x | lib/pure/hashes.nim | 23 | ||||
-rwxr-xr-x | lib/system/ecmasys.nim | 101 | ||||
-rw-r--r-- | tests/specials.nim | 16 | ||||
-rwxr-xr-x | tests/tester.nim | 25 | ||||
-rwxr-xr-x | todo.txt | 5 |
9 files changed, 223 insertions, 59 deletions
diff --git a/compiler/ecmasgen.nim b/compiler/ecmasgen.nim index 64ef75de5..532023136 100755 --- a/compiler/ecmasgen.nim +++ b/compiler/ecmasgen.nim @@ -546,11 +546,12 @@ proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) = 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, "($1.exc.m_type == $2)", + 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]) @@ -791,7 +792,7 @@ proc genSwap(p: var TProc, n: PNode, r: var TCompRes) = gen(p, n.sons[2], b) inc(p.unique) var tmp = ropef("Tmp$1", [toRope(p.unique)]) - case mapType(n.sons[1].typ) + case mapType(skipTypes(n.sons[1].typ, abstractVar)) of etyBaseIndex: inc(p.unique) var tmp2 = ropef("Tmp$1", [toRope(p.unique)]) @@ -804,24 +805,36 @@ proc genSwap(p: var TProc, n: PNode, r: var TCompRes) = 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 b.sons[1].kind != nkSym: InternalError(b.sons[1].info, "genFieldAddr") - var f = b.sons[1].sym - if f.loc.r == nil: f.loc.r = mangleName(f) - r.res = makeCString(ropeToStr(f.loc.r)) + if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple: + r.res = makeCString("Field" & $getFieldPosition(b.sons[1])) + else: + if b.sons[1].kind != nkSym: InternalError(b.sons[1].info, "genFieldAddr") + var f = b.sons[1].sym + if f.loc.r == nil: f.loc.r = mangleName(f) + r.res = makeCString(ropeToStr(f.loc.r)) r.com = mergeExpr(a) proc genFieldAccess(p: var TProc, n: PNode, r: var TCompRes) = r.kind = etyNone gen(p, n.sons[0], r) - if 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]) + 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 @@ -884,11 +897,17 @@ proc genAddr(p: var TProc, n: PNode, r: var TCompRes) = genCheckedFieldAddr(p, n, r) of nkDotExpr: genFieldAddr(p, n, r) - of nkBracketExpr: - genArrayAddr(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: + 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 @@ -956,6 +975,7 @@ proc genCall(p: var TProc, n: PNode, r: var TCompRes) = genArgs(p, n, r) proc genEcho(p: var TProc, n: PNode, r: var TCompRes) = + useMagic(p, "rawEcho") app(r.res, "rawEcho") genArgs(p, n, r) @@ -1146,6 +1166,25 @@ proc genRepr(p: var TProc, n: PNode, r: var TCompRes) = # XXX: internalError(n.info, "genRepr: Not implemented") +proc genOf(p: var TProc, n: PNode, r: var TCompRes) = + var x: TCompRes + let t = n.sons[2].typ + 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 @@ -1216,6 +1255,8 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = of mNLen..mNError: localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s) of mNewSeq: binaryStmt(p, n, r, "", "$1 = new Array($2)") + 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) @@ -1252,19 +1293,17 @@ proc genArrayConstr(p: var TProc, n: PNode, r: var TCompRes) = app(r.res, a.res) app(r.res, "]") -proc genRecordConstr(p: var TProc, n: PNode, r: var TCompRes) = +proc genTupleConstr(p: var TProc, n: PNode, r: var TCompRes) = var a: TCompRes - var i = 0 - var length = sonsLen(n) r.res = toRope("{") - while i < length: + for i in countup(0, sonsLen(n) - 1): if i > 0: app(r.res, ", ") - if (n.sons[i].kind != nkSym): - internalError(n.sons[i].info, "genRecordConstr") - gen(p, n.sons[i + 1], a) + 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, "$1: $2", [mangleName(n.sons[i].sym), a.res]) - inc(i, 2) + 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) @@ -1414,9 +1453,6 @@ proc genStmt(p: var TProc, n: PNode, r: var TCompRes) = genSym(p, n.sons[namePos], r2) else: genLineDir(p, n, r) - if n.sons[0].kind == nkSym: - if n.sons[0].sym.loc.r == nil: - n.sons[0].sym.loc.r = toRope(n.sons[0].sym.name.s) gen(p, n, r) app(r.res, ';' & tnl) @@ -1460,7 +1496,7 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) = genCall(p, n, r) of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) - of nkPar: genRecordConstr(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) @@ -1477,8 +1513,14 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) = of nkStmtListExpr: genStmtListExpr(p, n, r) of nkEmpty: nil of nkLambdaKinds: - # XXX not correct, as we need to put it into the proper scope! - gen(p, n.sons[namePos], r) + 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) diff --git a/compiler/main.nim b/compiler/main.nim index 0675142fa..d3a40a32d 100755 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -131,7 +131,9 @@ when has_LLVM_Backend: proc CommandCompileToEcmaScript = incl(gGlobalOptions, optSafeCode) setTarget(osEcmaScript, cpuEcmaScript) - initDefines() + #initDefines() + DefineSymbol("nimrod") # 'nimrod' is always defined + DefineSymbol("ecmascript") semanticPasses() registerPass(ecmasgenPass()) compileProject() diff --git a/compiler/nimrod.nim b/compiler/nimrod.nim index 6328c3ae3..1bd6698a7 100755 --- a/compiler/nimrod.nim +++ b/compiler/nimrod.nim @@ -98,9 +98,14 @@ proc HandleCmdLine() = formatFloat(epochTime() - start, ffDecimal, 3), formatSize(getTotalMem())]) if optRun in gGlobalOptions: - var ex = quoteIfContainsWhite( + if gCmd == cmdCompileToEcmaScript: + var ex = quoteIfContainsWhite( + completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) + execExternalProgram("node " & ex & ' ' & arguments) + else: + var ex = quoteIfContainsWhite( changeFileExt(gProjectFull, exeExt).prependCurDir) - execExternalProgram(ex & ' ' & arguments) + execExternalProgram(ex & ' ' & arguments) #GC_disableMarkAndSweep() diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index 0fda5bb88..b131acca1 100755 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -145,6 +145,7 @@ Define Effect ``useRealtimeGC`` Enables support of Nimrod's GC for *soft* realtime systems. See the documentation of the `gc <gc.html>`_ for further information. +``nodejs`` The EcmaScript target is actually ``node.js``. ================== ========================================================= diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 06cc6c79b..7d4613f50 100755 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -35,7 +35,11 @@ proc `!$`*(h: THash): THash {.inline.} = proc hashData*(Data: Pointer, Size: int): THash = ## hashes an array of bytes of size `size` var h: THash = 0 - var p = cast[cstring](Data) + when defined(ecmascript): + var p: cstring + asm """`p` = `Data`;""" + else: + var p = cast[cstring](Data) var i = 0 var s = size while s > 0: @@ -44,9 +48,24 @@ proc hashData*(Data: Pointer, Size: int): THash = Dec(s) result = !$h +when defined(ecmascript): + var objectID = 0 + proc hash*(x: Pointer): THash {.inline.} = ## efficient hashing of pointers - result = (cast[THash](x)) shr 3 # skip the alignment + when defined(ecmascript): + asm """ + if (typeof `x` == "object") { + if ("_NimID" in `x`) + `result` = `x`["_NimID"]; + else { + `result` = ++`objectID`; + `x`["_NimID"] = `result`; + } + } + """ + else: + result = (cast[THash](x)) shr 3 # skip the alignment proc hash*(x: int): THash {.inline.} = ## efficient hashing of integers diff --git a/lib/system/ecmasys.nim b/lib/system/ecmasys.nim index e9a1e92ed..8f4585931 100755 --- a/lib/system/ecmasys.nim +++ b/lib/system/ecmasys.nim @@ -7,7 +7,11 @@ # distribution, for details about the copyright. # -proc alert*(s: cstring) {.importc, nodecl.} +when defined(nodejs): + proc alert*(s: cstring) {.importc: "console.log", nodecl.} +else: + proc alert*(s: cstring) {.importc, nodecl.} + proc log*(s: cstring) {.importc: "console.log", nodecl.} type @@ -100,7 +104,7 @@ proc raiseException(e: ref E_Base, ename: cstring) {. alert(buf) asm """throw `e`;""" -proc reraiseException() = +proc reraiseException() {.compilerproc, noStackFrame.} = if excHandler == nil: raise newException(ENoExceptionToReraise, "no exception to reraise") else: @@ -295,27 +299,40 @@ type setAttribute*: proc (name, value: cstring) setAttributeNode*: proc (attr: ref TNode) -var - document {.importc, nodecl.}: ref TDocument +when defined(nodejs): + proc ewriteln(x: cstring) = log(x) + + proc rawEcho {.compilerproc, nostackframe.} = + asm """ + var buf = ""; + for (var i = 0; i < arguments.length; ++i) { + buf += `toEcmaStr`(arguments[i]); + } + console.log(buf); + """ -proc ewriteln(x: cstring) = - var node = document.getElementsByTagName("body")[0] - if node != nil: - node.appendChild(document.createTextNode(x)) +else: + var + document {.importc, nodecl.}: ref TDocument + + proc ewriteln(x: cstring) = + var node = document.getElementsByTagName("body")[0] + if node != nil: + node.appendChild(document.createTextNode(x)) + node.appendChild(document.createElement("br")) + else: + raise newException(EInvalidValue, "<body> element does not exist yet!") + + proc rawEcho {.compilerproc.} = + var node = document.getElementsByTagName("body")[0] + if node == nil: raise newException(EIO, "<body> element does not exist yet!") + asm """ + for (var i = 0; i < arguments.length; ++i) { + var x = `toEcmaStr`(arguments[i]); + `node`.appendChild(document.createTextNode(x)) + } + """ node.appendChild(document.createElement("br")) - else: - raise newException(EInvalidValue, "<body> element does not exist yet!") - -proc rawEcho {.compilerproc.} = - var node = document.getElementsByTagName("body")[0] - if node == nil: raise newException(EIO, "<body> element does not exist yet!") - asm """ - for (var i = 0; i < arguments.length; ++i) { - var x = `toEcmaStr`(arguments[i]); - `node`.appendChild(document.createTextNode(x)) - } - """ - node.appendChild(document.createElement("br")) # Arithmetic: proc addInt(a, b: int): int {.noStackFrame, compilerproc.} = @@ -474,7 +491,7 @@ proc isFatPointer(ti: PNimType): bool = proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} -proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.exportc.} = +proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = case n.kind of nkNone: sysAssert(false, "NimCopyAux") of nkSlot: @@ -525,6 +542,37 @@ proc NimCopy(x: pointer, ti: PNimType): pointer = else: result = x +proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} = + case ti.kind + of tyPtr, tyRef, tyVar, tyNil: + if not isFatPointer(ti): + result = nil + else: + asm """ + `result` = [null, 0]; + """ + of tySet: + asm """ + `result` = {}; + """ + of tyTuple, tyObject: + if ti.kind == tyObject: + asm "`result` = {m_type: `ti`};" + else: + asm "`result` = {};" + of tySequence, tyOpenArray: + asm """ + `result` = []; + """ + of tyArrayConstr, tyArray: + asm """ + `result` = new Array(`x`.length); + for (var i = 0; i < `x`.length; ++i) { + `result`[i] = genericReset(`x`[i], `ti`.base); + } + """ + else: + result = nil proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {. noStackFrame, compilerproc.} = @@ -552,4 +600,13 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} = raise newException(EInvalidObjectConversion, "invalid object conversion") x = x.base +proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = + # checks if obj is of type subclass: + var x = obj + if x == subclass: return true # optimized fast path + while x != subclass: + if x == nil: return false + x = x.base + return true + {.pop.} diff --git a/tests/specials.nim b/tests/specials.nim index da2013e02..073ea0d27 100644 --- a/tests/specials.nim +++ b/tests/specials.nim @@ -169,6 +169,22 @@ proc compileDebuggerTests(r: var TResults, options: string) = compileSingleTest(r, "tools/nimgrep", options & " --debugger:on") +# ------------------------- JS tests ------------------------------------------ + +proc runJsTests(r: var TResults, options: string) = + template test(filename: expr): stmt = + runSingleTest(r, filename, options & " -d:nodejs", targetJS) + runSingleTest(r, filename, options & " -d:nodejs -d:release", targetJS) + + # tactiontable, texceptions, texcpt1, texcsub, tfinally, tfinally2, + # tfinally3 + for t in os.walkFiles("tests/js/t*.nim"): + test(t) + test "tests/run/tactiontable" + test "tests/run/tmultim1" + test "tests/run/tmultim3" + test "tests/run/tmultim4" + # ------------------------- register special tests here ----------------------- proc runSpecialTests(r: var TResults, options: string) = runRodFiles(r, options) diff --git a/tests/tester.nim b/tests/tester.nim index a26b7c178..be0765874 100755 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -261,7 +261,10 @@ proc compileSingleTest(r: var TResults, test, options: string) = r.addResult(t, given.msg, if given.err: reFailure else: reSuccess) if not given.err: inc(r.passed) -proc runSingleTest(r: var TResults, test, options: string) = +type + TTarget = enum targetC, targetJS + +proc runSingleTest(r: var TResults, test, options: string, target: TTarget) = var test = test.addFileExt(".nim") var t = extractFilename(test) echo t @@ -275,9 +278,16 @@ proc runSingleTest(r: var TResults, test, options: string) = if given.err: r.addResult(t, "", given.msg, reFailure) else: - var exeFile = changeFileExt(test, ExeExt) + var exeFile: string + if target == targetC: + exeFile = changeFileExt(test, ExeExt) + else: + let (dir, file, ext) = splitFile(test) + exeFile = dir / "nimcache" / file & ".js" + if existsFile(exeFile): - var (buf, exitCode) = execCmdEx(exeFile) + var (buf, exitCode) = execCmdEx( + (if target==targetJS: "node " else: "") & exeFile) if exitCode != expected.ExitCode: r.addResult(t, "exitcode: " & $expected.ExitCode, "exitcode: " & $exitCode, reFailure) @@ -291,6 +301,9 @@ proc runSingleTest(r: var TResults, test, options: string) = else: r.addResult(t, expected.outp, "executable not found", reFailure) +proc runSingleTest(r: var TResults, test, options: string) = + runSingleTest(r, test, options, targetC) + proc run(r: var TResults, dir, options: string) = for test in os.walkFiles(dir / "t*.nim"): runSingleTest(r, test, options) @@ -350,6 +363,12 @@ proc main() = run(runRes, "tests/run", p.cmdLineRest.string) runSpecialTests(runRes, p.cmdLineRest.string) writeResults(runJson, runRes) + of "js": + var runRes = initResults() + if existsFile(runJSon): + runRes = readResults(runJson) + runJsTests(runRes, p.cmdLineRest.string) + writeResults(runJson, runRes) of "merge": var rejectRes = readResults(rejectJson) var compileRes = readResults(compileJson) diff --git a/todo.txt b/todo.txt index 13eecc584..ebca214b7 100755 --- a/todo.txt +++ b/todo.txt @@ -42,6 +42,10 @@ Bugs version 0.9.XX ============== +- JS gen: + - document it + - test & fix bugs + - document nimdoc properly finally - implement a warning message for shadowed 'result' variable - implement the high level optimizer @@ -51,7 +55,6 @@ version 0.9.XX - implicit ref/ptr->var conversion; the compiler may store an object implicitly on the heap for write barrier efficiency; better: proc specialization in the code gen -- 'of' operator for JS backend - tlastmod returns wrong results on BSD (Linux, MacOS X: works) - nested tuple unpacking; tuple unpacking in non-var-context - 'nimrod def': does not always work? |