idea: try/catch and "rethrow"...?
On Tue, Feb 1, 2011 at 12:53 PM, Kyle Simpson <getify at gmail.com> wrote:
The reason for not wanting to interfere is in the sense of wanting the original error to maintain its original execution context (almost like an error propagation "stack" if you will), so that when the browser/engine reports the uncaught error, it reports it from the correct origination point (source file, line-number, etc).
A plain "throw;" statement sounds reasonable enough to me. However, note that at least in Firefox, the .fileName, .lineNumber, and .stack properties are set when the Error object is created, not when it is thrown. So you can rethrow without affecting the context.
In Narwhal, we wrote something like this:
var thrown = true; try { // something done that might throw thrown = false; } finally { if (thrown) { // exception observed but not caught } }
If I recall correctly, and I'm sure it's been noticed that I often don't, this was necessary since we didn't want to baffle Rhino's stack-trace. I don't think this would have been necessary on V8 since stack traces are garnered by the Error constructor, not by decoration at the point of throw.
This both illustrates that the feature is not necessary and that the workaround is ugly.
Kris Kowal
On Tue, Feb 1, 2011 at 10:53 AM, Kyle Simpson <getify at gmail.com> wrote:
?I have something that annoys me about how JavaScript try/catch error handling currently works. Don't get me wrong, I totally understand why it works that way, and it makes sense. But having the option to get around that behavior would be really nice.
I believe that catchguards have been accepted as Harmonious. Spidermonkey has had them since the ES3 days, but they weren't standardized due to making things too hard for someone's debugger.
try { throw 5; } catch (e if e == 4) { // not caught four(e); }
try { throw 5; } catch (e if e == 4) { four(e); } catch { // fallback other(e); }
Mike
[Interested bystander 2p.]
The thing you are looking for is common in other advanced dynamic languages (mostly Lisp derivatives, see en.wikipedia.org/wiki/Exception_handling#Condition_systems). It is the concept of handling the condition in the context where the condition is signaled, rather than magically unwinding the stack looking for catch blocks to throw to. DOM2 has a similar distinction, whether to handle an event in the 'capturing' phase or the 'bubbling' phase.
try/catch can be implemented in terms of condition handling, but not the other way around. I would be all in favor of adding condition handling to JS, and recasting try/catch as syntactic sugar.
Deja vu all over again:
Us old geezers have a tendency to repeat ourselves... If you remind me often enough, perhaps I'll stop. But it is seductive!
2011/2/1 Kyle Simpson <getify at gmail.com>:
?I have something that annoys me about how JavaScript try/catch error handling currently works. Don't get me wrong, I totally understand why it works that way, and it makes sense. But having the option to get around that behavior would be really nice.
My idea/proposal is illustrated with this gist:
Essentially, what I'm running into is several different use-cases for the need to wrap a try/catch around some call, and "observe" if it error'ed or not. Notice in the code snippet that I'm "modifying" the error's message string before re-throwing it. However, that's only one use-case. I might very well not need to modify the error, but simply passively "observe" (ie, not interfere with the bubbling up of that error) and for instance do some sort of cleanup or other graceful handling, and then pass the error along to continue its bubbling.
The reason for not wanting to interfere is in the sense of wanting the original error to maintain its original execution context (almost like an error propagation "stack" if you will), so that when the browser/engine reports the uncaught error, it reports it from the correct origination point (source file, line-number, etc).
If you try/catch such an error, and then "re-throw" it, that context is lost. I'm not positive, but I'm guessing that perhaps this is intentional by-design, and not just a particular implementation detail. If it is standardized, I'm proposing an alternative/addition. If not, I'm proposing we standardize a way to both preserve and explicitly not-preserve (aka, override) the original context. For compat reasons, the default would certainly stay as it currently is, with my idea being opt-in if the author needs it.
Of course, JavaScript doesn't expose the internals like the original context source file/line-number/etc (although I kinda wish it would), so for purely JavaScript sake it really doesn't matter. But I'm running into this in the server-side JavaScript world numerous times, and wishing the engine could keep that context. It's also useful for debugging purposes when looking at JavaScript errors reported in the browser's error console, for instance.
I'm interested in thoughts on either snippet's approach and the feasibility of something like this?
Do these problems go away if the stack trace is grabbed when the Throwable is constructed, not when it's thrown?
Java does this for java.lang.Throwable and it provides a fillInStackTrace() method to allow rethrowers to manually reset the stack trace to the throw point.
Java also allows for stackless exceptions via setStackTrace(new StackTraceElement[0]) which allows for pre-allocation of user-exceptions which for pragmatic reasons (imagine throwing an OutOfMemoryError because you ran out of space allocating the stack frame) shouldn't include stack. This mitigates most of the inefficiency in using exceptions for control flow, and is used by Neal Gafter in his Java closures proposal to implement break/continue.
?Brendan/all--
I just tested, and the first snippet (just throw
ing the same error object)
indeed worked as I wanted (preserved original source/line-number context)
in: FF3.6/4, IE9, Saf5, and Op11. It only fails to preserve context in Chr8
(V8).
So, it would seem that my idea is valid and I'm way late to the game, everyone's already done it (except for V8) and that I just need to file a V8 ticket. Sorry for the premature post without doing proper checking. That's what I get for assuming too much about V8 and its standards-compliance.
I would like to know, is this something that is indeed spec'd for JavaScript, or just an implementation detail? Should it be spec'd? Could it even be spec'd?
?FYI: There was already a similar bug filed with V8. I updated it to indicate that I'm still seeing this happen with ReferenceError's.
On Feb 1, 2011, at 11:52 AM, Mike Samuel wrote:
Do these problems go away if the stack trace is grabbed when the Throwable is constructed, not when it's thrown?
Java does this for java.lang.Throwable and it provides a fillInStackTrace() method to allow rethrowers to manually reset the stack trace to the throw point.
Java also allows for stackless exceptions via setStackTrace(new StackTraceElement[0]) which allows for pre-allocation of user-exceptions which for pragmatic reasons (imagine throwing an OutOfMemoryError because you ran out of space allocating the stack frame) shouldn't include stack. This mitigates most of the inefficiency in using exceptions for control flow, and is used by Neal Gafter in his Java closures proposal to implement break/continue.
Of course JavaScript doesn't have the concept of Throwable. Any value can be thrown.
Also note that stacktrace properties on Error objects is not in the ECMAScript standard and personally I don't think it should be.
A debugger (or debugger infrastructure) can capture stack trace information and maintain an association between a stack trace info and specific thrown objects. Even if the object is thrown multiple times. (BTW, this is probably a good use of Ephemeron tables.). This information could even be reflected back to the JavaScript layer via an API that on-demand provides the stack trace info for a recently thrown error object. But I really don't think stack-trace info should be automatically reified for every throw. I think it should only happen if a debugger is active or if reflective debug support has been explicitly enabled.
You can probably get into a debate about about whether exceptions should be used for normal control flow purposes. But they really are the only way in JavaScript to implement control flow that crosses function invocation boundaries. Because being a good host language is one of our goals keeping that style of throw as cheap as possible seems like something we should care about.
Throwing a known string value at a handler with a catch guard for that string should seem to be about as lightweight as you can get. But sometimes (for example within a recursive function) you need a unique throw value and that probably means a new object..
If I was going to compile the following Smalltalk code to JavaScript:
lookFor: obj in: seq "return the position of obj within a sequence " | position | position = 0. seq do: [:elen| elem == obj ifTrue: [^position]. positiion := position+1]. ^ -1
(note that code in brackets [ ] is essentially an anonymous function and ^ within such code returns from the surrounding method)
It would probably generate into something like the following
function loopForIn( obj, seq) { let __innerReturn = {}; let __exitValue = __escape; try { let position = 0; seq.do(function(elem) { if (elem==obj) { //this clause is what the ^ compilers into if (__exitValue !== __innerReturn ) throw new cannotReturn(); //only one return allowed (not a full continuation) __exitValue = position; //probably cheaper than adding a property to __exit object throw __innerReturn; } position = position + 1; }); return -1; } catch (e if e === __innerReturn) { return __exitValue; } }
2011/2/1 Allen Wirfs-Brock <allen at wirfs-brock.com>:
On Feb 1, 2011, at 11:52 AM, Mike Samuel wrote:
Do these problems go away if the stack trace is grabbed when the Throwable is constructed, not when it's thrown?
Java does this for java.lang.Throwable and it provides a fillInStackTrace() method to allow rethrowers to manually reset the stack trace to the throw point.
Java also allows for stackless exceptions via setStackTrace(new StackTraceElement[0]) which allows for pre-allocation of user-exceptions which for pragmatic reasons (imagine throwing an OutOfMemoryError because you ran out of space allocating the stack frame) shouldn't include stack. This mitigates most of the inefficiency in using exceptions for control flow, and is used by Neal Gafter in his Java closures proposal to implement break/continue.
Of course JavaScript doesn't have the concept of Throwable. Any value can be thrown. Also note that stacktrace properties on Error objects is not in the ECMAScript standard and personally I don't think it should be.
I wasn't arguing that it should always be available. Stack introspection is abused in nasty ways in java and has made it hard to spec things like tail-call opts, but it also enables fine grained logging and statistical profiling which are often things one wants to be able to turn on in production.
The argument I was trying to make was that stacks derived at construct time are better semantically than stacks derived at throw time.
A debugger (or debugger infrastructure) can capture stack trace information and maintain an association between a stack trace info and specific thrown objects. Even if the object is thrown multiple times. (BTW, this is probably a good use of Ephemeron tables.). This information could even be
An ephemeron table can't associate a stack with a selfless value, so ephemeron tables don't close the non-Error value hole.
One way to close it is to wrap the thrown value in an envelope that can carry stack info, and then associate that envelope with the catch block that handles the exception. The base error class could just be a type that captures stack info at creation time and so doesn't need an envelope.
On Feb 1, 2011, at 1:42 PM, Mike Samuel wrote:
A debugger (or debugger infrastructure) can capture stack trace information and maintain an association between a stack trace info and specific thrown objects. Even if the object is thrown multiple times. (BTW, this is probably a good use of Ephemeron tables.). This information could even be
An ephemeron table can't associate a stack with a selfless value, so ephemeron tables don't close the non-Error value hole.
One way to close it is to wrap the thrown value in an envelope that can carry stack info, and then associate that envelope with the catch block that handles the exception. The base error class could just be a type that captures stack info at creation time and so doesn't need an envelope.
Actually, that's why I said "objects". I'm much more sympathetic about the utility of associating a stack traceback with an actual Error or <NativeError> object than I am with associating one with a thrown selfless value.
On Feb 1, 2011, at 1:17 PM, Allen Wirfs-Brock wrote:
Also note that stacktrace properties on Error objects is not in the ECMAScript standard and personally I don't think it should be.
Having implemented SpiderMonkey's Error object 'stack' property as an extension, I agree.
A debugger (or debugger infrastructure) can capture stack trace information and maintain an association between a stack trace info and specific thrown objects. Even if the object is thrown multiple times. (BTW, this is probably a good use of Ephemeron tables.). This information could even be reflected back to the JavaScript layer via an API that on-demand provides the stack trace info for a recently thrown error object. But I really don't think stack-trace info should be automatically reified for every throw. I think it should only happen if a debugger is active or if reflective debug support has been explicitly enabled.
This could be standardized some day, but just from what you describe, which is right on, it would take a lot of experimenting and evolution to get to the point where we could construct a good de-jure standard (bike-sheddy and nit-picky but little invention beyond the de-facto standard).
You can probably get into a debate about about whether exceptions should be used for normal control flow purposes. But they really are the only way in JavaScript to implement control flow that crosses function invocation boundaries. Because being a good host language is one of our goals keeping that style of throw as cheap as possible seems like something we should care about.
To reduce the cost I made SpiderMonkey's Error objects' 'stack' property string-valued, with the string formatted to be easily split or regexp-global-matched to get an array of useful frame info.
Also, this stack string is cons'ed up only for errors-as-exceptions. Throwing 42, "foo", false, or a user-defined object, does not entail any such overhead.
Throwing a known string value at a handler with a catch guard for that string should seem to be about as lightweight as you can get.
Right. One place we require this is the Harmony iteration protocol, which uses a StopIteration singleton. We discussed Scheme-ish optional parameterization of the stop-iterating value but it is not worth its weight and the possibility of the option being used contaminates due to failure to catch. And mostly because it's exceedingly rare to have to catch StopIteration because the for-in loops and comprehensions catch for you.
It would probably generate into something like the following
function loopForIn( obj, seq) { let __innerReturn = {}; let __exitValue = __escape; try { let position = 0; seq.do(function(elem) { if (elem==obj) { //this clause is what the ^ compilers into if (__exitValue !== __innerReturn ) throw new cannotReturn(); //only one return allowed (not a full continuation) __exitValue = position; //probably cheaper than adding a property to __exit object throw __innerReturn; } position = position + 1; }); return -1; } catch (e if e === __innerReturn) { return __exitValue; } }
This can be made quite fast. But IIRC the current JS VMs, which optimize heavily, still don't go so fast when you add exception-handling to the profiles. It's rare in web JS.
On Feb 1, 2011, at 3:57 PM, Brendan Eich wrote:
This can be made quite fast. But IIRC the current JS VMs, which optimize heavily, still don't go so fast when you add exception-handling to the profiles. It's rare in web JS.
One would hope that the constructs that will ultimately get optimized are those that are actually being widely used in the wild. Of course, that ignores the impact of benchmarks...
(fixed a couple naming bugs in my looopForIn function.)
On Feb 1, 2011, at 11:52 AM, Mike Samuel wrote:
Do these problems go away if the stack trace is grabbed when the Throwable is constructed, not when it's thrown?
Java does this for java.lang.Throwable and it provides a fillInStackTrace() method to allow rethrowers to manually reset the stack trace to the throw point.
Java also allows for stackless exceptions via setStackTrace(new StackTraceElement[0]) which allows for pre-allocation of user-exceptions which for pragmatic reasons (imagine throwing an OutOfMemoryError because you ran out of space allocating the stack frame) shouldn't include stack. This mitigates most of the inefficiency in using exceptions for control flow, and is used by Neal Gafter in his Java closures proposal to implement break/continue.
Of course JavaScript doesn't have the concept of Throwable. Any value can be thrown.
Also note that stacktrace properties on Error objects is not in the ECMAScript standard and personally I don't think it should be.
A debugger (or debugger infrastructure) can capture stack trace information and maintain an association between a stack trace info and specific thrown objects. Even if the object is thrown multiple times. (BTW, this is probably a good use of Ephemeron tables.). This information could even be reflected back to the JavaScript layer via an API that on-demand provides the stack trace info for a recently thrown error object. But I really don't think stack-trace info should be automatically reified for every throw. I think it should only happen if a debugger is active or if reflective debug support has been explicitly enabled.
You can probably get into a debate about about whether exceptions should be used for normal control flow purposes. But they really are the only way in JavaScript to implement control flow that crosses function invocation boundaries. Because being a good host language is one of our goals keeping that style of throw as cheap as possible seems like something we should care about.
Throwing a known string value at a handler with a catch guard for that string should seem to be about as lightweight as you can get. But sometimes (for example within a recursive function) you need a unique throw value and that probably means a new object..
If I was going to compile the following Smalltalk code to JavaScript:
lookFor: obj in: seq "return the position of obj within a sequence " | position | position = 0. seq do: [:elen| elem == obj ifTrue: [^position]. positiion := position+1]. ^ -1
(note that code in brackets [ ] is essentially an anonymous function and ^ within such code returns from the surrounding method)
It would probably generate into something like the following
function loopForIn( obj, seq) { let __innerReturn = {}; let __exitValue = __innerReturn; try { let position = 0; seq.do(function(elem) { if (elem==obj) { //this clause is what the ^ compilers into if (__exitValue !== __innerReturn ) throw new cannotReturn(); //only one return allowed (not a full continuation) __exitValue = position; //probably cheaper than adding a property to the __innerReturn object throw __innerReturn; } position = position + 1; }); return -1; } catch (e if e === __innerReturn) { return __exitValue; } }
?I have something that annoys me about how JavaScript try/catch error handling currently works. Don't get me wrong, I totally understand why it works that way, and it makes sense. But having the option to get around that behavior would be really nice.
My idea/proposal is illustrated with this gist:
gist.github.com/802625
Essentially, what I'm running into is several different use-cases for the need to wrap a try/catch around some call, and "observe" if it error'ed or not. Notice in the code snippet that I'm "modifying" the error's message string before re-throwing it. However, that's only one use-case. I might very well not need to modify the error, but simply passively "observe" (ie, not interfere with the bubbling up of that error) and for instance do some sort of cleanup or other graceful handling, and then pass the error along to continue its bubbling.
The reason for not wanting to interfere is in the sense of wanting the original error to maintain its original execution context (almost like an error propagation "stack" if you will), so that when the browser/engine reports the uncaught error, it reports it from the correct origination point (source file, line-number, etc).
If you try/catch such an error, and then "re-throw" it, that context is lost. I'm not positive, but I'm guessing that perhaps this is intentional by-design, and not just a particular implementation detail. If it is standardized, I'm proposing an alternative/addition. If not, I'm proposing we standardize a way to both preserve and explicitly not-preserve (aka, override) the original context. For compat reasons, the default would certainly stay as it currently is, with my idea being opt-in if the author needs it.
Of course, JavaScript doesn't expose the internals like the original context source file/line-number/etc (although I kinda wish it would), so for purely JavaScript sake it really doesn't matter. But I'm running into this in the server-side JavaScript world numerous times, and wishing the engine could keep that context. It's also useful for debugging purposes when looking at JavaScript errors reported in the browser's error console, for instance.
I'm interested in thoughts on either snippet's approach and the feasibility of something like this?
--Kyle Simpson