summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorLemonBoy <LemonBoy@users.noreply.github.com>2018-10-09 19:51:29 +0200
committerAndreas Rumpf <rumpf_a@web.de>2018-10-09 19:51:29 +0200
commita3fb0a769c05f5a88a68c9762069cc0056207258 (patch)
tree292788c43f2e54f0bd0194b1b113869ee4e39278
parentf98a3056c6c98ba546a302040679fb6226f555f6 (diff)
downloadNim-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.nim5
-rw-r--r--compiler/jsgen.nim38
-rw-r--r--lib/js/jsffi.nim11
-rw-r--r--tests/js/tnativeexc.nim31
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