Maximally minimal stack trace standardization

# John Lenz (10 years ago)

I would like to get see stack traces standardized for ES7, to that end, I would like to define a minimal set of behaviors that would need to be defined:

  • the "stack" property (a string)
  • when the stack property is attached (at Error object creation or at throw)
  • what happens when Error object that has been thrown, is thrown again (nothing)
  • the stack trace in the face of tail recursion optimizations (skipped?)
  • the minimal information that a stack trace should contain (file, line, column)
  • the format of the minimal information
  • how additional information is added to the stack trace (named evals, etc)

Does this sound like a reasonable minimal set?

# Filip Pizlo (10 years ago)

I would also like to see this standardized. Comments inline.

On Sep 27, 2014, at 10:15 PM, John Lenz <concavelenz at gmail.com> wrote:

I would like to get see stack traces standardized for ES7, to that end, I would like to define a minimal set of behaviors that would need to be defined:

  • the "stack" property (a string)
  • when the stack property is attached (at Error object creation or at throw)
  • what happens when Error object that has been thrown, is thrown again (nothing)
  • the stack trace in the face of tail recursion optimizations (skipped?)

Is that really necessary? If so, can you say something about the motivation?

You can do some tail recursion optimizations while preserving the stack trace. For example if you call yourself recursively and the JIT turns it into a loop, then all you need is the loop trip count to recover the original stack trace.

  • the minimal information that a stack trace should contain (file, line, column)
  • the format of the minimal information
  • how additional information is added to the stack trace (named evals, etc)

Does this sound like a reasonable minimal set?

+1

# Boris Zbarsky (10 years ago)

On 9/28/14, 1:15 AM, John Lenz wrote:

  • the "stack" property (a string)

I'd somewhat like to see this be an accessor, to allow implementations to compute the stack string lazily.

Does this sound like a reasonable minimal set?

Yes.

# Erik Arvidsson (10 years ago)

Last time this was tried the conclusion was that the current format using the stack property as a string could not be standardized. Different browsers use different format and therefore the format can not be changed without breaking existing code.

The conclusion was that we needed to use a different name and then we might add well stop using a string to represent this structured data.

Other than that. Great. +1.

# Marius Gundersen (10 years ago)

It would be helpful if the stack trace was machine readable, not just human readable. As Erik said

[...] we might ass well stop using a string to represent this structured

data.

The stacktrace should probably be an array of objects with the properties filename, function, line and column. It would then be simple to format this stacktrace as a string and make it look exactly like it does in browsers today. With a machine readable stack trace it would be easier to build editor sandboxes (jsbin, codepen.io, etc) where errors inside the sandbox could be caught by the editor and shown to the user.

Another feature of the stacktrace as an array is that a library could manipulate the stack trace after it has been thrown, to hide the internals of the library. This is similar to the black box feature in the Firefox devtools. For example, a divide function which takes two integers could throw an error when the second parameter is 0 (can't divide by zero). This would result in the library internals ending up in the stack trace:

function calculate(x, y){
  return myIntegerMathLibrary.divide(2, x)*y;
}

calculate(0, 5)
/*
Exception: Second parameter is '0'; can't divide by zero
divide at myIntegerMathLibrary/divide.js:15:15 <= this is library code,it's
not helpful to the developer
calculate at myApplication:2:3 <= this tells the developer where the problem
is in their code
@myApplication:4:1
*/

If the library was allowed to manipulate the stack of the Error object before throwing it, it could remove the top n lines that are internal to it, which would help the developer locate the issue in their code.

Since all modern browsers implement a non-standard stack trace it is possible to find out what file you are in and what line you are on by throwing and catching an exception and analyzing the stack string. If stack traces are added to the standard, then maybe a reflection API that can tell what file and line the code is on should be added as well. The module meta object will most likely get a filename property, but line number/character number is not yet available.

Marius Gundersen

# Filip Pizlo (10 years ago)

On Sep 28, 2014, at 8:01 AM, Marius Gundersen <gundersen at gmail.com> wrote:

It would be helpful if the stack trace was machine readable, not just human readable. As Erik said

+1

# John Lenz (10 years ago)

Firefox has changed aspects (adding columns and other things) so I think it is possible that we can change this. Was there hard data otherwise?

# John Lenz (10 years ago)

But that said an new name and structure would be fine with me. I'm a little worried "stack" would be out of sync with "stack trace" or whatever. Making stack an accesor of "stackTrace" would help but what do we do with writes?

# John Lenz (10 years ago)

Of course a standard string format can be machine readable (we maintain stack trace parsers for existing browsers) but it is inconvenient compared to simple JSON or whatever.

# John Lenz (10 years ago)

On Sat, Sep 27, 2014 at 10:53 PM, Filip Pizlo <fpizlo at apple.com> wrote:

I would also like to see this standardized. Comments inline.

On Sep 27, 2014, at 10:15 PM, John Lenz <concavelenz at gmail.com> wrote:

I would like to get see stack traces standardized for ES7, to that end, I would like to define a minimal set of behaviors that would need to be defined:

  • the "stack" property (a string)
  • when the stack property is attached (at Error object creation or at throw)
  • what happens when Error object that has been thrown, is thrown again (nothing)
  • the stack trace in the face of tail recursion optimizations (skipped?)

Is that really necessary? If so, can you say something about the motivation?

You can do some tail recursion optimizations while preserving the stack trace. For example if you call yourself recursively and the JIT turns it into a loop, then all you need is the loop trip count to recover the original stack trace.

I really have no idea what the behavior should be in the faces of optimized tail calls (which is must broader than simply self recursive methods that can be rewritten as a loop). I've seen various suggestions (a capped call history) but I'm curious how efficient functional languages deal with this.

I haven't actually seen anything about tail recursion optimizations being implemented, have any of the VM actually tried or committed to implementing tail call optimizations?

# Andreas Rossberg (10 years ago)

On 28 September 2014 17:01, Marius Gundersen <gundersen at gmail.com> wrote:

The stacktrace should probably be an array of objects with the properties filename, function, line and column.

Just to be clear, since you said "array of objects": 'function' would still have to be string-valued, to avoid security leaks.

# Filip Pizlo (10 years ago)

On Sep 29, 2014, at 7:55 AM, John Lenz <concavelenz at gmail.com> wrote:

On Sat, Sep 27, 2014 at 10:53 PM, Filip Pizlo <fpizlo at apple.com <mailto:fpizlo at apple.com>> wrote: I would also like to see this standardized. Comments inline.

On Sep 27, 2014, at 10:15 PM, John Lenz <concavelenz at gmail.com <mailto:concavelenz at gmail.com>> wrote:

I would like to get see stack traces standardized for ES7, to that end, I would like to define a minimal set of behaviors that would need to be defined:

  • the "stack" property (a string)
  • when the stack property is attached (at Error object creation or at throw)
  • what happens when Error object that has been thrown, is thrown again (nothing)
  • the stack trace in the face of tail recursion optimizations (skipped?)

Is that really necessary? If so, can you say something about the motivation?

You can do some tail recursion optimizations while preserving the stack trace. For example if you call yourself recursively and the JIT turns it into a loop, then all you need is the loop trip count to recover the original stack trace.

I really have no idea what the behavior should be in the faces of optimized tail calls (which is must broader than simply self recursive methods that can be rewritten as a loop). I've seen various suggestions (a capped call history) but I'm curious how efficient functional languages deal with this.

The last time I used ML, which was admittedly a long time ago, the two main implementations (MLton and SML/NJ) simply didn’t have stack traces.

I haven't actually seen anything about tail recursion optimizations being implemented, have any of the VM actually tried or committed to implementing tail call optimizations?

We (JSC) haven’t. And we don’t plan to, because:

  • We still need to be able to reconstruct the original stack for things like function.caller and function.arguments. We are trying to remove the latter but the former still lives.

  • Debugging. Our inspector UI promises stack traces for breakpoints and whenever exceptions are thrown. We could make tail call optimizations work only when the inspector is not attached, but this would be weird. Tail call optimizations end up being a kind of semantic guarantee, since if they work you can write loops using tail calls. It would be weird if attaching the inspector suddenly made all of your tail calls turn into stack overflows. It’s actually better if the risk of stack overflow is equal regardless of whether the inspector is attached.

  • JS is a great language; it actually lets you write an honest loop. You don’t need tail calls.

# Andreas Rossberg (10 years ago)

On 29 September 2014 16:55, John Lenz <concavelenz at gmail.com> wrote:

On Sat, Sep 27, 2014 at 10:53 PM, Filip Pizlo <fpizlo at apple.com> wrote:

On Sep 27, 2014, at 10:15 PM, John Lenz <concavelenz at gmail.com> wrote:

I would like to get see stack traces standardized for ES7, to that end, I would like to define a minimal set of behaviors that would need to be defined:

  • the "stack" property (a string)
  • when the stack property is attached (at Error object creation or at throw)
  • what happens when Error object that has been thrown, is thrown again (nothing)
  • the stack trace in the face of tail recursion optimizations (skipped?)

Is that really necessary? If so, can you say something about the motivation?

You can do some tail recursion optimizations while preserving the stack trace. For example if you call yourself recursively and the JIT turns it into a loop, then all you need is the loop trip count to recover the original stack trace.

I really have no idea what the behavior should be in the faces of optimized tail calls (which is must broader than simply self recursive methods that can be rewritten as a loop). I've seen various suggestions (a capped call history) but I'm curious how efficient functional languages deal with this.

Indeed, I think not enough people appreciate the (substantial) difference between general TCO and tail recursion. If the language was required to be able to construct stack traces then that would effectively kill the benefit of TCO.

# Andreas Rossberg (10 years ago)

On 29 September 2014 18:06, Filip Pizlo <fpizlo at apple.com> wrote:

  • JS is a great language; it actually lets you write an honest loop. You don’t need tail calls.

Let me repeat what I just wrote in my previous mail: "I think not enough people appreciate the (substantial) difference between general TCO and mere tail recursion." ;)

