Maximally minimal stack trace standardization
allen at wirfs-brock.com
Mon Sep 29 16:24:30 PDT 2014
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
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.
More information about the es-discuss