Merge pull request #2914 from yglukhov/js-exceptions

Fixed and slightly changed exception handling in JS.
This commit is contained in:
Andreas Rumpf 2015-06-16 13:07:34 +02:00
commit 042c0146cd
2 changed files with 50 additions and 33 deletions

View file

@ -505,12 +505,12 @@ proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
proc genTry(p: PProc, n: PNode, r: var TCompRes) = proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# code to generate: # code to generate:
# #
# var sp = {prev: excHandler, exc: null}; # ++excHandler;
# excHandler = sp;
# try { # try {
# stmts; # stmts;
# TMP = e # } catch (EXC) {
# } catch (e) { # var prevJSError = lastJSError; lastJSError = EXC;
# --excHandler;
# if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) {
# stmts; # stmts;
# } else if (e.typ && e.typ == NTI32342) { # } else if (e.typ && e.typ == NTI32342) {
@ -518,35 +518,41 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
# } else { # } else {
# stmts; # stmts;
# } # }
# lastJSError = prevJSError;
# } finally { # } finally {
# stmts; # stmts;
# excHandler = excHandler.prev;
# } # }
genLineDir(p, n) genLineDir(p, n)
if not isEmptyType(n.typ): if not isEmptyType(n.typ):
r.kind = resVal r.kind = resVal
r.res = getTemp(p) r.res = getTemp(p)
inc(p.unique) inc(p.unique)
var i = 1
var length = sonsLen(n)
var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
if catchBranchesExist:
add(p.body, "++excHandler;" & tnl)
var safePoint = "Tmp$1" % [rope(p.unique)] var safePoint = "Tmp$1" % [rope(p.unique)]
addf(p.body, addf(p.body,
"var $1 = {prev: excHandler, exc: null};$nexcHandler = $1;$n" | "" |
"local $1 = pcall(", "local $1 = pcall(",
[safePoint]) [safePoint])
if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl) if optStackTrace in p.options: add(p.body, "framePtr = F;" & tnl)
addf(p.body, "try {$n" | "function()$n", []) addf(p.body, "try {$n" | "function()$n", [])
var length = sonsLen(n)
var a: TCompRes var a: TCompRes
gen(p, n.sons[0], a) gen(p, n.sons[0], a)
moveInto(p, a, r) moveInto(p, a, r)
var i = 1 var generalCatchBranchExists = false
if p.target == targetJS and length > 1 and n.sons[i].kind == nkExceptBranch: if p.target == targetJS and catchBranchesExist:
addf(p.body, "} catch (EXC) {$n lastJSError = EXC;$n", []) addf(p.body, "} catch (EXC) {$n var prevJSError = lastJSError;$n" &
" lastJSError = EXC;$n --excHandler;$n", [])
elif p.target == targetLua: elif p.target == targetLua:
addf(p.body, "end)$n", []) addf(p.body, "end)$n", [])
while i < length and n.sons[i].kind == nkExceptBranch: while i < length and n.sons[i].kind == nkExceptBranch:
let blen = sonsLen(n.sons[i]) let blen = sonsLen(n.sons[i])
if blen == 1: if blen == 1:
# general except section: # general except section:
generalCatchBranchExists = true
if i > 1: addf(p.body, "else {$n" | "else$n", []) if i > 1: addf(p.body, "else {$n" | "else$n", [])
gen(p, n.sons[i].sons[0], a) gen(p, n.sons[i].sons[0], a)
moveInto(p, a, r) moveInto(p, a, r)
@ -558,17 +564,22 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
if n.sons[i].sons[j].kind != nkType: if n.sons[i].sons[j].kind != nkType:
internalError(n.info, "genTryStmt") internalError(n.info, "genTryStmt")
if orExpr != nil: add(orExpr, "||" | " or ") if orExpr != nil: add(orExpr, "||" | " or ")
addf(orExpr, "isObj($1.exc.m_type, $2)", addf(orExpr, "isObj(lastJSError.m_type, $1)",
[safePoint, genTypeInfo(p, n.sons[i].sons[j].typ)]) [genTypeInfo(p, n.sons[i].sons[j].typ)])
if i > 1: add(p.body, "else ") if i > 1: add(p.body, "else ")
addf(p.body, "if ($1.exc && ($2)) {$n" | "if $1.exc and ($2) then$n", addf(p.body, "if (lastJSError && ($2)) {$n" | "if $1.exc and ($2) then$n",
[safePoint, orExpr]) [safePoint, orExpr])
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)
addf(p.body, "}$n" | "end$n", []) addf(p.body, "}$n" | "end$n", [])
inc(i) inc(i)
if p.target == targetJS: if p.target == targetJS:
add(p.body, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) if catchBranchesExist:
if not generalCatchBranchExists:
useMagic(p, "reraiseException")
add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl)
add(p.body, "lastJSError = prevJSError;" & tnl)
add(p.body, "} finally {" & tnl)
if i < length and n.sons[i].kind == nkFinally: if i < length and n.sons[i].kind == nkFinally:
genStmt(p, n.sons[i].sons[0]) genStmt(p, n.sons[i].sons[0])
if p.target == targetJS: if p.target == targetJS:
@ -1067,6 +1078,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
r.kind = resExpr r.kind = resExpr
proc genEcho(p: PProc, n: PNode, r: var TCompRes) = proc genEcho(p: PProc, n: PNode, r: var TCompRes) =
useMagic(p, "toJSStr") # Used in rawEcho
useMagic(p, "rawEcho") useMagic(p, "rawEcho")
add(r.res, "rawEcho(") add(r.res, "rawEcho(")
let n = n[1].skipConv let n = n[1].skipConv
@ -1676,7 +1688,7 @@ proc genHeader(): Rope =
result = ("/* Generated by the Nim Compiler v$1 */$n" & result = ("/* Generated by the Nim Compiler v$1 */$n" &
"/* (c) 2015 Andreas Rumpf */$n$n" & "/* (c) 2015 Andreas Rumpf */$n$n" &
"var framePtr = null;$n" & "var framePtr = null;$n" &
"var excHandler = null;$n" & "var excHandler = 0;$n" &
"var lastJSError = null;$n") % "var lastJSError = null;$n") %
[rope(VersionAsString)] [rope(VersionAsString)]