# Sam Tobin-Hochstadt (10 years ago)

On Mon, Sep 29, 2014 at 10:55 AM, John Lenz <concavelenz at gmail.com> wrote:

I really have no idea what the behavior should be in the faces of optimized tail calls (which is must broader than simply self recursive methods that can be rewritten as a loop). I've seen various suggestions (a capped call history) but I'm curious how efficient functional languages deal with this.

Different functional languages do a variety of things here:

  • simply show the current stack, without the functions that made tail calls (this is probably the most common)
  • have a bounded buffer for stack traces
  • implement tail calls via a trampoline; this has the side-effect that the stack has "recent" tail calls in it already

I'm sure there are other choices here that people have made.

# Mark S. Miller (10 years ago)

On Mon, Sep 29, 2014 at 12:06 PM, Filip Pizlo <fpizlo at apple.com> wrote: [...]

I haven't actually seen anything about tail recursion optimizations being implemented, have any of the VM actually tried or committed to implementing tail call optimizations?

We (JSC) haven’t. And we don’t plan to, because:

  • We still need to be able to reconstruct the original stack for things like function.caller and function.arguments. We are trying to remove the latter but the former still lives.

First, kudos on trying to remove function.arguments. It would be awesome to see this gone, and I greatly appreciate that you/JSC are willing to test the waters.

Although we would both like to see function.caller gone as well, we are also both much more skeptical that this is possible. Let's assume that it is not. The conclusion that follows is only that sloppy calls to sloppy functions can't be TCOed away. That is in any case what we've been assuming. That's why, IIRC (haven't checked lately), TCO is only specified for calls from non-sloppy functions.

[...]

# Brendan Eich (10 years ago)

Mark S. Miller wrote:

That's why, IIRC (haven't checked lately), TCO is only specified for calls from non-sloppy functions.

PTC (Proper Tail Calls), not TCO. It's confusing to equate the two, from what I know (corrections welcome0.

To add to confusion, ES6 drafts say "Tail Position Calls" (TPC). Is this story reminidng anyone of the origin of the "UTC" acronym?

Anyway, Mark: you recall correctly: see 14.6.1 step 2 under

people.mozilla.org/~jorendorff/es6-draft.html#sec-tail-position-calls

Fil, this is normative draft spec for ES6, observable asymptotic space performance. Not optional at implementor's discretion. :

# Filip Pizlo (10 years ago)

On Sep 29, 2014, at 10:25 AM, Brendan Eich <brendan at mozilla.org> wrote:

Mark S. Miller wrote:

That's why, IIRC (haven't checked lately), TCO is only specified for calls from non-sloppy functions.

PTC (Proper Tail Calls), not TCO. It's confusing to equate the two, from what I know (corrections welcome0.

To add to confusion, ES6 drafts say "Tail Position Calls" (TPC). Is this story reminidng anyone of the origin of the "UTC" acronym?

Anyway, Mark: you recall correctly: see 14.6.1 step 2 under

people.mozilla.org/~jorendorff/es6-draft.html#sec-tail-position-calls

Fil, this is normative draft spec for ES6, observable asymptotic space performance. Not optional at implementor's discretion. :-P

Bummer.

# Filip Pizlo (10 years ago)

On Sep 29, 2014, at 9:23 AM, Mark S. Miller <erights at google.com> wrote:

On Mon, Sep 29, 2014 at 12:06 PM, Filip Pizlo <fpizlo at apple.com> wrote: [...]

I haven't actually seen anything about tail recursion optimizations being implemented, have any of the VM actually tried or committed to implementing tail call optimizations?

We (JSC) haven’t. And we don’t plan to, because:

  • We still need to be able to reconstruct the original stack for things like function.caller and function.arguments. We are trying to remove the latter but the former still lives.

First, kudos on trying to remove function.arguments. It would be awesome to see this gone, and I greatly appreciate that you/JSC are willing to test the waters.

Although we would both like to see function.caller gone as well, we are also both much more skeptical that this is possible. Let's assume that it is not. The conclusion that follows is only that sloppy calls to sloppy functions can't be TCOed away. That is in any case what we've been assuming. That's why, IIRC (haven't checked lately), TCO is only specified for calls from non-sloppy functions.

I agree! Thanks for the explanation. :-)

# Allen Wirfs-Brock (10 years ago)

On Sep 29, 2014, at 9:23 AM, Mark S. Miller wrote:

Although we would both like to see function.caller gone as well, we are also both much more skeptical that this is possible. Let's assume that it is not. The conclusion that follows is only that sloppy calls to sloppy functions can't be TCOed away. That is in any case what we've been assuming. That's why, IIRC (haven't checked lately), TCO is only specified for calls from non-sloppy functions.

Actually, TCO is only specified for calls from strict mode ECMAScript functions.

The only place the distinction between strict and non-sloppy is really significant is WRT built-in functions. Standard built-ins are allowed to be implemented either as strict ECMAScript functions or in some implementation defined manner (ie, using some other programming language). If the latter is the case, we don't really have any say over their internal call semantics, etc.

# Mark S. Miller (10 years ago)

Agreed. TCO or PTC specified only for strict mode functions, not for all non-sloppy functions.

Would be nice to extend this at least to bound functions resulting from binding a strict function, but too late to consider for ES6. In any case, all such TCO extensions can compatibly happen later.

# Allen Wirfs-Brock (10 years ago)

On Sep 29, 2014, at 11:00 AM, Mark S. Miller wrote:

Agreed. TCO or PTC specified only for strict mode functions, not for all non-sloppy functions.

Would be nice to extend this at least to bound functions resulting from binding a strict function, but too late to consider for ES6. In any case, all such TCO extensions can compatibly happen later.

Bound functions don't have bodies, and hence don't directly make ECMAScript function calls. All they do is delegate their [[Call]] behavior to the target function's [[Call]]. No particular reason an implementation can't optimize through that if they want to.

# Carl Smith (10 years ago)

Just wanted to add that CoffeeShop [carlsmith/coffeeshop] already uses stack traces the way JSFiddle and CodePen may do if they were standardised. This only works on V8 as it's the only engine that respects sourceURL 'directives' when listing eval'ed code in stack traces.

Providing the trace as an array, instead of a string, would be nice, but nothing special. It's not difficult to parse the string into an array of hashes now. On the other hand, named eval'ed code is absolutely killer. We need named evals now.

It's currently impossible to build a JavaScript shell in any browser. FF provides line and column numbers for compilation errors, but omits the actual name, so they may as well just not bother ~ it's impossible to build a traceback on Gecko runtime errors. V8 honours the given name, but doesn't do line and column numbers on compilation errors, so you'd better hope your users never make a syntax error.

CoffeeShop uses CoffeeScript, who's compiler provides it's own line and column numbers on compilation errors, so it actually does work in Chrome, but the app can't support JS :/

We need (1) the name of the file (2) the line number and (3) the column number. The function's name could be useful, but everything else is just go-faster-stripes.

# Carl Smith (10 years ago)

Sorry, this paragraph is a bit confusing...

It's currently impossible to build a JavaScript shell in any browser. FF provides line and column numbers for compilation errors, but omits the actual name, so they may as well just not bother ~ it's impossible to build a traceback on Gecko runtime errors. V8 honours the given name, but doesn't do line and column numbers on compilation errors, so you'd better hope your users never make a syntax error.

I meant to say that on Gecko, you can do compilation errors, but not runtime ones. On Chrome, it's the other way around.

# Brendan Eich (10 years ago)

Allen Wirfs-Brock wrote:

No particular reason an implementation can't optimize through that if they want to.

The question is whether it should be normative. PTC is about observable asymptotic space performance (I keep saying :-P).

# Mark Miller (10 years ago)

Yes, I believe that we should consider some non-strict non-sloppy functions, such as .bind()ings of strict functions, to also normatively have some TCO/PTC requirements. However, it is too late to consider such for ES6 and we can always extend such TCO/PTC requirements to more cases later.

# Steve Fink (10 years ago)

On 09/29/2014 09:14 AM, Sam Tobin-Hochstadt wrote:

On Mon, Sep 29, 2014 at 10:55 AM, John Lenz <concavelenz at gmail.com> wrote:

I really have no idea what the behavior should be in the faces of optimized tail calls (which is must broader than simply self recursive methods that can be rewritten as a loop). I've seen various suggestions (a capped call history) but I'm curious how efficient functional languages deal with this. Different functional languages do a variety of things here:

  • simply show the current stack, without the functions that made tail calls (this is probably the most common)
  • have a bounded buffer for stack traces
  • implement tail calls via a trampoline; this has the side-effect that the stack has "recent" tail calls in it already

I'm sure there are other choices here that people have made.

"Stack traces" are really an overload of (at least?) 3 different concepts:

  1. A record of how execution reached the current state. What debuggers want, mostly.
  2. The continuation from this point on - what function will be returned to when the current function returns normally, recursively up the call chain.
  3. A description of the actual state of the stack.

In all of these, the semantics of the youngest frame are different from all other frames in the stack trace.

For #2, thrown exceptions make the implied continuation ordering a lie, or at least a little more nuanced. You sort of want to see what frames will catch exceptions. (But that's not a trivial determination if you have some "native" frames mixed in there, with arbitrary logic for determining whether to catch or propagate an exception. Even JS frames may re-throw.)

Inlined functions may cause gaps in #1 and #2, unless the implementation takes pains to fill them in with dummy frames (in which case it's not really #3 anymore.)

Unless the implementation plays games, tail calls can make #1 lie as well. You really called f(), but it doesn't appear because its frame was used for executing g() before pushing the remaining frames on your stack. Tail calls don't really muck with #2 afaict.

All three meanings are legitimate things to want, and all of them require some implementation effort. Even #3 is tricky with a JIT involved. And I'm not even considering floating generator frames, which may not fit into a linear structure at all. Or when users want "long stacks" for callbacks, where the stack in effect when a callback was set is relevant.

# Allen Wirfs-Brock (10 years ago)

On Sep 29, 2014, at 12:02 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

No particular reason an implementation can't optimize through that if they want to.

The question is whether it should be normative. PTC is about observable asymptotic space performance (I keep saying :-P).

/be

What should be normative? You guys probably should probably review the actual spec. language and see if you have any issues with it. All the tail call action takes place in the spec. on the caller side. See people.mozilla.org/~jorendorff/es6-draft.html#sec-runtime-semantics-evaluatecall and people.mozilla.org/~jorendorff/es6-draft.html#sec-preparefortailcall

The resources requirements of a function are represented by its "execution context". PrepareForTailCall says the the caller's "execution context" is discarded before invoking the callee's [[call]] internal method. [[Call]] for bound functions does not create a new "execution context" so there is no associated specified resource consumption that needs to be optimized away. At least as far as the spec. is concerned, bound functions (whether strict or sloppy) have no impact on TCO.

I can't imagine what you would want be to try to say about non-EMCAScript functions. Their internal "call" semantics is determined by the semantics of their implementation language.

# Mark S. Miller (10 years ago)

The issue is the asymptotic space consumption almost-contract. The reason I say "almost" is that conformance of an implementation is not testable. Currently, the spec says nothing about when an implementation might run out of storage.

So we (at least I) mean normative only in the following sense: When someone writes an algorithm in ES6 using, say, bound functions in a loop, what claims may they validly make about the space complexity of their program? The specification should help answer such questions, sometimes.

Note that I distinguish here between "the space complexity of their program" and the space usage growth when their program is run on an actual implementation. Again, conformance with any requirement on the latter is not testable.

# Sam Tobin-Hochstadt (10 years ago)

On Mon, Sep 29, 2014 at 4:13 PM, Mark S. Miller <erights at google.com> wrote:

The issue is the asymptotic space consumption almost-contract. The reason I say "almost" is that conformance of an implementation is not testable. Currently, the spec says nothing about when an implementation might run out of storage.

So we (at least I) mean normative only in the following sense: When someone writes an algorithm in ES6 using, say, bound functions in a loop, what claims may they validly make about the space complexity of their program? The specification should help answer such questions, sometimes.

Note that I distinguish here between "the space complexity of their program" and the space usage growth when their program is run on an actual implementation. Again, conformance with any requirement on the latter is not testable.

I think we can make it testable.

We'd consider it a spec violation (at least, I would), if this program ran out of space, ever:

var i = 0;
while (1) {  i++; };

similarly, this program should never run out of space:

var i = 0;
function f() { i++; return f(); }
f();

If we write a test that checks counting to a specified number, I think test262 can adequately test this behavior.

# Mark S. Miller (10 years ago)

Practically speaking, I agree.

# Filip Pizlo (10 years ago)

Another way to make this (somewhat) testable is to require specific error.stack behavior for TCO. For example, a call in tail position may require that the caller does not show up in the stack trace.

# Sam Tobin-Hochstadt (10 years ago)

I think this would be a mistake -- as I mentioned, there are a number of possible strategies for stack traces w/ proper tail calls, and as Steve Fink mentioned, these also arise when considering inlining and other optimizations. We shouldn't prevent implementations from trying to experiment with what works best here.

# Jason Orendorff (10 years ago)

On Mon, Sep 29, 2014 at 3:02 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

I can't imagine what you would want be to try to say about non-EMCAScript functions. Their internal "call" semantics is determined by the semantics of their implementation language.

Function.prototype.apply, Function.prototype.call, and Reflect.apply currently call PrepareForTailCall. Is this a bug?

I can see that the current language in 14.6.3 PrepareForTailCall only covers "tail position calls" and "resources associated with the currently executing function execution context", but what's wrong with copying that sentence into 19.2.3.3 and changing it to refer to "the internal method call in the last step of the above algorithm" and "resources associated with the current call to Function.prototype.call"?

The spec constrains the behavior of builtins in all kinds of ways, regardless of what language they're written in. I don't understand what is special about stack space usage that makes it off-limits.

# Allen Wirfs-Brock (10 years ago)

On Sep 29, 2014, at 1:13 PM, Mark S. Miller wrote:

The issue is the asymptotic space consumption almost-contract. The reason I say "almost" is that conformance of an implementation is not testable. Currently, the spec says nothing about when an implementation might run out of storage.

So we (at least I) mean normative only in the following sense: When someone writes an algorithm in ES6 using, say, bound functions in a loop, what claims may they validly make about the space complexity of their program? The specification should help answer such questions, sometimes.

Note that I distinguish here between "the space complexity of their program" and the space usage growth when their program is run on an actual implementation. Again, conformance with any requirement on the latter is not testable.

So what would you like it to say that it doesn't already say?

Here is what it currently says:

14.6.3 Runtime Semantics: PrepareForTailCall ( )

The abstract operation PrepareForTailCall performs the following steps:

1 Let leafContext be the running execution context.
2 Suspend leafContext.
3 Pop leafContext from the execution context context stack. The execution context now on the top of the stack becomes the running execution context.
4 Assert: leafContext has no further use. It will never be activated as the running execution context.

A tail position call must either release any transient internal resources associated with the currently executing function execution context before invoking the target function or reuse those resources in support of the target function.

NOTE For example, a tail position call should only grow an implementation’s activation record stack by the amount that the size of the target function’s activation record exceeds the size of the calling function’s activation record. If the target function’s activation record is smaller, then the total size of the stack should decrease.

# Filip Pizlo (10 years ago)

On Sep 29, 2014, at 12:19 PM, Steve Fink <sphink at gmail.com> wrote:

On 09/29/2014 09:14 AM, Sam Tobin-Hochstadt wrote:

On Mon, Sep 29, 2014 at 10:55 AM, John Lenz <concavelenz at gmail.com> wrote: I really have no idea what the behavior should be in the faces of optimized tail calls (which is must broader than simply self recursive methods that can be rewritten as a loop). I've seen various suggestions (a capped call history) but I'm curious how efficient functional languages deal with this. Different functional languages do a variety of things here:

  • simply show the current stack, without the functions that made tail calls (this is probably the most common)
  • have a bounded buffer for stack traces
  • implement tail calls via a trampoline; this has the side-effect that the stack has "recent" tail calls in it already

I'm sure there are other choices here that people have made.

"Stack traces" are really an overload of (at least?) 3 different concepts:

  1. A record of how execution reached the current state. What debuggers want, mostly.
  2. The continuation from this point on - what function will be returned to when the current function returns normally, recursively up the call chain.
  3. A description of the actual state of the stack.

In all of these, the semantics of the youngest frame are different from all other frames in the stack trace.

For #2, thrown exceptions make the implied continuation ordering a lie, or at least a little more nuanced. You sort of want to see what frames will catch exceptions. (But that's not a trivial determination if you have some "native" frames mixed in there, with arbitrary logic for determining whether to catch or propagate an exception. Even JS frames may re-throw.)

Inlined functions may cause gaps in #1 and #2, unless the implementation takes pains to fill them in with dummy frames (in which case it's not really #3 anymore.)

AFAICT, production compilers already take pains to ensure that they leave behind sufficient meta-data for the runtime to fill in the missing stack frames whenever inlining has happened. This is certainly true in JSC. Crucially, the infrastructure to do this is also needed for other random stuff and it imposes zero overhead.

So let's not compare this to inlining.

# Filip Pizlo (10 years ago)

On Sep 29, 2014, at 1:26 PM, Sam Tobin-Hochstadt <samth at cs.indiana.edu> wrote:

I think this would be a mistake -- as I mentioned, there are a number of possible strategies for stack traces w/ proper tail calls,

But we should spec one of them, particularly if we want to move towards returning the stack trace as structured data. I think it would be good for implementations to be consistent and for the stack trace to not vary depending on which optimization tier you ended up in.

and as Steve Fink mentioned, these also arise when considering inlining and other optimizations.

Nope. Inlining and other optimization a don't typically wreak the havoc on stack traces that TCO does.

We shouldn't prevent implementations from trying to experiment with what works best here.

Is there something concrete that it would prevent anyone from trying?

# Sam Tobin-Hochstadt (10 years ago)

On Mon, Sep 29, 2014 at 4:57 PM, Filip Pizlo <fpizlo at apple.com> wrote:

On Sep 29, 2014, at 1:26 PM, Sam Tobin-Hochstadt <samth at cs.indiana.edu> wrote:

I think this would be a mistake -- as I mentioned, there are a number of possible strategies for stack traces w/ proper tail calls,

But we should spec one of them, particularly if we want to move towards returning the stack trace as structured data. I think it would be good for implementations to be consistent and for the stack trace to not vary depending on which optimization tier you ended up in.

and as Steve Fink mentioned, these also arise when considering inlining and other optimizations.

Nope. Inlining and other optimization a don't typically wreak the havoc on stack traces that TCO does.

A lot of time spent staring at C stack frames where calls have been inlined away disagrees.

We shouldn't prevent implementations from trying to experiment with what works best here.

Is there something concrete that it would prevent anyone from trying?

If we mandate that we get exactly the same stack frames from every implementation, which is what you are suggesting, then that would prevent (some forms of) experimentation. Especially experimentation that would produce better stack traces than what you suggest mandating.

# Allen Wirfs-Brock (10 years ago)

On Sep 29, 2014, at 1:41 PM, Jason Orendorff wrote:

On Mon, Sep 29, 2014 at 3:02 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

I can't imagine what you would want be to try to say about non-EMCAScript functions. Their internal "call" semantics is determined by the semantics of their implementation language.

Function.prototype.apply, Function.prototype.call, and Reflect.apply currently call PrepareForTailCall. Is this a bug?

No, I don't believe so. Built-ins (whether implemented in ES or native) are specified to have an "execution context". The PrepareForTailCall release that execution context for before performing a [[Call]]. It's working in the above functions just like it works from any other call site.

In other words, the spec. language says that these buil;t-ins functions explicitly end with an ECMAScript tail call.

I can see that the current language in 14.6.3 PrepareForTailCall only covers "tail position calls" and "resources associated with the currently executing function execution context", but what's wrong with copying that sentence into 19.2.3.3 and changing it to refer to "the internal method call in the last step of the above algorithm" and "resources associated with the current call to Function.prototype.call"?

The spec constrains the behavior of builtins in all kinds of ways, regardless of what language they're written in. I don't understand what is special about stack space usage that makes it off-limits.

I'm not sure what would be the point of duplicating the language. Since those functions use PrepareForTailCall, what is says applies to them.

# Filip Pizlo (10 years ago)

On Sep 29, 2014, at 2:05 PM, Sam Tobin-Hochstadt <samth at cs.indiana.edu> wrote:

On Mon, Sep 29, 2014 at 4:57 PM, Filip Pizlo <fpizlo at apple.com <mailto:fpizlo at apple.com>> wrote:

On Sep 29, 2014, at 1:26 PM, Sam Tobin-Hochstadt <samth at cs.indiana.edu> wrote:

I think this would be a mistake -- as I mentioned, there are a number of possible strategies for stack traces w/ proper tail calls,

But we should spec one of them, particularly if we want to move towards returning the stack trace as structured data. I think it would be good for implementations to be consistent and for the stack trace to not vary depending on which optimization tier you ended up in.

and as Steve Fink mentioned, these also arise when considering inlining and other optimizations.

Nope. Inlining and other optimization a don't typically wreak the havoc on stack traces that TCO does.

A lot of time spent staring at C stack frames where calls have been inlined away disagrees.

Ugh, that’s so irrelevant! ;-)

C compilers internally know what they inlined and where, but have trouble communicating this information to the debugger because the de facto standard debug data formats are old and messy. I have vague memories of DWARF making this particularly hard. Personally I’ve seen a stigma in the C compiler community against having compilers produce high-fidelity debug data at any optimization level other than -O0, so I suspect that the reason why this hasn’t been fixed is just that nobody really wants to do it. For example the top priority in LLVM’s debug support, last I checked, was to make it as compact as possible because of the dangers of space explosion in the case of C++, templates, and LTO. Seriously, the reasons for why C gets this wrong are completely irrelevant to this discussion. So this is a red herring. An ES VM can internally choose whatever format it wants for remembering inlining metadata, it never has to worry about interoperating with zillions of versions of gdb and lldb, and thankfully ES hasn’t own-goaled itself with anything like C++ templates (yet? ;-)).

Let’s look at some examples of runtimes that are more relevant to ES. Java VMs have for over a decade now performed aggressive inlining without ever forgetting about any frames (in fact they are almost spec-required to remember all frames because of how the runtime APIs work). The closest thing to “forgetting” a frame was in the old IBM JVM (the one before J9); all they would forget was the line numbers in functions that were inlined but the fact that they inlined them was still remembered.

I’m not aware of a major JS implementation forgetting an inlined stack frame.

We shouldn't prevent implementations from trying to experiment with what works best here.

Is there something concrete that it would prevent anyone from trying?

If we mandate that we get exactly the same stack frames from every implementation, which is what you are suggesting, then that would prevent (some forms of) experimentation. Especially experimentation that would produce better stack traces than what you suggest mandating.

Such is life when you spec a language. You have to sometimes spec something in a way that might in the future disagree with someone’s experiment.

# John Lenz (10 years ago)

The issues with "sourceUrl" and syntax errors is fixed in the latest Chrome dev channel and should be making its way to us in the near future. :-)

# John Lenz (10 years ago)

On Mon, Sep 29, 2014 at 12:19 PM, Steve Fink <sphink at gmail.com> wrote:

On 09/29/2014 09:14 AM, Sam Tobin-Hochstadt wrote:

On Mon, Sep 29, 2014 at 10:55 AM, John Lenz <concavelenz at gmail.com> wrote:

I really have no idea what the behavior should be in the faces of optimized

tail calls (which is must broader than simply self recursive methods that

can be rewritten as a loop). I've seen various suggestions (a capped call

history) but I'm curious how efficient functional languages deal with this. Different functional languages do a variety of things here:

  • simply show the current stack, without the functions that made tail calls (this is probably the most common)
  • have a bounded buffer for stack traces
  • implement tail calls via a trampoline; this has the side-effect that the stack has "recent" tail calls in it already

I'm sure there are other choices here that people have made.

"Stack traces" are really an overload of (at least?) 3 different concepts:

  1. A record of how execution reached the current state. What debuggers want, mostly.

This is also important for server side reporting of client side error, which while similar, is not the same as an optionally debugger activated.

# John Lenz (10 years ago)

What does TC39 expect with regard to PTC and the standard-because-everyone-has-one "stack" property? Has any of the VMs actually tried to implement PTC for JS?

# Jason Orendorff (10 years ago)

On Mon, Sep 29, 2014 at 4:06 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Sep 29, 2014, at 1:41 PM, Jason Orendorff wrote:

Function.prototype.apply, Function.prototype.call, and Reflect.apply currently call PrepareForTailCall. Is this a bug?

No, I don't believe so. Built-ins (whether implemented in ES or native) are specified to have an "execution context".

Oh, I see! Thanks. (This is specified in 9.3.1 [[Call]], for anyone following along.)

But in this case, the spec already has some non-ECMAScript functions performing tail calls, so now I am at a loss as to what your earlier line to Brendan could mean:

I can't imagine what you would want be to try to say about non-EMCAScript functions. Their internal "call" semantics is determined by the semantics of their implementation language.

It seems like to the full extent that the current draft manages to constrain Function.prototype.call, it could constrain bound functions too.

# Carl Smith (10 years ago)

On 29 September 2014 23:04, John Lenz <concavelenz at gmail.com> wrote:

The issues with "sourceUrl" and syntax errors is fixed in the latest Chrome dev channel and should be making its way to us in the near future. :-)

Awesome. I can add JavaScript support to the shell :)

I still think the omission of some method for naming eval'ed code in the ES6 spec is unacceptable. It's not a nice-to-have that can be bundled with nicer stack objects, which are relatively unimportant [easily fixed with a regex].

# Allen Wirfs-Brock (10 years ago)

On Sep 29, 2014, at 3:37 PM, Jason Orendorff wrote:

On Mon, Sep 29, 2014 at 4:06 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Sep 29, 2014, at 1:41 PM, Jason Orendorff wrote:

Function.prototype.apply, Function.prototype.call, and Reflect.apply currently call PrepareForTailCall. Is this a bug?

No, I don't believe so. Built-ins (whether implemented in ES or native) are specified to have an "execution context".

Oh, I see! Thanks. (This is specified in 9.3.1 [[Call]], for anyone following along.)

But in this case, the spec already has some non-ECMAScript functions performing tail calls, so now I am at a loss as to what your earlier line to Brendan could mean:

I guess we need to be a bit more careful about what kind of "call" we are talking about.

The ECMAScript spec. execution model uses a stack of "execution contexts" to tracks [[Call]]'s to and returns from function objects. [[Call]]'s to built-in functions are specified as creating an "execution context" so that, from a spec. perspective, both self-hosted and native implementations of built-ins can be treated uniformly within the spec.

The ES tail call resource rules are expressed in terms of manipulating the execution context stack, immediately prior to performing a [[Call]]. Or to put it another way, ES tail calls rules are only about [[Call]] operations. So, ES tail calls can occur in a built-in's that invoke [[Call]]. Generally this is possible if the built-in is specified to immediately return the [[Call]] result.

When I said we couldn't specify tail call behavior for non-ECMAScript functions, I think about the actual "call" semantics used by the implementation language. For example, if F.p.apply is implemented in C++ and if the last thing it does is a C++ call to another C++ function that is the [[Call]] implementation. I can't say anything about how C++ implements that C++ call.

However, from the ES perspective all the C++ execution state that is using to represent such an implementation of F.p.apply is just part of the ES execution context for the [[Call]] to F.p.apply. The C++ code could call thousands of levels deep before it performs its [[Call]] back to an ES function and from the ES perspective all of that C++ stack space is just part of the single F.p.apply execution context.

When we perform PrepareForTailCall in F.p.apply we are saying that the current ES execution context (the one that potentially includes that deep C++ call stack) must be discard (or mae available for reuse) before performing the the subsequent ES [[Call]]. How that is actually accomplished is an implementation "detail".

I strongly support full employment opportunities for language implementations hacker.

I can't imagine what you would want be to try to say about non-EMCAScript functions. Their internal "call" semantics is determined by the semantics of their implementation language.

It seems like to the full extent that the current draft manages to constrain Function.prototype.call, it could constrain bound functions too.

It does. The spec doesn't introduce an additional execution context between the [[Call]] of a bound function and the [[Call]] to the bound functions target. If the [[Call]] to the bound function is in tail position then the caller's execution context is discarded before the [[Call]] to the bound function.

# Brendan Eich (10 years ago)

Allen Wirfs-Brock wrote:

On Sep 29, 2014, at 12:02 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

No particular reason an implementation can't optimize through that if they want to.

The question is whether it should be normative. PTC is about observable asymptotic space performance (I keep saying :-P).

/be

What should be normative?

Something observable. I was picking on your "optimize".

Looks like Jason and others covered the rest of what I was gonna write here, except:

You guys probably should probably review the actual spec. language and see if you have any issues with it.

I've read it, earlier today. You're hearing issues now. :

# Brendan Eich (10 years ago)

Carl Smith wrote:

I still think the omission of some method for naming eval'ed code in the ES6 spec is unacceptable. It's not a nice-to-have that can be bundled with nicer stack objects, which are relatively unimportant [easily fixed with a regex].

See esdiscuss.org/topic/dynamic-compilation-including-eval-and-script-tag-insertion-was-re-clean-scope and possibly other old threads. JJB and I discussed, I forgot whether he implemented.

# Carl Smith (10 years ago)

On 30 September 2014 00:33, Brendan Eich <brendan at mozilla.org> wrote:

Carl Smith wrote:

I still think the omission of some method for naming eval'ed code in the ES6 spec is unacceptable. It's not a nice-to-have that can be bundled with nicer stack objects, which are relatively unimportant [easily fixed with a regex].

See esdiscuss.org/topic/dynamic-compilation-including- eval-and-script-tag-insertion-was-re-clean-scope and possibly other old threads. JJB and I discussed, I forgot whether he implemented.

Thanks Brendan [and for JavaScript]. That discussion, though interesting, seems to deal more with the implementation details, which went over my head a bit to be honest.

Chrome currently supports named evals by concatenating \n//# sourceURL=<filename> to the string before passing it to eval. There's a

longstanding ticket on FF for the pretty much the same thing [ bugzilla.mozilla.org/show_bug.cgi?id=583083]. The script tag workaround feels nasty in a shell, with lots of small inputs, and doesn't work for me ~ we use CoffeeScript.

If the source URL hack, or some cleaner wrapper for it, was standardised, it'd make all the difference.

Thanks again

# Brendan Eich (10 years ago)

Carl Smith wrote:

If the source URL hack, or some cleaner wrapper for it, was standardised, it'd make all the difference.

Why don't we just make the source URL hack a de-facto standard? That's how evolution happens, in the best case. Cc'ing @fitzgen.

# Boris Zbarsky (10 years ago)

On 9/29/14, 4:16 PM, Sam Tobin-Hochstadt wrote:

We'd consider it a spec violation (at least, I would), if this program ran out of space, ever:

 var i = 0;
 while (1) {  i++; };

How would you know whether it does? You can't tell whether your program is terminated because it runs out of space or because the browser has decided it's hung and killed it....

# John Lenz (10 years ago)

It is a defacto standard.

# Brendan Eich (10 years ago)

Put it in a worker or node.js. The point Sam was making was based

# Andreas Rossberg (10 years ago)

On 29 September 2014 19:25, Brendan Eich <brendan at mozilla.org> wrote:

Mark S. Miller wrote:

That's why, IIRC (haven't checked lately), TCO is only specified for calls from non-sloppy functions.

PTC (Proper Tail Calls), not TCO. It's confusing to equate the two, from what I know (corrections welcome0.

Hm, people most often refer to "mandatory tail call optimisation/elimination" when talking about a spec level requirement. I have never seen "PTC" used in a formal context, let alone the acronym.

# Andreas Rossberg (10 years ago)

Boris' point seems to be -- and I agree -- that such a test would only be a semi-decision procedure. I.e., it can only falsify, but not validate the property for the test program.

# Carl Smith (10 years ago)

John Lenz, using source URLs for naming eval'ed code in the traceback provided to window.onerror isn't a de-facto standard; only V8 does it??

# Nick Fitzgerald (10 years ago)

I remember web compat concerns, but if Chrome is exposing the //# sourceURL to the web in error stacks, maybe we can get away with it as well. I'd defer to jorendorff's opinion on this.

We've also discussed exposing the source mapped location of stack frames to JS, but that's even trickier:

  • We don't do any source mapping unless devtools are open, so exposing this would leak whether the user is using devtools or not. Not sure how serious that is, but it makes me hesitant. On the other hand, always source mapping seems impractical, but maybe that's a false assumption.

  • It is a nonstarter to block JS on fetching a source map, so early stack traces would not be source mapped, while later ones would be. This sort of non-determinism makes me feel :( We could introduce a new async method for getting stacks and only source map for these async stacks (or make the new method that other branches of this thread are discussing async).

Interested in hearing everyone's thoughts on this.

# Sam Tobin-Hochstadt (10 years ago)

On Tue, Sep 30, 2014 at 6:56 AM, Andreas Rossberg <rossberg at google.com> wrote:

On 29 September 2014 19:25, Brendan Eich <brendan at mozilla.org> wrote:

Mark S. Miller wrote:

That's why, IIRC (haven't checked lately), TCO is only specified for calls from non-sloppy functions.

PTC (Proper Tail Calls), not TCO. It's confusing to equate the two, from what I know (corrections welcome0.

Hm, people most often refer to "mandatory tail call optimisation/elimination" when talking about a spec level requirement. I have never seen "PTC" used in a formal context, let alone the acronym.

Clinger's paper, which formalizes the concept, calls it "proper tail recursion" in the title, as does the R5RS Scheme standard. Since recursion isn't fundamental, though, proper tail calls seems more accurate. This terminology is used lots of places these days, such as the Lua docs and this (archive of a) post by Guy Steele: www.eighty-twenty.org/index.cgi/tech/oo-tail-calls-20111001.html

# Andreas Rossberg (10 years ago)

On 30 September 2014 16:31, Sam Tobin-Hochstadt <samth at cs.indiana.edu> wrote:

On Tue, Sep 30, 2014 at 6:56 AM, Andreas Rossberg <rossberg at google.com> wrote:

On 29 September 2014 19:25, Brendan Eich <brendan at mozilla.org> wrote:

Mark S. Miller wrote:

That's why, IIRC (haven't checked lately), TCO is only specified for calls from non-sloppy functions.

PTC (Proper Tail Calls), not TCO. It's confusing to equate the two, from what I know (corrections welcome0.

Hm, people most often refer to "mandatory tail call optimisation/elimination" when talking about a spec level requirement. I have never seen "PTC" used in a formal context, let alone the acronym.

Clinger's paper, which formalizes the concept, calls it "proper tail recursion" in the title, as does the R5RS Scheme standard. Since recursion isn't fundamental, though, proper tail calls seems more accurate. This terminology is used lots of places these days, such as the Lua docs and this (archive of a) post by Guy Steele: www.eighty-twenty.org/index.cgi/tech/oo-tail-calls-20111001.html

Interesting, good to know. Thanks for the clarification.

(Still wondering what improper tail calls would be.)

# John Lenz (10 years ago)

I don't believe we want source map involved as, as you say, that information needs to be retrieved separately.

