Try/Catch support for native JS exceptions (#8955)
* Try/Catch support for native JS exceptions * Better tests
This commit is contained in:
parent
f98a3056c6
commit
a3fb0a769c
4 changed files with 77 additions and 8 deletions
|
|
@ -1739,13 +1739,14 @@ proc isException*(t: PType): bool =
|
||||||
return false
|
return false
|
||||||
|
|
||||||
proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
||||||
assert(t != nil)
|
assert t != nil
|
||||||
|
|
||||||
if optNoCppExceptions in conf.globalOptions:
|
if optNoCppExceptions in conf.globalOptions:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
|
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
|
result = true
|
||||||
|
|
||||||
proc isInfixAs*(n: PNode): bool =
|
proc isInfixAs*(n: PNode): bool =
|
||||||
|
|
|
||||||
|
|
@ -615,7 +615,6 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
gen(p, n.sons[0], a)
|
gen(p, n.sons[0], a)
|
||||||
moveInto(p, a, r)
|
moveInto(p, a, r)
|
||||||
var generalCatchBranchExists = false
|
var generalCatchBranchExists = false
|
||||||
let dollar = rope("")
|
|
||||||
if catchBranchesExist:
|
if catchBranchesExist:
|
||||||
addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" &
|
addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" &
|
||||||
" lastJSError = EXC;$n --excHandler;$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", [])
|
if i > 1: lineF(p, "}$n", [])
|
||||||
else:
|
else:
|
||||||
var orExpr: Rope = nil
|
var orExpr: Rope = nil
|
||||||
|
var excAlias: PNode = nil
|
||||||
|
|
||||||
useMagic(p, "isObj")
|
useMagic(p, "isObj")
|
||||||
for j in countup(0, blen - 2):
|
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")
|
internalError(p.config, n.info, "genTryStmt")
|
||||||
|
|
||||||
if orExpr != nil: add(orExpr, "||")
|
if orExpr != nil: add(orExpr, "||")
|
||||||
addf(orExpr, "isObj($2lastJSError.m_type, $1)",
|
# Generate the correct type checking code depending on whether this is a
|
||||||
[genTypeInfo(p, n.sons[i].sons[j].typ), dollar])
|
# 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 ")
|
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)
|
gen(p, n.sons[i].sons[blen - 1], a)
|
||||||
moveInto(p, a, r)
|
moveInto(p, a, r)
|
||||||
lineF(p, "}$n", [])
|
lineF(p, "}$n", [])
|
||||||
|
|
@ -650,7 +676,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
line(p, "else {\L")
|
line(p, "else {\L")
|
||||||
line(p, "\treraiseException();\L")
|
line(p, "\treraiseException();\L")
|
||||||
line(p, "}\L")
|
line(p, "}\L")
|
||||||
addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
|
lineF(p, "lastJSError = prevJSError;$n")
|
||||||
line(p, "} finally {\L")
|
line(p, "} finally {\L")
|
||||||
line(p, "framePtr = $1;$n" % [tmpFramePtr])
|
line(p, "framePtr = $1;$n" % [tmpFramePtr])
|
||||||
if i < length and n.sons[i].kind == nkFinally:
|
if i < length and n.sons[i].kind == nkFinally:
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,17 @@ var
|
||||||
jsFilename* {.importc: "__filename", nodecl.}: cstring
|
jsFilename* {.importc: "__filename", nodecl.}: cstring
|
||||||
## JavaScript's __filename pseudo-variable
|
## 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
|
# New
|
||||||
proc newJsObject*: JsObject {. importcpp: "{@}" .}
|
proc newJsObject*: JsObject {. importcpp: "{@}" .}
|
||||||
## Creates a new empty JsObject
|
## Creates a new empty JsObject
|
||||||
|
|
|
||||||
31
tests/js/tnativeexc.nim
Normal file
31
tests/js/tnativeexc.nim
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue