diff options
author | LemonBoy <LemonBoy@users.noreply.github.com> | 2018-10-09 19:51:29 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2018-10-09 19:51:29 +0200 |
commit | a3fb0a769c05f5a88a68c9762069cc0056207258 (patch) | |
tree | 292788c43f2e54f0bd0194b1b113869ee4e39278 | |
parent | f98a3056c6c98ba546a302040679fb6226f555f6 (diff) | |
download | Nim-a3fb0a769c05f5a88a68c9762069cc0056207258.tar.gz |
Try/Catch support for native JS exceptions (#8955)
* Try/Catch support for native JS exceptions * Better tests
-rw-r--r-- | compiler/ast.nim | 5 | ||||
-rw-r--r-- | compiler/jsgen.nim | 38 | ||||
-rw-r--r-- | lib/js/jsffi.nim | 11 | ||||
-rw-r--r-- | tests/js/tnativeexc.nim | 31 |
4 files changed, 77 insertions, 8 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 694944631..55970596b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1739,13 +1739,14 @@ proc isException*(t: PType): bool = return false proc isImportedException*(t: PType; conf: ConfigRef): bool = - assert(t != nil) + assert t != nil + if optNoCppExceptions in conf.globalOptions: return false let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst}) - if base.sym != nil and sfCompileToCpp in base.sym.flags: + if base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {}: result = true proc isInfixAs*(n: PNode): bool = diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 277fe00f5..69e6db8ad 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -615,7 +615,6 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], a) moveInto(p, a, r) var generalCatchBranchExists = false - let dollar = rope("") if catchBranchesExist: addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" & " lastJSError = EXC;$n --excHandler;$n", []) @@ -631,15 +630,42 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if i > 1: lineF(p, "}$n", []) else: var orExpr: Rope = nil + var excAlias: PNode = nil + useMagic(p, "isObj") for j in countup(0, blen - 2): - if n.sons[i].sons[j].kind != nkType: + var throwObj: PNode + let it = n.sons[i].sons[j] + + if it.isInfixAs(): + throwObj = it[1] + excAlias = it[2] + # If this is a ``except exc as sym`` branch there must be no following + # nodes + doAssert orExpr == nil + elif it.kind == nkType: + throwObj = it + else: internalError(p.config, n.info, "genTryStmt") + if orExpr != nil: add(orExpr, "||") - addf(orExpr, "isObj($2lastJSError.m_type, $1)", - [genTypeInfo(p, n.sons[i].sons[j].typ), dollar]) + # Generate the correct type checking code depending on whether this is a + # NIM-native or a JS-native exception + # if isJsObject(throwObj.typ): + if isImportedException(throwObj.typ, p.config): + addf(orExpr, "lastJSError instanceof $1", + [throwObj.typ.sym.loc.r]) + else: + addf(orExpr, "isObj(lastJSError.m_type, $1)", + [genTypeInfo(p, throwObj.typ)]) + if i > 1: line(p, "else ") - lineF(p, "if ($1lastJSError && ($2)) {$n", [dollar, orExpr]) + lineF(p, "if (lastJSError && ($1)) {$n", [orExpr]) + # If some branch requires a local alias introduce it here. This is needed + # since JS cannot do ``catch x as y``. + if excAlias != nil: + excAlias.sym.loc.r = mangleName(p.module, excAlias.sym) + lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r) gen(p, n.sons[i].sons[blen - 1], a) moveInto(p, a, r) lineF(p, "}$n", []) @@ -650,7 +676,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = line(p, "else {\L") line(p, "\treraiseException();\L") line(p, "}\L") - addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar]) + lineF(p, "lastJSError = prevJSError;$n") line(p, "} finally {\L") line(p, "framePtr = $1;$n" % [tmpFramePtr]) if i < length and n.sons[i].kind == nkFinally: diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 7b44c57c7..307fe2382 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -89,6 +89,17 @@ var jsFilename* {.importc: "__filename", nodecl.}: cstring ## JavaScript's __filename pseudo-variable +# Exceptions +type + JsError* {.importc: "Error".} = object of JsRoot + message*: cstring + JsEvalError* {.importc: "EvalError".} = object of JsError + JsRangeError* {.importc: "RangeError".} = object of JsError + JsReferenceError* {.importc: "ReferenceError".} = object of JsError + JsSyntaxError* {.importc: "SyntaxError".} = object of JsError + JsTypeError* {.importc: "TypeError".} = object of JsError + JsURIError* {.importc: "URIError".} = object of JsError + # New proc newJsObject*: JsObject {. importcpp: "{@}" .} ## Creates a new empty JsObject diff --git a/tests/js/tnativeexc.nim b/tests/js/tnativeexc.nim new file mode 100644 index 000000000..ea371c1cd --- /dev/null +++ b/tests/js/tnativeexc.nim @@ -0,0 +1,31 @@ +discard """ + action: "run" +""" + +import jsffi + +# Can catch JS exceptions +try: + asm """throw new Error('a new error');""" +except JsError as e: + doAssert e.message == "a new error" +except: + doAssert false + +# Can distinguish different exceptions +try: + asm """JSON.parse(';;');""" +except JsEvalError: + doAssert false +except JsSyntaxError as se: + doAssert se.message == "Unexpected token ; in JSON at position 0" +except JsError as e: + doAssert false + +# Can catch parent exception +try: + asm """throw new SyntaxError();""" +except JsError as e: + discard +except: + doAssert false |