# Carl Smith (10 years ago)

On 30 September 2014 17:28, John Lenz <concavelenz at gmail.com> wrote:

I don't believe we want source map involved as, as you say, that information needs to be retrieved separately.

I seems there's three parts to this. One is allowing evals to be named the way Chrome does, and providing line and column numbers for both caught and uncaught errors. Another is improving the stack traces, providing an array of hashes instead of a string. The last is source map support. I don't personally see any reason to bundle this stuff into one issue.

# Carl Smith (10 years ago)

Just to be clear, you can convert a stack trace into a struct pretty easily, and then do source mapping on it with Mozilla Source Maps. This already works. I can see why a console would provide integrated source map support, but no reason for that to be exposed to client code. It's only a rarely used, pure convenience outside of dev tools.

From a humble Web dev's point of view, the important distinction is between

what I depend on the browser for, and what I can fix myself.

# Carl Smith (10 years ago)

Sorry, I didn't mean to kind of hijack the thread. Just read it back. Really just wanted to share the point of view of someone who actually has to provide tracebacks to the user as a central feature in an app. It's difficult, but doable, but only in Chrome/Opera. Pragmatically, it'd just be nice if FireFox did the named eval thing so it wasn't an engine specific hack. That'd be close enough to a standard for a lot of cases. Other vendors are what they are.

# John Lenz (10 years ago)

Is ES6 "shipping" PTCs without implementer feedback? Or how have those that tried dealt with stack traces?

# Frankie Bagnardi (10 years ago)

I think this is a great idea. I often use stack traces, especially in node.js, for debug error messages and logging. This would make it much simpler to work with them.

Also there are some libraries which end up with a lot of wrapped functions, which could be omitted using an error cleaner function.

I suggest a stackTrace property (getter, no write) of Error objects:

  • .frames is an array of stack frame metadata objects
  • no concept of sourcemaps
  • [[ToString]] is left implementation dependant, allowing them to add information from sourcemaps, or otherwise customize it