View file

@ -37,9 +37,7 @@ type
var var
framePtr {.importc, nodecl, volatile.}: PCallFrame framePtr {.importc, nodecl, volatile.}: PCallFrame
excHandler {.importc, nodecl, volatile.}: PSafePoint = nil excHandler {.importc, nodecl, volatile.}: int = 0
# list of exception handlers
# a global variable for the root of all try blocks
lastJSError {.importc, nodecl, volatile.}: PJSError = nil lastJSError {.importc, nodecl, volatile.}: PJSError = nil
{.push stacktrace: off, profiler:off.} {.push stacktrace: off, profiler:off.}
@ -52,9 +50,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} =
result[0] = x result[0] = x
proc getCurrentExceptionMsg*(): string = proc getCurrentExceptionMsg*(): string =
if excHandler != nil and excHandler.exc != nil: if lastJSError != nil:
return $excHandler.exc.msg
elif lastJSError != nil:
return $lastJSError.message return $lastJSError.message
else: else:
return "" return ""
@ -99,32 +95,41 @@ proc rawWriteStackTrace(): string =
else: else:
result = "No stack traceback available\n" result = "No stack traceback available\n"
proc raiseException(e: ref Exception, ename: cstring) {. proc unhandledException(e: ref Exception) {.
compilerproc, asmNoStackFrame.} = compilerproc, asmNoStackFrame.} =
e.name = ename when NimStackTrace:
if excHandler != nil: var buf = rawWriteStackTrace()
excHandler.exc = e
else: else:
when NimStackTrace: var buf = ""
var buf = rawWriteStackTrace()
else:
var buf = ""
if e.msg != nil and e.msg[0] != '\0': if e.msg != nil and e.msg[0] != '\0':
add(buf, "Error: unhandled exception: ") add(buf, "Error: unhandled exception: ")
add(buf, e.msg) add(buf, e.msg)
else: else:
add(buf, "Error: unhandled exception") add(buf, "Error: unhandled exception")
add(buf, " [") add(buf, " [")
add(buf, ename) add(buf, e.name)
add(buf, "]\n") add(buf, "]\n")
alert(buf) alert(buf)
asm """throw `e`;"""
proc raiseException(e: ref Exception, ename: cstring) {.
compilerproc, asmNoStackFrame.} =
e.name = ename
when not defined(noUnhandledHandler):
if excHandler == 0:
unhandledException(e)
asm "throw `e`;"
proc reraiseException() {.compilerproc, asmNoStackFrame.} = proc reraiseException() {.compilerproc, asmNoStackFrame.} =
if excHandler == nil: if lastJSError == nil:
raise newException(ReraiseError, "no exception to reraise") raise newException(ReraiseError, "no exception to reraise")
else: else:
asm """throw excHandler.exc;""" when not defined(noUnhandledHandler):
if excHandler == 0:
var isNimException : bool
asm "`isNimException` = lastJSError.m_type;"
if isNimException:
unhandledException(cast[ref Exception](lastJSError))
asm "throw lastJSError;"
proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} = proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} =
raise newException(OverflowError, "over- or underflow") raise newException(OverflowError, "over- or underflow")