Specification styles

# Boris Zbarsky (10 years ago)

On 1/30/14 8:25 AM, Domenic Denicola wrote:

since I find that style more precise and idiomatic

It's not clear to me that the former is true (and in fact, making sure that an ES-style spec is not fundamentally buggy in the "doesn't have the desired behavior" sense is much harder than doing it for a WebIDL spec, in my experience).

It's also much harder to read and understand in my experience, and the experience of many other people I've talked to.

I would be interested in knowing what aspects of what you want to specify cannot be expressed in WebIDL + normal DOM spec style.....

and potentially in the future streams could become a language-level feature.

Quite honestly, I would appreciate it if language-level features in ES were not defined with as much error-prone and hard-to-read boilerplate as they are now.

Basically, I want to get ahead of the situation TextEncoder/TextDecoder find themselves in

What "situation" is that, exactly?

# Domenic Denicola (10 years ago)

From: es-discuss <es-discuss-bounces at mozilla.org> on behalf of Boris Zbarsky <bzbarsky at MIT.EDU>

I would be interested in knowing what aspects of what you want to specify cannot be expressed in WebIDL + normal DOM spec style.....

Well, there's a lot of issues. The lack of data properties; the fact that all argument coercion and type-checking happens up-front (verus just-in-time checking if a callable property exists and throwing if it does not, for example); the inability to change return types based on the input (e.g. return new this.constructor(...) or return firstArg); the lack of care in handling abrupt completions; the lack of formalization around internal slots; the inability to support the ES6 inheritance and constructor model; the constant appeal to vague prose phrases; the lack of support for iterators; ...

Some of the type-related stuff could be handled within a WebIDL-using specification, by simply using optional any liberally and doing the work manually. Other issues are areas where WebIDL simply hasn't kept up with ECMAScript, and I imagine it will evolve to do so in the future. But the bottom line is, I want to be able to use the capabilities that JavaScript gives me, and second to writing JavaScript itself, ECMASpeak is a much better language for doing so than WebIDL + web-spec-style prose.

What "situation" is that, exactly?

I was referring in particular to the contents of the referenced thread, wherein we discussed ways in which TextDecoder/TextEncoder didn't align with the ECMAScript model, e.g. exception type usage.

# Forrest L Norvell (10 years ago)

On Thu, Jan 30, 2014 at 9:40 AM, Boris Zbarsky <bzbarsky at mit.edu> wrote:

It's not clear to me that the former is true (and in fact, making sure that an ES-style spec is not fundamentally buggy in the "doesn't have the desired behavior" sense is much harder than doing it for a WebIDL spec, in my experience).

It's also much harder to read and understand in my experience, and the experience of many other people I've talked to.

I am going through the process of implementing Domenic's spec in ES5 for Node as a way of validating my understanding of the spec (in reality, the only Nodeisms in my implementation is my use of CommonJS modules), so maybe I can shed a little light on this.

So far, this has been a very straightforward spec to implement. The spec language is imperative and concrete, so it is easy both to turn into JavaScript and to point to the correspondences between the implementation code and the relevant line items in the spec. So far, it has also been easy to use the code to illuminate inconsistencies or identify problems with the spec, and using the nomenclature of ES-262 makes very clear from context how to distinguish between the public interface of the API and its internal properties and methods.

I've been surprised (and pleased) by how easy it has been to implement the spec thus far, and most of the issues I've raised with Domenic have been down to either minor misunderstandings on my part or judgment calls without objectively right or wrong answers. To me, this feels like the appropriate sort of issues to be coming out of working with a draft specification. Much of the credit for that goes to Domenic and his collaborators (and probably also to my familiarity with other JavaScript streams implementations), but I appreciate not having to spend a lot of time translating between WebIDL's abstract hybrid of C++, Java and JavaScript type models and the code I'm working on.

As an implementor, I find this all a refreshing contrast to trying to wrap my head around WebIDL, which, in my blunt opinion, does a poor job of capturing any real operational model used on the web. I have nothing against formal specification methods per se, but, at least for things that are implementable in pure JavaScript (as Domenic's proposal is), I think ECMASpeak is preferable to WebIDL.

# Boris Zbarsky (10 years ago)

On 1/30/14 9:52 AM, Domenic Denicola wrote:

The lack of data properties

That's fair.

the fact that all argument coercion and type-checking happens up-front (verus just-in-time checking if a callable property exists and throwing if it does not, for example)

This is a philosophical disagreement, agreed.

the inability to change return types based on the input

WebIDL has this ability. Either union types or object/any, depending on how much you want to change it.

the lack of care in handling abrupt completions

Uh... WebIDL does in fact define the handling of those if you operate on WebIDL objects.

Obviously if you want to operate on "object" or "any" you have to do it yourself as now.

the lack of formalization around internal slots

This would look the same way in a WebIDL spec as in yours.

the inability to support the ES6 inheritance and constructor model

WebIDL is supposed to support this. If it doesn't, please file bugs.

the constant appeal to vague prose phrases

Can you give an example?

the lack of support for iterators

See discussion about sequence<>?

I was referring in particular to the contents of the referenced thread, wherein we discussed ways in which TextDecoder/TextEncoder didn't align with the ECMAScript model, e.g. exception type usage.

Exception type usage is the issue you're having in your ES version as well, right? Seems like that's something that just needs to be fixed as needed and doesn't depend so much on the specification formalism.

# Boris Zbarsky (10 years ago)

On 1/30/14 9:40 AM, Boris Zbarsky wrote:

It's not clear to me that the former is true (and in fact, making sure that an ES-style spec is not fundamentally buggy in the "doesn't have the desired behavior" sense is much harder than doing it for a WebIDL spec, in my experience).

Let me give a concrete example, actually. Any web specification that involves asynchronously invoking callbacks from the event loop needs to define the incumbent and entry script settings object used when the callback is invoked. If you use WebIDL callbacks, you get this for free, since those define the behavior for you. I don't see the spec under discussion here defining anything like that; presumably it was just overlooked. Preventing that sort of "overlooked" mistake is one of the major goals we had with WebIDL.

I'm not going to make the absurd claim that WebIDL is perfect. I will claim that we want to make it easier to write web specs that don't make basic mistakes like that. And we want to make it easier to write web specs that use the ES capabilities you describe. A desirable property is that even people who are not intimately familiar with the edge cases of ES and the web platform can write specifications describing some sort of basic functionality without tripping up in all sorts of ways.

The question is how we get there. Constructive ideas are very much welcome.

# Rick Waldron (10 years ago)

On Thu, Jan 30, 2014 at 1:20 PM, Forrest L Norvell <forrest at newrelic.com>wrote:

So far, this has been a very straightforward spec to implement. The spec language is imperative and concrete, so it is easy both to turn into JavaScript and to point to the correspondences between the implementation code and the relevant line items in the spec. So far, it has also been easy to use the code to illuminate inconsistencies or identify problems with the spec,

This is excellent, please be sure to file any spec bugs of any nature here: bugs.ecmascript.org Feedback based on implementation experience is critical to ES6 success :)

# Boris Zbarsky (10 years ago)

On 1/30/14 10:20 AM, Forrest L Norvell wrote:

So far, this has been a very straightforward spec to implement.

Forrest,

I think I may have been a bit unclear, my apologies.

I think we all agree that ES-style specifications are in fact more straightforward to transcribe into an ES implementation.

What is harder, in my experience, is understanding whether the specification actually says what it means to say and determining whether there are bugs in the specification. This arises to some extent from the core functionality being partially obscured by a fair amount of boilerplate.

Now I agree this is somewhat subjective. Some people prefer to have all the complications explicitly written out, both in specifications and in code, while others prefer reusable tested black boxes that have certain defined behavior. The former approach is much more verbose and can thus hinder understanding of the big picture. The latter approach involves in-depth understanding of the black boxes to understand the details. In practice, both the WebIDL and ES styles use some mixture of the two; what differs is the exact set of black boxes used. The ES style black boxes are mostly more fine-grained than the WebIDL ones.

I wish I had a good way to combine the strengths of the two approaches more than existing specification formalisms do right now. In some ways, something that allows looking at higher-level definition in terms of black boxes and then on-demand (e.g. a "expand this" or "expand all in this section" link) allows the reader to inline the black boxes to see all the details might be a possibility. That would involve creating some tooling, obviously.

As an implementor, I find this all a refreshing contrast to trying to wrap my head around WebIDL

I honestly think that this (just like other people's experiences with trying to wrap their heads around ECMASpeak) is at least partially due to familiarity, or lack thereof, with the black boxes being used as specification primitives... Partially it may be due to the wrong primitives being used, of course, which is a real problem in both styles I believe.

I have nothing against formal specification methods per se, but, at least for things that are implementable in pure JavaScript (as Domenic's proposal is)

It's not, actually, since it requires asynchronous callbacks which do not currently exist in pure JavaScript. This is not quite a nitpick, since properly defining asynchronous callbacks is a fairly nontrivial matter with security implications, as I already pointed out in this thread.

# Forrest L Norvell (10 years ago)

On Thu, Jan 30, 2014 at 11:07 AM, Boris Zbarsky <bzbarsky at mit.edu> wrote:

On 1/30/14 10:20 AM, Forrest L Norvell wrote:

So far, this has been a very straightforward spec to implement.

Forrest,

I think I may have been a bit unclear, my apologies.

I think we all agree that ES-style specifications are in fact more straightforward to transcribe into an ES implementation.

What is harder, in my experience, is understanding whether the specification actually says what it means to say and determining whether there are bugs in the specification. This arises to some extent from the core functionality being partially obscured by a fair amount of boilerplate.

I agree with this entirely, with the caveat that "partially obscured" and even "boilerplate" are in this case subjective factors. Once you split Domenic's streams documentation into two pieces -- a narrative, informal piece explaining the design and its motivation, and the specification proper, which is still informal but more organized -- the specification itself seems neither particularly verbose nor overdetermined. Again, this is my subjective interpretation, but I've spent a fair amount of time dealing with specifications (and, just to be clear, mostly not in the JavaScript world).

Neither WebIDL nor the style of ES-262 are formal specification languages, nor do they specify any kind of rigorous operational semantics. This isn't a problem, as that's not their purpose. Their purpose, as I understand it, is to facilitate the specification of behavior in such a way that a consensus can be formed as to whether a given implementation is congruent with a given design (and, more nebulously, the designers' intent).

Now I agree this is somewhat subjective. Some people prefer to have all

the complications explicitly written out, both in specifications and in code, while others prefer reusable tested black boxes that have certain defined behavior. The former approach is much more verbose and can thus hinder understanding of the big picture. The latter approach involves in-depth understanding of the black boxes to understand the details. In practice, both the WebIDL and ES styles use some mixture of the two; what differs is the exact set of black boxes used. The ES style black boxes are mostly more fine-grained than the WebIDL ones.

I agree with this description, and I wish there were a way to support both approaches. Neither the declarative style of WebIDL nor the imperative style of ES-262 are good at capturing both at the same time. As designers and researchers we want to be able to point to the formal model and, if not prove, at least attempt to make strong assertions about particular behaviors or abstractions. As implementors, it's much easier to work from imperative descriptions of behavior, and the process of resolving the ambiguities during implementation will surface aspects of the design that are not obvious when looking at a declarative model.

I spoke up in this thread because my primary purpose in taking on the effort of implementing Domenic's spec was to figure out how I feel about it (spoiler: so far I feel pretty good!). The easiest way for me to get up to speed was to write some code (and tests) to see what gotchas popped up in the real world (as streams have historically been probably the most fertile source of edge cases in Node). For this purpose, Domenic's choice of specification style has been ideal, for me.

I wish I had a good way to combine the strengths of the two approaches more than existing specification formalisms do right now. In some ways, something that allows looking at higher-level definition in terms of black boxes and then on-demand (e.g. a "expand this" or "expand all in this section" link) allows the reader to inline the black boxes to see all the details might be a possibility. That would involve creating some tooling, obviously.

I think the underlying problem is that neither of these are truly formal, at least not in the sense that I understand it. There's structure and some essays at rigor, but nothing like, say, the recent implementation of ES5 in Coq (which is awesome) or a Robert Harper-style operational semantics rooted in axiomatic logic. On the other hand, I don't think formalism is really what WebIDL is after.

As an implementor, I find this all a refreshing contrast to trying to

wrap my head around WebIDL

I honestly think that this (just like other people's experiences with trying to wrap their heads around ECMASpeak) is at least partially due to familiarity, or lack thereof, with the black boxes being used as specification primitives... Partially it may be due to the wrong primitives being used, of course, which is a real problem in both styles I believe.

I'm fine with black boxes, and referring to standards documents when decrypting specifications (I've had to work with ASN.1, I did a fair amount of CORBA development with its IDL once upon a time, etc). I just find the level of abstraction chosen for WebIDL inappropriate for JavaScript. For example, WebIDL's pretense that there are multiple numeric types in the web platform, when in fact there is only a relatively narrow domain (TypedArrays, browser / DOM objects that aren't hosted in JavaScript, etc) where that kind of typing is applicable.

I understand that tradeoffs are necessary to support specifications that cross such a wide domain of use cases and implementation technologies, but WebIDL just feels at odds with the way I understand the web platform to work. It's a very imprecise mapping, I guess is how I feel.

I have nothing against formal specification methods per se, but, at least

for things that are implementable in pure JavaScript (as Domenic's proposal is)

It's not, actually, since it requires asynchronous callbacks which do not currently exist in pure JavaScript. This is not quite a nitpick, since properly defining asynchronous callbacks is a fairly nontrivial matter with security implications, as I already pointed out in this thread.

It's interesting that you raise this issue, because all of the aspects involving asynchronicity and callbacks in Domenic's spec do so in the context of ES promises, and unless I'm missing something, all of the trickiness around it is handled by the promises specification, leaving the streams proposal as a simple consumer of that spec. Am I missing something?

F

# Allen Wirfs-Brock (10 years ago)

On Jan 30, 2014, at 11:52 AM, Forrest L Norvell wrote:

On Thu, Jan 30, 2014 at 11:07 AM, Boris Zbarsky <bzbarsky at mit.edu> wrote: ... I think we all agree that ES-style specifications are in fact more straightforward to transcribe into an ES implementation.

What is harder, in my experience, is understanding whether the specification actually says what it means to say and determining whether there are bugs in the specification. This arises to some extent from the core functionality being partially obscured by a fair amount of boilerplate.

I agree with this entirely, with the caveat that "partially obscured" and even "boilerplate" are in this case subjective factors. Once you split Domenic's streams documentation into two pieces -- a narrative, informal piece explaining the design and its motivation, and the specification proper, which is still informal but more organized -- the specification itself seems neither particularly verbose nor overdetermined. Again, this is my subjective interpretation, but I've spent a fair amount of time dealing with specifications (and, just to be clear, mostly not in the JavaScript world).

Some of the boiler plate is just a legacy (go look at the ES3 spec) and I'm slowly eliminating some of the verbosity. But it is a fairly low priority task in the overall effort of finishing ES6.

Neither WebIDL nor the style of ES-262 are formal specification languages, nor do they specify any kind of rigorous operational semantics. This isn't a problem, as that's not their purpose. Their purpose, as I understand it, is to facilitate the specification of behavior in such a way that a consensus can be formed as to whether a given implementation is congruent with a given design (and, more nebulously, the designers' intent).

Now I agree this is somewhat subjective. Some people prefer to have all the complications explicitly written out, both in specifications and in code, while others prefer reusable tested black boxes that have certain defined behavior. The former approach is much more verbose and can thus hinder understanding of the big picture. The latter approach involves in-depth understanding of the black boxes to understand the details. In practice, both the WebIDL and ES styles use some mixture of the two; what differs is the exact set of black boxes used. The ES style black boxes are mostly more fine-grained than the WebIDL ones.

I agree with this description, and I wish there were a way to support both approaches. Neither the declarative style of WebIDL nor the imperative style of ES-262 are good at capturing both at the same time. As designers and researchers we want to be able to point to the formal model and, if not prove, at least attempt to make strong assertions about particular behaviors or abstractions. As implementors, it's much easier to work from imperative descriptions of behavior, and the process of resolving the ambiguities during implementation will surface aspects of the design that are not obvious when looking at a declarative model.

The ES6 spec. is much more detailed and prescriptive than any other language specification I've ever been involved with. This is intentional and motivated by the fact that the interoperability expectations across multiple independently developed implementations of the language are much high than any other language I've ever encountered. In addition, the collective experience in TC39 is that if something (for example various edge cases) is under specified in an ES spec. then implementations will inevitably have observable differences and that some JS programmers will write code that is sensitive to the differences. Hence, ES6 tries to specify the handling of all edge cases and the ordering of all observable side-effects.

Other standards might choose to use common test cases or other techniques to ensure interoperability. That isn't the path TC39 has taken.

# Boris Zbarsky (10 years ago)

On 1/30/14 11:52 AM, Forrest L Norvell wrote:

I agree with this entirely, with the caveat that "partially obscured" and even "boilerplate" are in this case subjective factors.

Absolutely. In fact, it sounds like we agree on most of this stuff in general. ;)

It's interesting that you raise this issue, because all of the aspects involving asynchronicity and callbacks in Domenic's spec do so in the context of ES promises, and unless I'm missing something

Hmm... It's hard to tell. I'm looking at things like [[callPull]](), which say:

When/if this.[[startedPromise]] is fulfilled, call this.[[onPull]](this.[[push]], this.[[close]], this.[[error]]).

I don't think this is explicitly defining what the script settings stack looks like when the [[onPull]] function is called, because the "When/if this.[[startedPromise]] is fulfilled" wording doesn't tell me exactly how this interacts with the promise specification and its manipulation of that stack. Is this supposed to have equivalent behavior to doing:

  this.[[startedPromise]].then(
    this.[[onPull]].bind(undefined, this.[[push]],
                         this.[[close]], this.[[error]]))

or something else? If this is supposed to be like a then() call, then I agree that promises would completely define the behavior here. But if this is just saying that at some point after the promise is fulfilled the [[onPull]] needs to be called, then the behavior is not defined by Promises.

Unfortunately, the promise specification doesn't handle incumbent/entry settings objects either, so the behavior is undefined no matter what. :( It needs to.

# Anne van Kesteren (10 years ago)

On Thu, Jan 30, 2014 at 10:55 PM, Boris Zbarsky <bzbarsky at mit.edu> wrote:

Unfortunately, the promise specification doesn't handle incumbent/entry settings objects either, so the behavior is undefined no matter what. :( It needs to.

Could you maybe give an explicit example? I think that would help in understanding the bug in the promises specification. I suspect not everyone here is familiar with what you are referring to.

# Boris Zbarsky (10 years ago)

On 2/3/14 12:35 PM, Anne van Kesteren wrote:

Could you maybe give an explicit example? I think that would help in understanding the bug in the promises specification. I suspect not everyone here is familiar with what you are referring to.

Sure. Given a promise named "somePromise", something needs to define the behavior of the following two cases:

  1. somePromise.then(window.postMessage.bind(window, 5, "*"));
    

2. ```js
 var set = Object.getOwnPropertyDescriptor(location, "href").set;
 var navigate = set.bind(location, "relativeURI");
 somePromise.then(navigate);

The incumbent settings object (aka "the script you were called from") defines the behavior of case #1: it determines the .origin property of the resulting MessageEvent.

The entry settings object (aka "that weird backwards-compat thing Location needs, which is kinda where you entered JS execution from browser code, whatever that means") defines the behavior of case #2: it determines the base URI used for resolving the "relativeURI" string.

My real point was that we don't want to make every single specification writer worry about edge cases like this and various other ones scattered about the web platform unless we absolutely have to. Especially since the "sane" behavior in the cases above is not trivially obvious.

P.S. For the record, if Promise.prototype.then took a WebIDL callback as an argument the behavior would be as follows:

In case 1 the origin is the origin of the incumbent settings object at the time the "then()" call was made. If that happened from a script, it's the origin of that script; if it happened via more bind/callback games then it depends on what those callbacks pushed on the script settings stack.

In case 2 the base URI is the base URI of the global associated with the "navigate" Function object.

P.P.S. You can add some curliques to case #2 by grabbing the setter from one global, the location object from a second global, calling Function.prototype.bind from a third global, having the promise come from a fourth global, having the "then" method come from a fifth global and binding it to the promise using Function.prototype.bind from a sixth global. Just to keep people on their toes and all.

# Domenic Denicola (10 years ago)

I don't see the confusion here. The promises spec is explicit that it calls the passed function's internal [[Call]] method, with undefined as thisArgument.

Are you saying that [[Call]] is not well-defined for window.postMessage.bind(window, 5, "*") or for set.bind(location, "relativeURI")? That seems strange. If that's the case the passed objects will not pass the IsCallable tests. But I don't believe any browsers behave this way.

# Boris Zbarsky (10 years ago)

On 2/3/14 1:05 PM, Domenic Denicola wrote:

Are you saying that [[Call]] is not well-defined for window.postMessage.bind(window, 5, "*") or for set.bind(location, "relativeURI")? That seems strange.

I'm saying that the algorithm in the [[Call]] of those objects (or rather of the functions being bound in this case) explicitly references a part of the environment called the "script settings object stack" and expects whoever plans to invoke that [[Call]] to maintain it as needed.

I'm also saying that promises as currently specified in domenic/promises-unwrapping do not maintain that stack. In particular, they can invoke the [[Call]] above with the stack empty, and the behavior of that is not defined as far as I can tell.

# Boris Zbarsky (10 years ago)

On 2/3/14 1:10 PM, Boris Zbarsky wrote:

I'm saying that the algorithm in the [[Call]] of those objects (or rather of the functions being bound in this case) explicitly references a part of the environment called the "script settings object stack" and expects whoever plans to invoke that [[Call]] to maintain it as needed.

I'm also saying that promises as currently specified in domenic/promises-unwrapping do not maintain that stack. In particular, they can invoke the [[Call]] above with the stack empty, and the behavior of that is not defined as far as I can tell.

But even more importantly, I'm saying that all of this stuff with the script settings object stack is something that most specification authors shouldn't have to think about, and that we should adopt specification formalisms that do not force them to think about it.

# Domenic Denicola (10 years ago)

From: Boris Zbarsky <bzbarsky at MIT.EDU>

I'm saying that the algorithm in the [[Call]] of those objects (or rather of the functions being bound in this case) explicitly references a part of the environment called the "script settings object stack" and expects whoever plans to invoke that [[Call]] to maintain it as needed.

This expectation seems untenable. It should be the job of a function that depends on global state to specify how that dependency on global state works.

What I am understanding from this is that DOM specs have forked the ES spec's definition of [[Call]] to include a hidden "script settings object stack" parameter, which ES is not aware of. It doesn't seem reasonable for ES to take into account these forked semantics, and add a "with script object settings stack X" to each invocation of [[Call]]. Instead, the DOM should specify the details of its forking, and all the places that invoke [[Call]] in ES need to also be forked to describe what parameter is passed for the script settings object stack parameter.

# Allen Wirfs-Brock (10 years ago)

On Feb 3, 2014, at 9:53 AM, Boris Zbarsky wrote:

On 2/3/14 12:35 PM, Anne van Kesteren wrote:

Could you maybe give an explicit example? I think that would help in understanding the bug in the promises specification. I suspect not everyone here is familiar with what you are referring to.

Sure. Given a promise named "somePromise", something needs to define the behavior of the following two cases:

  1. somePromise.then(window.postMessage.bind(window, 5, "*"));

  2. var set = Object.getOwnPropertyDescriptor(location, "href").set; var navigate = set.bind(location, "relativeURI"); somePromise.then(navigate);

The incumbent settings object (aka "the script you were called from") defines the behavior of case #1: it determines the .origin property of the resulting MessageEvent.

The entry settings object (aka "that weird backwards-compat thing Location needs, which is kinda where you entered JS execution from browser code, whatever that means") defines the behavior of case #2: it determines the base URI used for resolving the "relativeURI" string.

My real point was that we don't want to make every single specification writer worry about edge cases like this and various other ones scattered about the web platform unless we absolutely have to. Especially since the "sane" behavior in the cases above is not trivially obvious.

It's hard to even talk about this because the above is filled with concepts and assumptions that don't currently occur anywhere in the ES specification. For example, what is a location and what does bind have to do with anything? In ES semantic, all bind does is is create a filtering mechanism that passes a fixed value to the target function.

ES6 does provide the concept of Realms (approximately a global environment) and a immutable association between scripts/functions and Realms. Is it possible to frame the discussion in those terms?

-Boris

P.S. For the record, if Promise.prototype.then took a WebIDL callback as an argument the behavior would be as follows:

In case 1 the origin is the origin of the incumbent settings object at the time the "then()" call was made. If that happened from a script, it's the origin of that script; if it happened via more bind/callback games then it depends on what those callbacks pushed on the script settings stack.

In case 2 the base URI is the base URI of the global associated with the "navigate" Function object.

Are you suggesting that all uses of ECMAScript promises in all hosting environments must have a dependency upon WebIDL callback semantics?

P.P.S. You can add some curliques to case #2 by grabbing the setter from one global, the location object from a second global, calling Function.prototype.bind from a third global, having the promise come from a fourth global, having the "then" method come from a fifth global and binding it to the promise using Function.prototype.bind from a sixth global. Just to keep people on their toes and all.

What I most don't understand about all of the above is what F.p.bind has to do with all of this?

# Allen Wirfs-Brock (10 years ago)

On Feb 3, 2014, at 10:12 AM, Boris Zbarsky wrote:

On 2/3/14 1:10 PM, Boris Zbarsky wrote:

I'm saying that the algorithm in the [[Call]] of those objects (or rather of the functions being bound in this case) explicitly references a part of the environment called the "script settings object stack" and expects whoever plans to invoke that [[Call]] to maintain it as needed.

I'm also saying that promises as currently specified in domenic/promises-unwrapping do not maintain that stack. In particular, they can invoke the [[Call]] above with the stack empty, and the behavior of that is not defined as far as I can tell.

But even more importantly, I'm saying that all of this stuff with the script settings object stack is something that most specification authors shouldn't have to think about, and that we should adopt specification formalisms that do not force them to think about it.

Speaking from the perspective of a ES language designer WTF is a "script setting object stack" and why have I never heard about it?

# Boris Zbarsky (10 years ago)

On 2/3/14 1:15 PM, Domenic Denicola wrote:

From: Boris Zbarsky <bzbarsky at MIT.EDU>

I'm saying that the algorithm in the [[Call]] of those objects (or rather of the functions being bound in this case) explicitly references a part of the environment called the "script settings object stack" and expects whoever plans to invoke that [[Call]] to maintain it as needed.

This expectation seems untenable.

It's completely tenable in today's world where all JS is run to completion any anything async is happening via the browser event loop and WebIDL; those maintain the stack in question across async invocations, and the behavior of the stack across synchronous calls is defined in the whatwg specification. To the extent that the latter is true, you could argue that it did fork [[Call]] on scripted functions in ES, I guess...

As we add an event loop to ES, we will need to maintain that stack in ES, presumably, or some equivalent. But right now we don't.

It should be the job of a function that depends on global state to specify how that dependency on global state works.

It's defined. The global state just needs to be there.

What I am understanding from this is that DOM specs have forked the ES spec's definition of [[Call]] to include a hidden "script settings object stack" parameter, which ES is not aware of.

You can view it that way if you want, I suppose. Or you can view implementations as having implicit things like that already, for postMessage and the like.

But again, you're ratholing on this particular example. My point was that there are lots of edge cases like this in the web platform that we shouldn't force every single spec author to think about. Some of them are due to DOM things, some are due to ES things. To the extent possible, we should make it possible to write correct and readable specifications that do the right thing in these edge cases even if the specification author wasn't aware of the complications.

# Domenic Denicola (10 years ago)

From: Boris Zbarsky <bzbarsky at MIT.EDU>

But again, you're ratholing on this particular example. My point was that there are lots of edge cases like this in the web platform that we shouldn't force every single spec author to think about. Some of them are due to DOM things, some are due to ES things. To the extent possible, we should make it possible to write correct and readable specifications that do the right thing in these edge cases even if the specification author wasn't aware of the complications.

Just to acknowledge this point, I think we can all agree that providing tools to write specifications clearly, concisely, and precisely is important and useful.

I just happen to find that agreement very boring, and thus dove into the much-more-interesting aspect of the conversation, which was about this script settings object stack :)

# Boris Zbarsky (10 years ago)

On 2/3/14 1:19 PM, Allen Wirfs-Brock wrote:

It's hard to even talk about this because the above is filled with concepts and assumptions that don't currently occur anywhere in the ES specification.

Yes, indeed. Welcome to the world most spec authors operate in most of the time: they have to deal with concepts and assumptions that they don't know much, if anything, about.

For example, what is a location and what does bind have to do with anything?

bind is needed here only because the functions involved throw if their "this" value is not correctly branded. Since the APIs being called just call a function, with undefined this value, you have to pass in a bound version of the function to get anything other than an exception to happen.

In ES semantic, all bind does is is create a filtering mechanism that passes a fixed value to the target function.

Right, that's all I need it for here.

ES6 does provide the concept of Realms (approximately a global environment) and a immutable association between scripts/functions and Realms. Is it possible to frame the discussion in those terms?

Possibly, yes.

Do Realms have associated origins and base URIs? Those are the concepts that are relevant here. Some DOM functions need some ambient concept of origin and base URI to do what they do. In a single-global world there is no problem: you just use the origin and base URI of that global. But in a multiple-global world you have to define which global's origin and base URI is used. The situation is complicated by the fact that for the base URI case there are backwards compatibility constraints (which would clearly not apply to Realms per se, but do apply to iframes).

If Realms have those concepts associated with them?

Are you suggesting that all uses of ECMAScript promises in all hosting environments must have a dependency upon WebIDL callback semantics?

There are two relevant answers here.

Answering the question directly: yes, since the only interesting callback semantic that a WebIDL "callback Function = any(any... args)" has is that it maintains this script settings object stack that's consumed by DOM APIs; if your hosting environment doesn't have those APIs then it's black-box indistinguishable from direct invocation of the [[Call]] of the function object in question. So the dependency only affects behavior when you're exposing DOM APIs.

But the real answer is probably more that this dichotomy between "ES" and "WebIDL" needs to go, and we should define ES callback semantics that WebIDL can then rely on, which expose hooks that DOM can use to maintain the state it needs.

What I most don't understand about all of the above is what F.p.bind has to do with all of this?

It just makes the testcases actually do something instead of throwing.

# Boris Zbarsky (10 years ago)

On 2/3/14 1:21 PM, Allen Wirfs-Brock wrote:

Speaking from the perspective of a ES language designer WTF is a "script setting object stack" and why have I never heard about it?

Precisely my point about the world most spec writers find themselves in! ;)

More seriously, it's www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#script-settings-for-browsing-contexts

and www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#calling-scripts

with particular emphasis on www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#entry-settings-object

and www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#incumbent-settings-object.

You probably haven't heard of it because it was only recently formalized. Previously this behavior was defined in terms of JS syntactic constructs attempting to explain the behavior of APIs like the location setter and postMessage, but of course when the event loop directly invokes functions there is no JS syntactic construct on the stack. Hence the redefinition in terms of concepts that make sense in the event loop case.

You also probably haven't heard of it because it wasn't relevant to ES until an event loop was introduced to the language.

That said, if you're following public-script-coord you probably saw at least lists.w3.org/Archives/Public/public-script-coord/2014JanMar/0080.html... Unfortunately a lot of the other discussion on the matter happened directly in bugzilla bugs, and wasn't mirrored to public-script-coord, for which I apologize. :(

# Boris Zbarsky (10 years ago)

On 2/3/14 1:24 PM, Domenic Denicola wrote:

Just to acknowledge this point, I think we can all agree that providing tools to write specifications clearly, concisely, and precisely is important and useful.

I just happen to find that agreement very boring

Well, the exciting part is the details of how to go about doing that. ;)

Even agreement in principle here is a good start, by the way.

# Allen Wirfs-Brock (10 years ago)

On Feb 3, 2014, at 11:30 AM, Boris Zbarsky wrote:

On 2/3/14 1:19 PM, Allen Wirfs-Brock wrote:

...

For example, what is a location and what does bind have to do with anything?

bind is needed here only because the functions involved throw if their "this" value is not correctly branded. Since the APIs being called just call a function, with undefined this value, you have to pass in a bound version of the function to get anything other than an exception to happen.

Do me this seems style should be considered an anti-pattern as the then contract is for a function that does not require a this value. They could be more concisely and clearly restated as:

1): somePromise.then(() => window.postMessage(5, "*"));

2): somePromise.then(() => location.href = "relativeURI");

Am I misunderstanding something about these?

In ES semantic, all bind does is is create a filtering mechanism that passes a fixed value to the target function.

Right, that's all I need it for here.

ES6 does provide the concept of Realms (approximately a global environment) and a immutable association between scripts/functions and Realms. Is it possible to frame the discussion in those terms?

Possibly, yes.

Do Realms have associated origins and base URIs? Those are the concepts that are relevant here. Some DOM functions need some ambient concept of origin and base URI to do what they do. In a single-global world there is no problem: you just use the origin and base URI of that global. But in a multiple-global world you have to define which global's origin and base URI is used. The situation is complicated by the fact that for the base URI case there are backwards compatibility constraints (which would clearly not apply to Realms per se, but do apply to iframes).

If Realms have those concepts associated with them?

Realms don't directly expose those concepts (that would be pretty host platform dependent) but they do have a global object upon which properties can be predefined and they allow for association with a Loader that might be host specific and have such state.

see people.mozilla.org/~jorendorff/es6-draft.html#sec-code-realms

Are you suggesting that all uses of ECMAScript promises in all hosting environments must have a dependency upon WebIDL callback semantics?

There are two relevant answers here.

Answering the question directly: yes, since the only interesting callback semantic that a WebIDL "callback Function = any(any... args)" has is that it maintains this script settings object stack that's consumed by DOM APIs; if your hosting environment doesn't have those APIs then it's black-box indistinguishable from direct invocation of the [[Call]] of the function object in question. So the dependency only affects behavior when you're exposing DOM APIs.

But the real answer is probably more that this dichotomy between "ES" and "WebIDL" needs to go, and we should define ES callback semantics that WebIDL can then rely on, which expose hooks that DOM can use to maintain the state it needs.

If there needs to be hooks (in [[Call]] or anywhere else) we should be able to describe those hooks in a way that is meaningful and offer potential utility to other kinds of hosts. How would you abstractly describe you requirements?

What I most don't understand about all of the above is what F.p.bind has to do with all of this?

It just makes the testcases actually do something instead of throwing.

Unless I missed something, the use of bind just obscured the point you were really trying to make.

# Boris Zbarsky (10 years ago)

On 2/3/14 3:55 PM, Allen Wirfs-Brock wrote:

Do me this seems style should be considered an anti-pattern as the then contract is for a function that does not require a this value.

Sure. No one in their right mind would write it that way. Except insofar as they're trying to get particular security behavior...

They could be more concisely and clearly restated as:

1): somePromise.then(() => window.postMessage(5, "*"));

2): somePromise.then(() => location.href = "relativeURI");

Am I misunderstanding something about these?

These actually have somewhat different behavior from the bind case.

Specifically, the origin that the postMessage sees in the arrow function acse right now will be that of the arrow function passed to then() in this case (or whatever JS function it is that invokes postMessage if it's a JS function with actual JS code that invokes it). This is not necessarily the same as the origin of the code that called then()...

I'm not sure whether that's in fact the behavior we want for arrow functions... but certainly in general if I have some code in window A that calls then() and passes to it a function from window B that then calls postMessage on window C, we want the message to be coming from B, not from A. Or so I'm told.

Realms don't directly expose those concepts (that would be pretty host platform dependent) but they do have a global object upon which properties can be predefined and they allow for association with a Loader that might be host specific and have such state.

OK, great. ;)

If there needs to be hooks (in [[Call]] or anywhere else) we should be able to describe those hooks in a way that is meaningful and offer potential utility to other kinds of hosts. How would you abstractly describe you requirements?

Requirement #1: When some actual JS code someone typed into a <script>

tag calls postMessage on some Window object, the origin needs to be the origin of the JS code in question.

Requirement #2: When some actual JS code someone typed into a <script>

tag calls the location.href setter, the base URI needs to be the thing that's compatible with what UAs do (which in practice means "the base URI of the window where we last called from C++ into JavaScript)... Yes, this sucks, I'm sorry.

Requirement #3: We need to extend these definitions to all cases where these functions can be called, even if there is no actual script someone typed into a <script> tag that is calling them.

I'm not sure how to describe all this for an environment that doesn't have postMessage or a location setter, honestly. In particular, the location setter behavior is purely compat with insanity. :(

www.w3.org/Bugs/Public/show_bug.cgi?id=18242 has some of the relevant discussion, if that helps, for the postMessage case.

Unless I missed something, the use of bind just obscured the point you were really trying to make.

I needed a way to have the browser asynchronously invoke the method in question in a context where the phrase "script that invoked the method" (which is what postMessage used to be defined in terms of) is nonsensical. Unfortunately, in the arrow function case it's perfectly well-defined what the "script that invoked the method is", so the arrow function version doesn't actually exercise the edge case I wanted to exercise.

# Allen Wirfs-Brock (10 years ago)

On Feb 3, 2014, at 1:44 PM, Boris Zbarsky wrote:

On 2/3/14 3:55 PM, Allen Wirfs-Brock wrote:

Do me this seems style should be considered an anti-pattern as the then contract is for a function that does not require a this value.

Sure. No one in their right mind would write it that way. Except insofar as they're trying to get particular security behavior...

They could be more concisely and clearly restated as:

1): somePromise.then(() => window.postMessage(5, "*"));

2): somePromise.then(() => location.href = "relativeURI");

Am I misunderstanding something about these?

These actually have somewhat different behavior from the bind case.

Specifically, the origin that the postMessage sees in the arrow function acse right now will be that of the arrow function passed to then() in this case (or whatever JS function it is that invokes postMessage if it's a JS function with actual JS code that invokes it). This is not necessarily the same as the origin of the code that called then()...

I'm not sure that I see the difference. You can think of bind as if it was defined as:

 function bind(boundThis, ...boundArgs) {return (...perCallArgs)=>this.call(boundThis,...boundArgs,...perCallArgs)};

this isn't exactly how ES defines it but it is conceptually what is happening and what frameworks actually did (of course not uses => and ...) prior to ES5.

Are you assuming something about the "origin" of built-ins such as ES5 bind? In ES6 all functions, including built-ins are permanently associated with a Realm when they are created. In the case of an arrow functions, their realm is the same as the realm of the code that contains the arrow expression.

I'm not sure whether that's in fact the behavior we want for arrow functions... but certainly in general if I have some code in window A that calls then() and passes to it a function from window B that then calls postMessage on window C, we want the message to be coming from B, not from A. Or so I'm told.

Yes, that's how it would functions, but I don't think that bind really changes anything. The arrow function (or the call to bind) could have taken in the context of B before being passed to A. In that case, the call to postMessge will be coming form B.

Realms don't directly expose those concepts (that would be pretty host platform dependent) but they do have a global object upon which properties can be predefined and they allow for association with a Loader that might be host specific and have such state.

OK, great. ;)

If there needs to be hooks (in [[Call]] or anywhere else) we should be able to describe those hooks in a way that is meaningful and offer potential utility to other kinds of hosts. How would you abstractly describe you requirements?

Requirement #1: When some actual JS code someone typed into a <script> tag calls postMessage on some Window object, the origin needs to be the origin of the JS code in question.

Requirement #2: When some actual JS code someone typed into a <script> tag calls the location.href setter, the base URI needs to be the thing that's compatible with what UAs do (which in practice means "the base URI of the window where we last called from C++ into JavaScript)... Yes, this sucks, I'm sorry.

Requirement #3: We need to extend these definitions to all cases where these functions can be called, even if there is no actual script someone typed into a <script> tag that is calling them.

I'm not sure how to describe all this for an environment that doesn't have postMessage or a location setter, honestly. In particular, the location setter behavior is purely compat with insanity. :(

Yes, the above is so specific and operational, it is hard to generalize into more abstract concepts. But I'm sure it can be done.

www.w3.org/Bugs/Public/show_bug.cgi?id=18242 has some of the relevant discussion, if that helps, for the postMessage case.

Unless I missed something, the use of bind just obscured the point you were really trying to make.

I needed a way to have the browser asynchronously invoke the method in question in a context where the phrase "script that invoked the method" (which is what postMessage used to be defined in terms of) is nonsensical. Unfortunately, in the arrow function case it's perfectly well-defined what the "script that invoked the method is", so the arrow function version doesn't actually exercise the edge case I wanted to exercise.

In ES6 we are introducing asynchronous tasks (needed to support promise, but someday probably also events) and I'm trying to be careful to make sure it is well specified which Realm each async task initially represents. If the task is created (perhaps, indirectly) from JS code then it is the realm of the JS code that is the creator. If the task is created by an external, non-JS agent, then the initial realm of the task must be explicitly provided. In either case, most task immediate call a JS function so that's probably where realm (or origin??) crossing actually takes place.

# Boris Zbarsky (10 years ago)

On 2/3/14 6:45 PM, Allen Wirfs-Brock wrote:

I'm not sure that I see the difference.

That's fair. In SpiderMonkey's implementation there is a difference: bind is not a "scripted" function associated with actual user script, while an arrow function (and any other function actually defined in a script) is.

Are you assuming something about the "origin" of built-ins such as ES5 bind?

No. On the contrary, I'm assuming that foo.bind(thisval, args).call(undefined) and foo.call(thisval, args) and thisval.foo(args) should all behave identically in terms of things like origins and base URIs. As should setTimeout(foo.bind(thisval), 0, args), for that matter.

In ES6 all functions, including built-ins are permanently associated with a Realm when they are created.

Yes. That plus every global having an origin gives us the concept of origin of a function, which is well defined. And that's useful and used for things. But it doesn't match what postMessage needs to do, because if my code does:

otherWindow.postMessage(args)

then the origin of the message is my code, not otherWindow...

I'm not sure whether that's in fact the behavior we want for arrow functions... but certainly in general if I have some code in window A that calls then() and passes to it a function from window B that then calls postMessage on window C, we want the message to be coming from B, not from A. Or so I'm told.

Yes, that's how it would functions, but I don't think that bind really changes anything.

It simply makes it possible to invoke a "native" (that is, browser-provided) function directly from the event loop without any user code on the stack.

The arrow function (or the call to bind) could have taken in the context of B before being passed to A. In that case, the call to postMessge will be coming form B.

I'm honestly not sure what you're arguing here.

In ES6 we are introducing asynchronous tasks (needed to support promise, but someday probably also events) and I'm trying to be careful to make sure it is well specified which Realm each async task initially represents.

Ah. To support the things DOM needs you need to associate at least two possibly-distinct realms to each task, I believe. Those are the "incumbent" and "entry" globals (or "script settings objects", as one prefers).

In either case, most task immediate call a JS function so that's probably where realm (or origin??) crossing actually takes place.

That would chance the incumbent global. What it does on the entry global depends. Generally, calling a JS function doesn't change the entry global; WebIDL callbacks should be explicitly changing it before calling their callback function...

# Kevin Reid (10 years ago)

On Mon, Feb 3, 2014 at 4:16 PM, Boris Zbarsky <bzbarsky at mit.edu> wrote:

On 2/3/14 6:45 PM, Allen Wirfs-Brock wrote:

In ES6 all functions, including built-ins are permanently associated with a Realm when they are created.

Yes. That plus every global having an origin gives us the concept of origin of a function, which is well defined. And that's useful and used for things. But it doesn't match what postMessage needs to do, because if my code does:

otherWindow.postMessage(args)

then the origin of the message is my code, not otherWindow...

It is an extremely bad idea to have the consequences of a function call depend on properties of the caller rather than of the function and the arguments. (Hence the removal of .caller and .callee in strict mode.) Even if it proves necessary for legacy compatibility, no such behavior should be specified in new systems.

I think I heard that Firefox already handles this via a membrane ('otherWindow' is not actually the same as that window's 'window' object, but you can't tell because they're substituted as needed when passed across origins); can someone confirm this? This removes any dependency on the calling context — in particular, there is no need to have origin information in contexts/"stack frames" rather than objects (which naturally belong to a particular realm and hence origin).

# Boris Zbarsky (10 years ago)

On 2/3/14 7:46 PM, Kevin Reid wrote:

It is an extremely bad idea to have the consequences of a function call depend on properties of the caller rather than of the function and the arguments.

I agree, but the postMessage API is what it is....

Even if it proves necessary for legacy compatibility, no such behavior should be specified in new systems.

I believe the postMessage behavior is all about legacy compatibility. as is the Location href setter behavior. Sadly, it's not the only legacy thing in the platform that needs that behavior. :(

So really, for each function call there are three relevant globals: the global of the function itself and the entry and incumbent globals at the callsite.

I think I heard that Firefox already handles this via a membrane ('otherWindow' is not actually the same as that window's 'window' object, but you can't tell because they're substituted as needed when passed across origins); can someone confirm this?

Firefox has a membrane here, but the call actually pierces the membrane in this case, so when Firefox needs to find out who "the caller" was it actually just examines the callstack (modulo the cases when called with no script on the stack, etc).

Specifically, in Firefox if code in window A has a reference to window B, then it actually has a reference to a proxy. If it then does proxyForB.postMessage that returns a proxy for the actual windowB.postMessage method. When [[Call]] happens on this proxy, it checks that you're allowed to make the call at all, then invokes windowB.postMessage, passing to it proxies to the arguments it was called with.

But now the windowB.postMessage method would like to determine who called it... and that information is no longer available from itself, since it itself lives in window B.

-Boris

P.S. Boy do I wish people had mutated the subject when taking the discussion in this direction. Not really worth doing it now...

# Kevin Reid (10 years ago)

On Mon, Feb 3, 2014 at 4:56 PM, Boris Zbarsky <bzbarsky at mit.edu> wrote:

On 2/3/14 7:46 PM, Kevin Reid wrote:

It is an extremely bad idea to have the consequences of a function call depend on properties of the caller rather than of the function and the arguments.

I agree, but the postMessage API is what it is....

[...]

I think I heard that Firefox already handles this via a membrane

('otherWindow' is not actually the same as that window's 'window' object, but you can't tell because they're substituted as needed when passed across origins); can someone confirm this?

Firefox has a membrane here, but the call actually pierces the membrane in this case, so when Firefox needs to find out who "the caller" was it actually just examines the callstack (modulo the cases when called with no script on the stack, etc).

Specifically, in Firefox if code in window A has a reference to window B, then it actually has a reference to a proxy. If it then does proxyForB.postMessage that returns a proxy for the actual windowB.postMessage method. When [[Call]] happens on this proxy, it checks that you're allowed to make the call at all, then invokes windowB.postMessage, passing to it proxies to the arguments it was called with.

But now the windowB.postMessage method would like to determine who called it... and that information is no longer available from itself, since it itself lives in window B.

Could not this be done while matching the above principle as follows?

proxyForB, or more precisely the proxy for the function object (windowB.postMessage), does not actually invoke windowB.postMessage itself but a corresponding "post message from origin A" function.

Or if the distinction should be made using 'this'-values rather than function values, make it by the function proxy recognizing this=proxyForB and not unwrapping it but performing the cross-origin post.

Either should be observably equivalent to the legacy behavior while not introducing execution-context dependence.

# Boris Zbarsky (10 years ago)

On 2/4/14 12:08 PM, Kevin Reid wrote:

Could not this be done while matching the above principle as follows?

proxyForB, or more precisely the proxy for the function object (windowB.postMessage), does not actually invoke windowB.postMessage itself but a corresponding "post message from origin A" function.

That's actually pretty complicated. Now you have two not-object-identical representations of windowB.postMessage in the scope of windowB, no? The current membrane in SpiderMonkey has a single per-global representation of each object, for sanity's sake. As a simple example, consider this code in window A:

proxyForB.setTimeout(proxyForB.postMessage, 0, 5, "*");

What function object should the setTimeout implementation see? What should happen when the timeout fires?

Or if the distinction should be made using 'this'-values rather than function values, make it by the function proxy recognizing this=proxyForB

Ensuring that in the setTimeout case above is not trivial, I expect.

# Kevin Reid (10 years ago)

On Tue, Feb 4, 2014 at 9:21 AM, Boris Zbarsky <bzbarsky at mit.edu> wrote:

On 2/4/14 12:08 PM, Kevin Reid wrote:

Could not this be done while matching the above principle as follows?

proxyForB, or more precisely the proxy for the function object (windowB.postMessage), does not actually invoke windowB.postMessage itself but a corresponding "post message from origin A" function.

That's actually pretty complicated. Now you have two not-object-identical representations of windowB.postMessage in the scope of windowB, no? The current membrane in SpiderMonkey has a single per-global representation of each object, for sanity's sake. As a simple example, consider this code in window A:

proxyForB.setTimeout(proxyForB.postMessage, 0, 5, "*");

What function object should the setTimeout implementation see? What should happen when the timeout fires?

You point out that this is indeed more complicated to get “right” than I had realized. I think it could still be done but things like setTimeout would have to be proxied in the same way.

This arguably shows that the legacy policy is even more of a bad idea, though, because it breaks a property kind of like TCP (I don't know a name for it offhand, but it's related to the confused deputy). For example, suppose that in windowB we previously evaluated function later(f, ...as) { setTimeout(function() { f(...as); }, 0); } and then in windowA we do proxyForB.later(proxyForB.postMessage, 5, "*"); then if I understand your description correctly, this would perform a postMesage from B's origin. But why should it, just because there's an intervening user-defined HOF?

In particular, what if "later" instead is something that is built-in, not defined by the DOM, but has the effect of calling a function passed in? Whether or not one were to implement the mechanics in the way I propose, there needs to be a well-defined and preferably sensible decision about what happens upon something like:

proxyForB.Array.prototype.forEach.call([5], proxyForB.postMessage);

# Boris Zbarsky (10 years ago)

On 2/5/14 1:02 PM, Kevin Reid wrote:

suppose that in windowB we previously evaluated function later(f, ...as) { setTimeout(function() { f(...as); }, 0); } and then in windowA we do proxyForB.later(proxyForB.postMessage, 5, "*"); then if I understand your description correctly, this would perform a postMesage from B's origin.

Correct. In fact, you don't even need the setTimeout. Just this in windowB:

function doNow(f, ...as) { f(...as); }

and then windowA doing:

proxyForB.doNow(proxyForB.postMessage, 5, "*")

would have the same effect: the message would seem to come from windowB...

In particular, what if "later" instead is something that is built-in, not defined by the DOM, but has the effect of calling a function passed in?

The way HTML5 attempts to define things right now is that if your "later" or my "doNow" is built-in as opposed to explicitly written in script then the message will come from window A.

This is unfortunately gross, I agree...

there needs to be a well-defined and preferably sensible decision about what happens upon something like:

proxyForB.Array.prototype.forEach.call([5], proxyForB.postMessage);

Right. That's what led to the concept of the script settings stack...

The behavior here is in fact well-defined-ish, but it relies on this distinction between scripted and built-in functions. I believe the relevant text at www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#calling-scripts is this:

When a JavaScript SourceElements production is to be evaluated, the settings object of the script corresponding to that SourceElements must be pushed onto the stack of script settings objects before the evaluation begins, and popped when the evaluation ends (regardless of whether it's an abrupt completion or not).

Again, this doesn't so much make me happy; it's just the best people have come up with so far.

In this case, since there is no SourceElements involved in Array.prototype.forEach (lots of handwaving involved in that claim!) there would be no change to the script settings stack when forEach is invoked, so the message would be posted in the same way as if the forEach caller had directly invoked postMessage.

Yes, this means that if you replace forEach with a scripted version you get different behavior. :( I'd love a consistent proposal that avoids that problem that browsers that aren't currently using any sort of membranes for their DOM are actually willing to implement.