This allows for a programmable api, and a human readable version. Also devtools and other code applications (like Carl Smith's) can create rich UIs for this data.

I'm sure the smart people at TC39 can come up with a good (or good enough) way to represent PTCs.

# John Lenz (9 years ago)

I wanted to ping this thread and see how we could get "max-min stack traces" to the next step?

# Mark S. Miller (9 years ago)

On Sat, Mar 7, 2015 at 2:55 PM, John Lenz <concavelenz at gmail.com> wrote:

I wanted to ping this thread and see how we could get "max-min stack traces" to the next step?

Hi John, the best way to take this to the next step is to read < docs.google.com/document/d/1QbEE0BsO4lvl7NFTn5WXWeiEIBfaVUF7Dk0hpPpPDzU/edit>

and submit a proposal to tc39/ecma262.

"If you are a TC39 member representative, just submit a pull request for your proposal."

Since you are at a member organization, attend and participate actively at TC39 meetings to advance your proposal through the process.

Please keep in mind that the stack trace information should not be available simply from the error object by itself, as that is a bad information leak. See the previous es-discuss discussions of the need for something like the getStack function of < code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/debug.js#300>

for rights amplification.

Other proposals-to-be that need to traffic in source positions are

  • source maps
  • passing source position through an eval
  • causality tracking for multi-turn computation; at least deep stacks
  • adding source positions/maps to the template objects of template strings.

Of course, these should be as decoupled as possible. But it's good to keep your eye on the whole picture when you start standards work that depend on source positions.

# John Lenz (9 years ago)

On Mon, Mar 9, 2015 at 12:15 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Mar 7, 2015 at 2:55 PM, John Lenz <concavelenz at gmail.com> wrote:

I wanted to ping this thread and see how we could get "max-min stack traces" to the next step?

Hi John, the best way to take this to the next step is to read < docs.google.com/document/d/1QbEE0BsO4lvl7NFTn5WXWeiEIBfaVUF7Dk0hpPpPDzU/edit> and submit a proposal to tc39/ecma262.

"If you are a TC39 member representative, just submit a pull request for your proposal."

Since you are at a member organization, attend and participate actively at TC39 meetings to advance your proposal through the process.

Please keep in mind that the stack trace information should not be available simply from the error object by itself, as that is a bad information leak.

The threads I dug up, simply state what you state here. That there is an "information leak". Are filename and function names considered sensitive? In what way? I did not intend to promote a "rich stack inspection API" such as V8 has.

# Mark S. Miller (9 years ago)

On Mon, Mar 9, 2015 at 5:02 PM, John Lenz <concavelenz at gmail.com> wrote:

On Mon, Mar 9, 2015 at 12:15 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Mar 7, 2015 at 2:55 PM, John Lenz <concavelenz at gmail.com> wrote:

I wanted to ping this thread and see how we could get "max-min stack traces" to the next step?

Hi John, the best way to take this to the next step is to read < docs.google.com/document/d/1QbEE0BsO4lvl7NFTn5WXWeiEIBfaVUF7Dk0hpPpPDzU/edit> and submit a proposal to tc39/ecma262.

"If you are a TC39 member representative, just submit a pull request for your proposal."

Since you are at a member organization, attend and participate actively at TC39 meetings to advance your proposal through the process.

Please keep in mind that the stack trace information should not be available simply from the error object by itself, as that is a bad information leak.

The threads I dug up, simply state what you state here. That there is an "information leak". Are filename and function names considered sensitive? In what way?

They reveal details of the callee's computation to the caller that the callee should have been able to assume were private. See starting at middle of 2nd paragraph of < combex.com/papers/darpa-review/security-review.html#UniversalScope>.

the depth of the execution stack is visible, which could pose a risk in certain scenarios: for instance, consider trusted code containing a recursive function whose level of recursion depends on some sensitive data (e.g., a secret cryptographic key), and suppose the recursive function is called with arguments that induce it to hit an error condition and throw an exception from deep within the recursion. In such a case, the caller might be able to learn something about the callee’s secrets by catching the exception, examining the resulting stack trace, and recovering the stack depth. These scenarios do not occur in the DarpaBrowser, but have been used in exploits on other systems. Accordingly, though the risk for DarpaBrowser is small, it should probably be repaired (Fixing this was determined not to be hard).

--David Wagner and E. Dean Tribble,
    "A Security Review of the Combex DarpaBrowser Architecture"

Likewise, the risk here -- of only a stack of function names and source positions -- is small. But it violates the normal privacy assumptions between caller and callee; and fixing it is again not hard -- via getStack.

I did not intend to promote a "rich stack inspection API" such as V8 has.

That's good, but there is one thing I really like about the rich inspection API that it would be a shame to lose: The user doesn't have to do their own adhoc parsing of yet another ad hoc textual format. Since this format contains function names, we would then even need to worry about maliciously chosen function names, intended to get this stack format parsing code to misparse. If the stack is a stack of, for example, JSON strings, then we avoid this hazard.

# John Lenz (9 years ago)

On Mon, Mar 9, 2015 at 5:45 PM, Mark S. Miller <erights at google.com> wrote:

On Mon, Mar 9, 2015 at 5:02 PM, John Lenz <concavelenz at gmail.com> wrote:

On Mon, Mar 9, 2015 at 12:15 PM, Mark S. Miller <erights at google.com> wrote:

On Sat, Mar 7, 2015 at 2:55 PM, John Lenz <concavelenz at gmail.com> wrote:

I wanted to ping this thread and see how we could get "max-min stack traces" to the next step?

Hi John, the best way to take this to the next step is to read < docs.google.com/document/d/1QbEE0BsO4lvl7NFTn5WXWeiEIBfaVUF7Dk0hpPpPDzU/edit> and submit a proposal to tc39/ecma262.

"If you are a TC39 member representative, just submit a pull request for your proposal."

Since you are at a member organization, attend and participate actively at TC39 meetings to advance your proposal through the process.

Please keep in mind that the stack trace information should not be available simply from the error object by itself, as that is a bad information leak.

The threads I dug up, simply state what you state here. That there is an "information leak". Are filename and function names considered sensitive? In what way?

They reveal details of the callee's computation to the caller that the callee should have been able to assume were private. See starting at middle of 2nd paragraph of < combex.com/papers/darpa-review/security-review.html#UniversalScope

.

the depth of the execution stack is visible, which could pose a risk in certain scenarios: for instance, consider trusted code containing a recursive function whose level of recursion depends on some sensitive data (e.g., a secret cryptographic key), and suppose the recursive function is called with arguments that induce it to hit an error condition and throw an exception from deep within the recursion. In such a case, the caller might be able to learn something about the callee’s secrets by catching the exception, examining the resulting stack trace, and recovering the stack depth. These scenarios do not occur in the DarpaBrowser, but have been used in exploits on other systems. Accordingly, though the risk for DarpaBrowser is small, it should probably be repaired (Fixing this was determined not to be hard).

--David Wagner and E. Dean Tribble,
    "A Security Review of the Combex DarpaBrowser Architecture"

Likewise, the risk here -- of only a stack of function names and source positions -- is small. But it violates the normal privacy assumptions between caller and callee; and fixing it is again not hard -- via getStack.

I did not intend to promote a "rich stack inspection API" such as V8 has.

That's good, but there is one thing I really like about the rich inspection API that it would be a shame to lose: The user doesn't have to do their own adhoc parsing of yet another ad hoc textual format. Since this format contains function names, we would then even need to worry about maliciously chosen function names, intended to get this stack format parsing code to misparse. If the stack is a stack of, for example, JSON strings, then we avoid this hazard.

Sure, but I feel like that is independent, I mostly want to codify what already exists and standardize throw/rethrow behavior. That is why I ask about the information leak. Error objects already have "stack" properties on all the major browsers. If "stack" leaks information then they already do and the rectification should be there. (It makes no sense to add a "leak-free" API when a "leaky" one already exists).

# Mark S. Miller (9 years ago)

On Chrome and Opera (v8), < code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/debug.js>

hides the stack. It is important that we not lose this.

Regarding the rest, as previously discussed, there are enough differences between browsers that there is no legacy we must codify because of web-wide agreement. Take a look at the extensive efforts < code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/debug.js>

makes to parse despite these differences in stack format. As long as we're standardizing something not compat with web-wide legacy, as we must, we might as well also fix this security leak in the process.

# John Lenz (9 years ago)

Ok, as long as we are clear there is an existing information leak on non-v8 engines.

# Mark S. Miller (9 years ago)

Yes there is. When I have time again, I'll file bugs on them. I should have done this long ago. Thanks.

# Domenic Denicola (9 years ago)

Can’t we just have Error.prototype.stack be a getter that SES is allowed to delete and hide away for its own purposes later?

From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of John Lenz Sent: Wednesday, March 11, 2015 08:35 To: Mark S. Miller Cc: es-discuss; Erik Arvidsson Subject: Re: Maximally minimal stack trace standardization

Ok, as long as we are clear there is an existing information leak on non-v8 engines.

On Tue, Mar 10, 2015 at 1:48 PM, Mark S. Miller <erights at google.com<mailto:erights at google.com>> wrote:

On Chrome and Opera (v8), code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/debug.js hides the stack. It is important that we not lose this.

Regarding the rest, as previously discussed, there are enough differences between browsers that there is no legacy we must codify because of web-wide agreement. Take a look at the extensive efforts code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/debug.js makes to parse despite these differences in stack format. As long as we're standardizing something not compat with web-wide legacy, as we must, we might as well also fix this security leak in the process.

On Tue, Mar 10, 2015 at 1:24 PM, John Lenz <concavelenz at gmail.com<mailto:concavelenz at gmail.com>> wrote:

On Mon, Mar 9, 2015 at 5:45 PM, Mark S. Miller <erights at google.com<mailto:erights at google.com>> wrote:

On Mon, Mar 9, 2015 at 5:02 PM, John Lenz <concavelenz at gmail.com<mailto:concavelenz at gmail.com>> wrote:

On Mon, Mar 9, 2015 at 12:15 PM, Mark S. Miller <erights at google.com<mailto:erights at google.com>> wrote:

On Sat, Mar 7, 2015 at 2:55 PM, John Lenz <concavelenz at gmail.com<mailto:concavelenz at gmail.com>> wrote:

I wanted to ping this thread and see how we could get "max-min stack traces" to the next step?

Hi John, the best way to take this to the next step is to read docs.google.com/document/d/1QbEE0BsO4lvl7NFTn5WXWeiEIBfaVUF7Dk0hpPpPDzU/edit and submit a proposal to tc39/ecma262.

"If you are a TC39 member representative, just submit a pull request for your proposal."

Since you are at a member organization, attend and participate actively at TC39 meetings to advance your proposal through the process.

Please keep in mind that the stack trace information should not be available simply from the error object by itself, as that is a bad information leak.

The threads I dug up, simply state what you state here. That there is an "information leak". Are filename and function names considered sensitive? In what way?

They reveal details of the callee's computation to the caller that the callee should have been able to assume were private. See starting at middle of 2nd paragraph of combex.com/papers/darpa-review/security-review.html#UniversalScope.

the depth of the execution stack is visible, which could pose a risk in certain scenarios: for instance, consider trusted code containing a recursive function whose level of recursion depends on some sensitive data (e.g., a secret cryptographic key), and suppose the recursive function is called with arguments that induce it to hit an error condition and throw an exception from deep within the recursion. In such a case, the caller might be able to learn something about the callee’s secrets by catching the exception, examining the resulting stack trace, and recovering the stack depth. These scenarios do not occur in the DarpaBrowser, but have been used in exploits on other systems. Accordingly, though the risk for DarpaBrowser is small, it should probably be repaired (Fixing this was determined not to be hard).

--David Wagner and E. Dean Tribble,
    "A Security Review of the Combex DarpaBrowser Architecture"

Likewise, the risk here -- of only a stack of function names and source positions -- is small. But it violates the normal privacy assumptions between caller and callee; and fixing it is again not hard -- via getStack.

I did not intend to promote a "rich stack inspection API" such as V8 has.

That's good, but there is one thing I really like about the rich inspection API that it would be a shame to lose: The user doesn't have to do their own adhoc parsing of yet another ad hoc textual format. Since this format contains function names, we would then even need to worry about maliciously chosen function names, intended to get this stack format parsing code to misparse. If the stack is a stack of, for example, JSON strings, then we avoid this hazard.

Sure, but I feel like that is independent, I mostly want to codify what already exists and standardize throw/rethrow behavior. That is why I ask about the information leak. Error objects already have "stack" properties on all the major browsers. If "stack" leaks information then they already do and the rectification should be there. (It makes no sense to add a "leak-free" API when a "leaky" one already exists).

# Mark S. Miller (9 years ago)

No, that makes the std SES API non-conformant to the std API, making porting more difficult, and making it harder to write code that works in both environments.

Also, if you make it look like err.stack, then no matter what you stdize, it will conflict with existing err.stack behavior, since they conflict with each other. This makes the transition more difficult. If the new std behavior looks like getStack(err), then it can be rolled out without creating a transition conflict.

As so often happens, the better security is the better modularity. If you make it err.stack, then you have to make visible one canonical mapping to source positions. If you make it getStack(err), then different getStack functions might map backwards through different sourcemaps.

# Boris Zbarsky (9 years ago)

On 3/10/15 10:45 PM, Domenic Denicola wrote:

Can’t we just have Error.prototype.stack be a getter

This.

Right now, in SpiderMonkey, .stack is a value property on Error instances but I plan to change that in the near future for various reasons to use a getter anyway.

We'll need to figure out what the interaction with DOMException is, but worst-case DOMException.prototype has a separate .stack accessor and SES can kill that off that as well.

# Domenic Denicola (9 years ago)

I don’t see how any of this follows. SES can censor/remove/etc. either the .stack getter or the .getStack function. They are isomorphic.

.stack already has very close to de-facto standard behavior. We should be attempting to converge it to a standard, and not leaving it a non-interoperable mess while adding a second API.

I also don’t see why .stack cannot map backward through different source maps. Again, a getter and a function are isomorphic in this regard.

From: Mark S. Miller [mailto:erights at google.com] Sent: Wednesday, March 11, 2015 12:12 To: Domenic Denicola Cc: John Lenz; es-discuss; Erik Arvidsson Subject: Re: Maximally minimal stack trace standardization

No, that makes the std SES API non-conformant to the std API, making porting more difficult, and making it harder to write code that works in both environments.

Also, if you make it look like err.stack, then no matter what you stdize, it will conflict with existing err.stack behavior, since they conflict with each other. This makes the transition more difficult. If the new std behavior looks like getStack(err), then it can be rolled out without creating a transition conflict.

As so often happens, the better security is the better modularity. If you make it err.stack, then you have to make visible one canonical mapping to source positions. If you make it getStack(err), then different getStack functions might map backwards through different sourcemaps.

On Tue, Mar 10, 2015 at 7:45 PM, Domenic Denicola <d at domenic.me<mailto:d at domenic.me>> wrote:

Can’t we just have Error.prototype.stack be a getter that SES is allowed to delete and hide away for its own purposes later?

From: es-discuss [mailto:es-discuss-bounces at mozilla.org<mailto:es-discuss-bounces at mozilla.org>] On Behalf Of John Lenz

Sent: Wednesday, March 11, 2015 08:35 To: Mark S. Miller Cc: es-discuss; Erik Arvidsson Subject: Re: Maximally minimal stack trace standardization

Ok, as long as we are clear there is an existing information leak on non-v8 engines.

On Tue, Mar 10, 2015 at 1:48 PM, Mark S. Miller <erights at google.com<mailto:erights at google.com>> wrote:

On Chrome and Opera (v8), code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/debug.js hides the stack. It is important that we not lose this.

Regarding the rest, as previously discussed, there are enough differences between browsers that there is no legacy we must codify because of web-wide agreement. Take a look at the extensive efforts code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/debug.js makes to parse despite these differences in stack format. As long as we're standardizing something not compat with web-wide legacy, as we must, we might as well also fix this security leak in the process.

On Tue, Mar 10, 2015 at 1:24 PM, John Lenz <concavelenz at gmail.com<mailto:concavelenz at gmail.com>> wrote:

On Mon, Mar 9, 2015 at 5:45 PM, Mark S. Miller <erights at google.com<mailto:erights at google.com>> wrote:

On Mon, Mar 9, 2015 at 5:02 PM, John Lenz <concavelenz at gmail.com<mailto:concavelenz at gmail.com>> wrote:

On Mon, Mar 9, 2015 at 12:15 PM, Mark S. Miller <erights at google.com<mailto:erights at google.com>> wrote:

On Sat, Mar 7, 2015 at 2:55 PM, John Lenz <concavelenz at gmail.com<mailto:concavelenz at gmail.com>> wrote:

I wanted to ping this thread and see how we could get "max-min stack traces" to the next step?

Hi John, the best way to take this to the next step is to read docs.google.com/document/d/1QbEE0BsO4lvl7NFTn5WXWeiEIBfaVUF7Dk0hpPpPDzU/edit and submit a proposal to tc39/ecma262.

"If you are a TC39 member representative, just submit a pull request for your proposal."

Since you are at a member organization, attend and participate actively at TC39 meetings to advance your proposal through the process.

Please keep in mind that the stack trace information should not be available simply from the error object by itself, as that is a bad information leak.

The threads I dug up, simply state what you state here. That there is an "information leak". Are filename and function names considered sensitive? In what way?

They reveal details of the callee's computation to the caller that the callee should have been able to assume were private. See starting at middle of 2nd paragraph of combex.com/papers/darpa-review/security-review.html#UniversalScope.

the depth of the execution stack is visible, which could pose a risk in certain scenarios: for instance, consider trusted code containing a recursive function whose level of recursion depends on some sensitive data (e.g., a secret cryptographic key), and suppose the recursive function is called with arguments that induce it to hit an error condition and throw an exception from deep within the recursion. In such a case, the caller might be able to learn something about the callee’s secrets by catching the exception, examining the resulting stack trace, and recovering the stack depth. These scenarios do not occur in the DarpaBrowser, but have been used in exploits on other systems. Accordingly, though the risk for DarpaBrowser is small, it should probably be repaired (Fixing this was determined not to be hard).

--David Wagner and E. Dean Tribble,
    "A Security Review of the Combex DarpaBrowser Architecture"

Likewise, the risk here -- of only a stack of function names and source positions -- is small. But it violates the normal privacy assumptions between caller and callee; and fixing it is again not hard -- via getStack.

I did not intend to promote a "rich stack inspection API" such as V8 has.

That's good, but there is one thing I really like about the rich inspection API that it would be a shame to lose: The user doesn't have to do their own adhoc parsing of yet another ad hoc textual format. Since this format contains function names, we would then even need to worry about maliciously chosen function names, intended to get this stack format parsing code to misparse. If the stack is a stack of, for example, JSON strings, then we avoid this hazard.

Sure, but I feel like that is independent, I mostly want to codify what already exists and standardize throw/rethrow behavior. That is why I ask about the information leak. Error objects already have "stack" properties on all the major browsers. If "stack" leaks information then they already do and the rectification should be there. (It makes no sense to add a "leak-free" API when a "leaky" one already exists).

# Mark S. Miller (9 years ago)

On Tue, Mar 10, 2015 at 9:02 PM, Domenic Denicola <d at domenic.me> wrote:

I don’t see how any of this follows. SES can censor/remove/etc. either the .stack getter or the .getStack function. They are isomorphic.

I can selectively provide or deny a given getStack function to different code in the same realm.

.stack already has very close to de-facto standard behavior.

Have you looked at the case analysis I go through in debug.js to parse the variety of stack formats we currently have?

We should be attempting to converge it to a standard, and not leaving it a non-interoperable mess while adding a second API.

I also don’t see why .stack cannot map backward through different source maps. Again, a getter and a function are isomorphic in this regard.

In a given realm, there can only be one Error.prototype.stack. But what getStack function is in scope can differ per scope as well as per loader.

# Domenic Denicola (9 years ago)

From: Mark S. Miller [mailto:erights at google.com]

I can selectively provide or deny a given getStack function to different code in the same realm.

I see, thank you.

I still think given the existence of .stack in existing browsers I'd rather standardize and converge that. But at least I understand your concern better now.

# John Lenz (9 years ago)

On Tue, Mar 10, 2015 at 9:10 PM, Mark S. Miller <erights at google.com> wrote:

On Tue, Mar 10, 2015 at 9:02 PM, Domenic Denicola <d at domenic.me> wrote:

I don’t see how any of this follows. SES can censor/remove/etc. either the .stack getter or the .getStack function. They are isomorphic.

I can selectively provide or deny a given getStack function to different code in the same realm.

Can't you do the same by hiding "Error" in the same way that "window" is hidden? Through a proxy or subclass?

# Mark S. Miller (9 years ago)

I don't understand. Could you show example code? Thanks.

# John Lenz (9 years ago)

I'll retract that suggestion having tried to write an argument for it. It sad though, removing "stack" isn't really an option.

# Mark Miller (9 years ago)

On Wed, Mar 11, 2015 at 6:31 PM, John Lenz <concavelenz at gmail.com> wrote:

I'll retract that suggestion having tried to write an argument for it.

Thanks.

It sad though, removing "stack" isn't really an option.

a) It has never been "added", in the sense that it does not appear in the std. b) It differs so wildly between platforms that cross-web content can't rely on it. c) We removed ".caller", ".callee", and ".arguments". I'm sure that in 2007 many would have bet that we never could.

A further point about selectively providing or denying getStack: Something that's acting like a console typically needs the privilege of extracting stacks from thrown errors. The callers of the console almost never do. Rather, the typical pattern is they pass the errors to the console as method arguments. They may know that the error contains a stack, and their purpose in passing it may be to show that stack to the user/programmer, but it is very rare that they ever look at the stack themselves.

code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/useHTMLLogger.js

uses exactly this pattern to create a somewhat console-like display on a normal html page, having been granted the getStack ability that its caller's typically don't get. It's really just a special case of the general principle that debugging tools are typically granted more privilege with respect to the computation being debugged than the objects within that computation are.

# John Lenz (9 years ago)

On Wed, Mar 11, 2015 at 6:53 PM, Mark Miller <erights at gmail.com> wrote:

On Wed, Mar 11, 2015 at 6:31 PM, John Lenz <concavelenz at gmail.com> wrote:

I'll retract that suggestion having tried to write an argument for it.

Thanks.

It sad though, removing "stack" isn't really an option.

a) It has never been "added", in the sense that it does not appear in the std.

The Web is full of tech that appeared by consensus not spec.

b) It differs so wildly between platforms that cross-web content can't rely on it.

It exists everywhere and from where I sit it is pretty essential and there is lot of wiring that exist to propagate and preserve stack traces.

c) We removed ".caller", ".callee", and ".arguments". I'm sure that in 2007 many would have bet that we never could.

We never removed "callee", all the implementations still support it, there is just a way to opt out it of being there. I doubt anyone would suggest "use more strict" to remove "stack" from Exception traces. It is certainly possible to add a non-mode way of opt-ing out of stack traces.

A further point about selectively providing or denying getStack: Something that's acting like a console typically needs the privilege of extracting stacks from thrown errors. The callers of the console almost never do. Rather, the typical pattern is they pass the errors to the console as method arguments. They may know that the error contains a stack, and their purpose in passing it may be to show that stack to the user/programmer, but it is very rare that they ever look at the stack themselves.

code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/useHTMLLogger.js

uses exactly this pattern to create a somewhat console-like display on a normal html page, having been granted the getStack ability that its caller's typically don't get. It's really just a special case of the general principle that debugging tools are typically granted more privilege with respect to the computation being debugged than the objects within that computation are.

Here are the cases I care about:

(1) reporting in the field exceptions back to the home server (2) async programming models that build multi-stage stack traces (3) like the async case - exception with transitive exception causes (exception of Foo) (4) custom error building logic that hides some bit of the stack trace for readability (the last call or two, or some such)

And wiring to support the same. My goal when I started this thread was to minimize the browser differences, not to create a new API that would require the error reporting world to reinvent itself.

For SES, perhaps a system level hook would be sufficient to allow a weak-map getStack API without discard "stack" in normal usage.

# Brendan Eich (9 years ago)

John Lenz wrote:

b) It differs so wildly between platforms that cross-web content
can't rely on it.

It exists everywhere and from where I sit it is pretty essential and there is lot of wiring that exist to propagate and preserve stack traces.

Yeah, I'd be careful concluding too much from the format differeing. Libraries such as Mark's can cope, and have. So it may be a browser-specific de-facto standard of sorts. We could still do better by leaving it unspecified, because it's browser-specific, and providing a cross-browser, well-designed and -tested de-jure alternative. Is that a static Error.getStack(e) method?

# John Lenz (9 years ago)

I'm under the impression Error.getStack would suffer the same scoping issues and it would have to be a module import that a custom loader could override or a global method that could be hidden by scoping. This doesnt seem like a pleasant API and seems like something to be relegated to SES/Caja specifically. But hopefully, I'm missing something.

# Brendan Eich (9 years ago)

Right, Mark pointed out shadowing as an advantage of using just a getStack lexical name. Need to see more to judge pleasantness.