July TC39 meeting notes, day 1

# Brendan Eich (13 years ago)

== Handler access to proxies ==

Proxy handler traps need to receive the proxy as a parameter: first, or last?

Last allows trap implementors to leave |proxy| off. It's also a compatible extension to the proposal and its prototype implementations. Putting |proxy| last may also steer implementors away from touching proxy, reducing the bugs where you infinitely diverge.

First is more normal-order (proxy, name) and some find it more aesthetically pleasing.

Another alternative: the proxy could be "passed" via a data property on the handler. But the only use for the proxy reference is as a key in a weakmap, and if the handler references it, the handler could just as well be the key -- or the handler could simply hold the value associated by the weakmap.

Conclusion: no rationale for adding a |proxy| parameter to all traps.

== Proxy drop receiver ==

Sean Eagan pointed out in

esdiscuss/2011-April/013916

that the receiver parameter of Proxy handlers' get and set traps is useless, due to how these derived traps layer on fundamnetal traps.

Conclusion: remove |receiver| from these traps.

== Luke's report on Microsoft-internal feedback ==

Runtime extensions usable soonest, modules significant but full impact unclear, concern about versioning.

Positive: private names, classes, let, proxies, typed arrays and binary data

Concern: enhanced object literals, destructuring, quasis

Indifferent: Maps/sets, iterators, tail calls, (parameter) default values, comprehensions, spread/rest

Possible confusion about Map and Set requiring object typed keys -- they allow all key types. What may be wanted: "computed keys", i.e., objects that are equated not by identity, rather via equals/hashcode-style methods.

ES5 not viewed as super-compelling. A few "use strict"; adoption cases, unclear what it helped or hurt.

Happy to be able to feature-detect APIs, cannot feature-detect syntax (false: eval), but can't use ES5 until it is supported by most browsers in the field. Counterpoint: in that case, when it is supported enough both APIs and syntax can be used.

Subsets while prototyping the next edition, coordination among implementors, to get the good stuff out.

Concern about brevity of <script> vs. <script type="application/ecmascript;version=6">. Counterpoint: |use version 6;| in-band in the script content. Can use both, or just the latter. Agreement on usability including conciseness being important to work on for ES.next.

An offline compiler from ES.next to ES3+R would help.

Some better way to conditionally load scripts depending on UA language version support...

API and feature wishlist:

  • Crypto APIs (no details re: getRandomValues vs. AES, etc.)
  • Stack traces, debugging aids
  • Data-binding
  • Binary source format for code size,load time perf wins
  • Shothand for typeof someGlobal == "undefined"
  • Compiler hooks for code transformation
  • Int64, Bignum
  • More focus on static analysis (part of each group's toolchain)

Destructuring had love / hate reaction (object pattern without shorthand, less worthy objections).

Modules:

  • Early errors make code brittle as modules evolve if you use |import foo.*|.
  • Async loading implications
    • Soft failure on timeouts?
    • Scenarios where developer doesn't know whether to use static or dynamic loading?
    • Want dependent script loading to be delayed till "truly dynamically needed"
  • Browser-level compoment dependency mechanism -- can support CSS and HTML too?

Classes

  • Positive readability and debugability reactions
  • Desugaring important for interop with existing prototypal patterns
  • "class" terminology and the keyword itself raises some hackles

Object literal

  • The ~!# and := cussing is not popular
  • People don't want to make properties non-writable or non-configurable
  • Prototype-of well-received as idea, <| syntax looks awkward
  • Seems like a lot of overlap with classes, want only one

Quasi-literals

  • "I don't like Perl" reaction
  • String.format instead
  • "Love it, I would use it all the time" reaction
  • Name is confusing

Iterators and generators

  • "Yes, that would be cool"
  • Not something that would be heavily used unless provided by common libraries Audience was mix of front end developers and library/middleware authors

Proxies

  • Awesome... wait, do we really want unstratified ("observe")? Yeah...
  • Don't immediately see applications, but could see abstracting away DOM diffs
  • Could simplify sandbox, but would prefer direct browser support, at MS Web Sandbox or Caja granularity

== Luke's work on Math and String extensions ==

Math functions

  • Java signum (-1, 0, or 1 return value) vs. C signbit (non-zero if negative including -0, zero otherwise)
    • What is the purpose? Translating numeric algorithms from Java, or C?
    • NaNs should be canonical so you can't distinguish negative from positive NaNs
    • Safe way to make a non-NaN float from uint32 or even uint64 data
  • Math.{log1p,expm1,log2,log10,hypot}, etc.
  • Math.{cosh,sinh,tanh,acosh,asinh,atanh}
  • gamma, erf? Waldemar argues for including
  • Open question about OS libm interoperation
  • deg2rad and rad2deg? Math.M_2_PI instead? neither? not sure

String methods

  • reverse (like s.split('').reverse().join('')) along with the ones adopted in May
    • Waldemar: reorders uint16 units that form combining sequences, surrogates
    • General agreement that this is novel as a hazard and reverse is too marginal a thing to add with this hazard
  • want UTF-8 transcoding with byte arrays, topic for binary data

== Editor's update ==

Allen gave an update on the editorial process and his use of bugs.ecmascript.org to track ES5.1 errata and ES6 work items.

# David Bruant (13 years ago)

Le 28/07/2011 06:21, Brendan Eich a écrit :

== Handler access to proxies ==

Proxy handler traps need to receive the proxy as a parameter: first, or last?

Last allows trap implementors to leave |proxy| off. It's also a compatible extension to the proposal and its prototype implementations. Putting |proxy| last may also steer implementors away from touching proxy, reducing the bugs where you infinitely diverge.

First is more normal-order (proxy, name) and some find it more aesthetically pleasing.

Another alternative: the proxy could be "passed" via a data property on the handler.

I think we discussed already the idea of "proxy" being passed as a data property to the handler and came to the conclusion that it may not be a good idea, because it breaks the stratification. If two proxies use the same handler as in [2], then, there is an ambiguity on what the value of this property should be.

But the only use for the proxy reference is as a key in a weakmap,and if the handler references it, the handler could just as well be the key -- or the handler could simply hold the value associated by the weakmap.

There is at least one cases other than the weakmap-key one which is to obtain Object.getPrototypeOf(proxy) within a trap. With a handler reference instead of a proxy reference, it is not impossible, but requires some additional work as in [1] (I provide a reference to the proxy to use it later to get the prototype). In my opinion at least all traps emulating an inheritance-related trap should have a reference to the proxy.

For the same reason than previously, the prototype could not be a data property of the handler as several proxies could use the same handler with different prototypes.

Conclusion: no rationale for adding a |proxy| parameter to all traps.

I felt that there really was a need for a proxy parameter in all traps, but can't find a use case (besides the weakmap-key one) for why at the moment.

== Proxy drop receiver ==

Sean Eagan pointed out in

esdiscuss/2011-April/013916

that the receiver parameter of Proxy handlers' get and set traps is useless, due to how these derived traps layer on fundamnetal traps.

Conclusion: remove |receiver| from these traps.

I'm glad :-)

Also, Microsoft enthousiasm toward proxies makes me happy!

Thanks for the meeting notes!

David

[1] DavidBruant/HarmonyProxyLab/blob/master/NonExtensibleProxies/NonExtensibleProxies.js#L14 [2] DavidBruant/DeclO/blob/master/DeclO.js

# David Bruant (13 years ago)

Le 28/07/2011 06:21, Brendan Eich a écrit :

== Handler access to proxies ==

Another alternative: the proxy could be "passed" via a data property on the handler. But the only use for the proxy reference is as a key in a weakmap, and if the handler references it, the handler could just as well be the key -- or the handler could simply hold the value associated by the weakmap.

Also, if the FixedProperty or NonExtensibleProxy proposals (the later has only been discussed on es-discuss and has nothing on the wiki) are accepted, then proxies will carry some information that the handler may want to introspect (Object.isExtensible, Object.getOwnPropertyDescriptor().configurable). These informations are meant to be kept out of the proxy author control, so they cannot be put as a handler property either. Having the proxy as an argument of all traps would be a best way to access these informations in my opinion. Of course, this is dependent on the FixedProperties (NonConfigurableProperties) and NonExtensibleProxy

# Juan Ignacio Dopazo (13 years ago)

On Thu, Jul 28, 2011 at 1:21 AM, Brendan Eich <brendan at mozilla.com> wrote:

Math functions

...

Can I add a request? Math.randomInt(n) returning [0, n) would be a nice addition. Additionally but not necessarily, it could accept an optional extra parameter for Math.randomInt(from, to). Most of the time, when writing games or simple applications a random integer is a lot more useful than a float.

Juan

# Andreas Rossberg (13 years ago)

On 28 July 2011 10:35, David Bruant <david.bruant at labri.fr> wrote:

Le 28/07/2011 06:21, Brendan Eich a écrit :

== Handler access to proxies ==

Proxy handler traps need to receive the proxy as a parameter: first, or last?

Last allows trap implementors to leave |proxy| off. It's also a compatible extension to the proposal and its prototype implementations. Putting |proxy| last may also steer implementors away from touching proxy, reducing the bugs where you infinitely diverge.

First is more normal-order (proxy, name) and some find it more aesthetically pleasing.

Another alternative: the proxy could be "passed" via a data property on the handler.

I think we discussed already the idea of "proxy" being passed as a data property to the handler and came to the conclusion that it may not be a good idea, because it breaks the stratification. If two proxies use the same handler as in [2], then, there is an ambiguity on what the value of this property should be.

The solution we discussed is to simply use prototypes. That is, share handler methods by putting them on a (single) prototype object, and have per-proxy instances that carry the individual proxy references (or other per-proxy data, for that matter).

# David Bruant (13 years ago)

Le 28/07/2011 19:52, Andreas Rossberg a écrit :

On 28 July 2011 10:35, David Bruant <david.bruant at labri.fr> wrote:

Le 28/07/2011 06:21, Brendan Eich a écrit :

== Handler access to proxies ==

Proxy handler traps need to receive the proxy as a parameter: first, or last?

Last allows trap implementors to leave |proxy| off. It's also a compatible extension to the proposal and its prototype implementations. Putting |proxy| last may also steer implementors away from touching proxy, reducing the bugs where you infinitely diverge.

First is more normal-order (proxy, name) and some find it more aesthetically pleasing.

Another alternative: the proxy could be "passed" via a data property on the handler. I think we discussed already the idea of "proxy" being passed as a data property to the handler and came to the conclusion that it may not be a good idea, because it breaks the stratification. If two proxies use the same handler as in [2], then, there is an ambiguity on what the value of this property should be. The solution we discussed is to simply use prototypes. That is, share handler methods by putting them on a (single) prototype object, and have per-proxy instances that carry the individual proxy references (or other per-proxy data, for that matter).

This is a pattern that I have seen used by Tom a lot and that I really like too, but you can't force a user to do that. So I assume, you would systematically add a base object and use the argument handler as its prototype?


// h is a handler object var p1 = Proxy.create(h); var p2 = Proxy.create(h);

When a user does this, what does he want? To use the exact same handler (same object identity)? Or to use the same logic but different internal properties? The solution you discussed seems to assume the latter, but who knows? And how do I implement the former if the proxy spec imposes that the object I pass internally becomes another object?

Now that I think about it, it's a bit weird that the proxy API allows to create several proxies with the same handler (same object identity). Maybe the API could be reworked in order to prevent it? Maybe Proxy.create should return the same proxy object if provided the same handler (p1 === p2, here)?

# Brendan Eich (13 years ago)

On Jul 28, 2011, at 1:35 AM, David Bruant wrote:

Le 28/07/2011 06:21, Brendan Eich a écrit :

== Handler access to proxies ==

Proxy handler traps need to receive the proxy as a parameter: first, or last?

Last allows trap implementors to leave |proxy| off. It's also a compatible extension to the proposal and its prototype implementations. Putting |proxy| last may also steer implementors away from touching proxy, reducing the bugs where you infinitely diverge.

First is more normal-order (proxy, name) and some find it more aesthetically pleasing.

Another alternative: the proxy could be "passed" via a data property on the handler. I think we discussed already the idea of "proxy" being passed as a data property to the handler and came to the conclusion that it may not be a good idea, because it breaks the stratification. If two proxies use the same handler as in [2], then, there is an ambiguity on what the value of this property should be.

The argument then (at the meeting, sorry for not recording it) is that you can always use prototypal delegation to share the shared parts of the handlers, and each delegating handler has its own proxy back-reference.

Also, Microsoft enthousiasm toward proxies makes me happy!

:-)

Thanks for the meeting notes!

Np.

# Andreas Rossberg (13 years ago)

On 28 July 2011 20:34, David Bruant <david.bruant at labri.fr> wrote:

Le 28/07/2011 19:52, Andreas Rossberg a écrit :

On 28 July 2011 10:35, David Bruant <david.bruant at labri.fr> wrote:

Le 28/07/2011 06:21, Brendan Eich a écrit :

== Handler access to proxies ==

Proxy handler traps need to receive the proxy as a parameter: first, or last?

Last allows trap implementors to leave |proxy| off. It's also a compatible extension to the proposal and its prototype implementations. Putting |proxy| last may also steer implementors away from touching proxy, reducing the bugs where you infinitely diverge.

First is more normal-order (proxy, name) and some find it more aesthetically pleasing.

Another alternative: the proxy could be "passed" via a data property on the handler. I think we discussed already the idea of "proxy" being passed as a data property to the handler and came to the conclusion that it may not be a good idea, because it breaks the stratification. If two proxies use the same handler as in [2], then, there is an ambiguity on what the value of this property should be. The solution we discussed is to simply use prototypes. That is, share handler methods by putting them on a (single) prototype object, and have per-proxy instances that carry the individual proxy references (or other per-proxy data, for that matter). This is a pattern that I have seen used by Tom a lot and that I really like too, but you can't force a user to do that. So I assume, you would systematically add a base object and use the argument handler as its prototype?


// h is a handler object var p1 = Proxy.create(h); var p2 = Proxy.create(h);

When a user does this, what does he want? To use the exact same handler (same object identity)? Or to use the same logic but different internal properties? The solution you discussed seems to assume the latter, but who knows? And how do I implement the former if the proxy spec imposes that the object I pass internally becomes another object?

I'm not sure I understand what you are asking. The solution I mentioned is purely user-side. There is no magic assumed in the proxy semantics. If you pass the same handler twice, it will be the same handler. If you need proxy-specific state, pass different handlers. If you still want some form of code sharing, use prototypal delegation.

Now that I think about it, it's a bit weird that the proxy API allows to create several proxies with the same handler (same object identity). Maybe the API could be reworked in order to prevent it? Maybe Proxy.create should return the same proxy object if provided the same handler (p1 === p2, here)?

I agree that there probably aren't too many useful examples for using the same handler. However, I also don't see a good reason for disallowing it, nor to require Proxy.create to memoise all handlers.

# Tom Schuster (13 years ago)

I am wondering if you discussed the typeof null proposal?

# Brendan Eich (13 years ago)

On Jul 29, 2011, at 12:18 PM, Tom Schuster wrote:

I am wondering if you discussed the typeof null proposal?

No, that was accepted back in January, IIRC. We have to see how big a migration burden it is, still, but that can't be simulated. We need implementations and user testing.

# Kevin Reid (13 years ago)

On Fri, Jul 29, 2011 at 15:20, Mark S. Miller <erights at google.com> wrote:

---------- Forwarded message ---------- From: Brendan Eich <brendan at mozilla.com> Date: Wed, Jul 27, 2011 at 9:21 PM

== Handler access to proxies == [...] Conclusion: no rationale for adding a |proxy| parameter to all traps.

== Proxy drop receiver ==

Sean Eagan pointed out in

esdiscuss/2011-April/013916

that the receiver parameter of Proxy handlers' get and set traps is useless, due to how these derived traps layer on fundamnetal traps.

Conclusion: remove |receiver| from these traps.

Hi; Mark Miller asked me to post this. I am a developer working on Google's Caja, writing ES5 code which uses proxies for our DOM virtualization.

Suppose a proxy handler wishes to implement the 'set' or 'get' trap in a way similar but not identical to the derived trap — similar in that it obtains a property descriptor (from the getOwnPropertyDescriptor trap or otherwise) and proceeds according to the contents of the descriptor.

If the property being accessed is an accessor property, then the descriptor's 'get' or 'set' function should be invoked, with 'this' being the proxy. This behavior cannot be implemented purely within the handler object (i.e. without the explicit cooperation of the code calling Proxy.create) unless the proxy is passed to the handler.

I consider it a useful principle that it should be possible for an object with a given method (the get/set trap of the handler, here) to implement the same behavior as will be performed by the caller (Proxy) if that method did not exist (the derived trap); therefore, I recommend that the 'get' and 'set' traps should have a parameter which is the proxy.

I have no position on whether the proxy parameter should be first or last, or whether other traps should have one.

# Brendan Eich (13 years ago)

On Jul 29, 2011, at 5:42 PM, Kevin Reid wrote:

On Fri, Jul 29, 2011 at 15:20, Mark S. Miller <erights at google.com> wrote:

---------- Forwarded message ---------- From: Brendan Eich <brendan at mozilla.com> Date: Wed, Jul 27, 2011 at 9:21 PM

== Handler access to proxies == [...] Conclusion: no rationale for adding a |proxy| parameter to all traps.

== Proxy drop receiver ==

Sean Eagan pointed out in

esdiscuss/2011-April/013916

that the receiver parameter of Proxy handlers' get and set traps is useless, due to how these derived traps layer on fundamnetal traps.

Conclusion: remove |receiver| from these traps.

Hi; Mark Miller asked me to post this. I am a developer working on Google's Caja, writing ES5 code which uses proxies for our DOM virtualization.

Thanks, you caught a blatant inconsistency in our reasoning. We used Sean's message (linked above) as a reason to remove receiver, that Sean wrote that message assuming both proxy and receiver would be parameters to get and set traps. Sean demonstrated that the two parameters would always have the same value, ergo only one was needed, and since at that time, proxy was considered important to keep (and add to all traps), receiver fell under the ax.

But as noted higher above, at this week's meeting, we rejected adding proxy to all traps, including get and set. Therefore we must keep the receiver parameter of those two traps, for the reasons you give. Thanks again!

# Brendan Eich (13 years ago)

On Jul 29, 2011, at 6:13 PM, Brendan Eich wrote:

Thanks, you caught a blatant inconsistency in our reasoning. We used Sean's message (linked above) as a reason to remove receiver, that

("but" at the end of the above line instead of "that", of course.)

Sean wrote that message assuming both proxy and receiver would be parameters to get and set traps. Sean demonstrated that the two parameters would always have the same value, ergo only one was needed, and since at that time, proxy was considered important to keep (and add to all traps), receiver fell under the ax.

But as noted higher above, at this week's meeting, we rejected adding proxy to all traps, including get and set. Therefore we must keep the receiver parameter of those two traps, for the reasons you give. Thanks again!

The good news: the proxy traps as proposed are unchanged. Hats off to Tom and Mark for nailing that API and minimizing its parameterization!

Proxies have two prototype implementations now: Firefox since 4, and now V8 (code.google.com/p/v8/source/detail?r=8733) -- not sure when that shows up under a flag in Chrome canaries, someone from Google will know.

# Kevin Reid (13 years ago)

On Fri, Jul 29, 2011 at 19:57, Brendan Eich <brendan at mozilla.com> wrote:

The good news: the proxy traps as proposed are unchanged. Hats off to Tom and Mark for nailing that API and minimizing its parameterization! Proxies have two prototype implementations now: Firefox since 4, and now V8 (code.google.com/p/v8/source/detail?r=8733) -- not sure when that shows up under a flag in Chrome canaries, someone from Google will know.

Just to clarify: “as proposed” = the current version of harmony:proxies? That is,

will the proxy parameter remain first, rather than last?

# Mark Miller (13 years ago)

On Fri, Jul 29, 2011 at 10:31 PM, Kevin Reid <kpreid at google.com> wrote:

On Fri, Jul 29, 2011 at 19:57, Brendan Eich <brendan at mozilla.com> wrote:

The good news: the proxy traps as proposed are unchanged. Hats off to Tom and Mark for nailing that API and minimizing its parameterization! Proxies have two prototype implementations now: Firefox since 4, and now V8 (code.google.com/p/v8/source/detail?r=8733) -- not sure when that shows up under a flag in Chrome canaries, someone from Google will know.

Just to clarify: “as proposed” = the current version of harmony:proxies? That is, will the proxy parameter remain first, rather than last?

We can certainly continue discussing this. But let's postpone trying to resolve any of this until Tom gets back from vacation. I still suspect there was some crucial point Tom had explained to me that I forgot.

# Brendan Eich (13 years ago)

On Jul 29, 2011, at 10:31 PM, Kevin Reid wrote:

On Fri, Jul 29, 2011 at 19:57, Brendan Eich <brendan at mozilla.com> wrote:

The good news: the proxy traps as proposed are unchanged. Hats off to Tom and Mark for nailing that API and minimizing its parameterization! Proxies have two prototype implementations now: Firefox since 4, and now V8 (code.google.com/p/v8/source/detail?r=8733) -- not sure when that shows up under a flag in Chrome canaries, someone from Google will know.

Just to clarify: “as proposed” = the current version of harmony:proxies? That is, will the proxy parameter remain first, rather than last?

It's the receiver parameter for get and set in the spec, but yes: first.

Again, we went through a path that added proxy at the end:

get: function (receiver, name, proxy) {...}

and then Sean pointed out how receiver and proxy can only ever denote the same object.

If the trapping proxy is directly referenced, there is no prototype object that might differ and the proxy's get trap is called with receiver = proxy.

If the trapping proxy is on the prototype chain of some native object O, then because of how ES5 works, the proxy's get trap is not invoked. As Sean wrote:

ES5 section 8.12.3 step [1 in ES5.1] calls [[GetProperty]] on the receiver, causing a [[GetProperty]] (not [[Get]]) call to propagate up the prototype chain, meaning the proxy's "getPropertyDescriptor" (not "get") trap is called.

(esdiscuss/2011-April/013916)

So the get trap is not invoked on the proto-proxy, rather the getPropertyDescriptor trap is invoked.

We can rename receiver to proxy and clarify this with a note, for sure.

# Brendan Eich (13 years ago)

On Jul 29, 2011, at 10:43 PM, Mark Miller wrote:

We can certainly continue discussing this. But let's postpone trying to resolve any of this until Tom gets back from vacation. I still suspect there was some crucial point Tom had explained to me that I forgot.

Nothing is resolved by discussing it, but the TC39 meeting's resolution that I recorded in my notes was clearly invalid.

Great to have Tom weigh in when he can.

# Andreas Rossberg (13 years ago)

On 30 July 2011 04:57, Brendan Eich <brendan at mozilla.com> wrote:

Proxies have two prototype implementations now: Firefox since 4, and now V8 (code.google.com/p/v8/source/detail?r=8733) -- not sure when that shows up under a flag in Chrome canaries, someone from Google will know.

If you invoke a current V8 shell it is available by passing --harmony-proxies. I'm roughly tracking progress here:

code.google.com/p/v8/issues/detail?id=1543

Note however that it is still work in progress, and certainly not anywhere near stable yet. That is, expect serious bugs.

At some point we will start threading harmony-related flags through Chrome, but we haven't decided on the when and how yet.

# Sean Eagan (13 years ago)

A 'receiver' argument is not needed because it would never be different than the proxy, and the proxy can either be passed as an argument or stored either as an own property of the handler, or as a value keyed by the handler in a weak map, which there seems to have been TC39 concensus on.

Regarding whether or not a proxy argument should be added, there do seem to be advantages to passing the proxy as an argument, but I don't feel particularly strong about it. I do however feel strongly that a 'proxy' argument should not be added selectively to just the 'get' and 'set' trap, or any other limited set of traps. There are surely use cases for accessing the proxy within all traps, so for consistency's sake it seems the method for accessing the proxy should be the same across all traps.

# Brendan Eich (13 years ago)

On Jul 31, 2011, at 1:04 PM, Sean Eagan wrote:

A 'receiver' argument is not needed because it would never be different than the proxy, and the proxy can either be passed as an argument or stored either as an own property of the handler, or as a value keyed by the handler in a weak map, which there seems to have been TC39 concensus on.

Ok, right -- even without the extra proxy parameter in addition to receiver, dropping receiver makes sense. Sorry to go in a circle on this.

It's a trap API change, and I agree with Mark that we need Tom to bless it.

Regarding whether or not a proxy argument should be added, there do seem to be advantages to passing the proxy as an argument, but I don't feel particularly strong about it.

There is a downside, Mark noted it in the meeting: touching the proxy is highly likely to diverge in runaway recursion.

I do however feel strongly that a 'proxy' argument should not be added selectively to just the 'get' and 'set' trap, or any other limited set of traps. There are surely use cases for accessing the proxy within all traps, so for consistency's sake it seems the method for accessing the proxy should be the same across all traps.

Agreed.

# Brendan Eich (13 years ago)

A week late, I missed some of the morning due to a conflict. Thanks to Alex Russell for whiteboard photos. Others in attendance, please fill in and correct as needed. Thanks.

== Overview of initial working draft for 6th edition and discuss work flow for developing 6th edition draft ==

Allen presented the draft 6th edition and how best to develop it:

harmony:specification_drafts

The phrase "extended code" as a way of specifying semantics for Harmony above and beyond "strict mode code" received some discussion (I missed most of it). In the end no one had a better term than "extended".

Allen also presented the use of cover grammars ("Supplemental Syntax") to specify destructuring assignment and possibly other syntactic extensions. The problem here is that an LR(1) grammar cannot distinguish an object or array literal from a destructuring pattern of the same form, until parsing reaches the '=' after the pattern. GLR or ordered choice top-down parsing techniques can cope, but LR(1) and therefore LL(1) cannot -- such an LR(1) grammar is ambiguous.

Note that ES1-5 cope with the existing ambiguity where x.y.z and x.y.z = w both start with a member expression by pushing the ambiguity off to the semantics, creating a spec-internal Reference type, which remembers the base object (what x.y evaluated to) and property name ('z'), and only getting the value (GetValue) if the x.y.z expression is an rvalue, otherwise using the Reference type lvalue to assign to the named property (PutValue).

Computing a "Reference tree" or "minimal AST" for whole and arbitrarily large literal patterns, to defer evaluation till an assignment operator is parsed and we know the pattern is a destructuring lvalue rather than an object/array literal rvalue (after which GetValue or PutValue would process the tree according to its rvalue or lvalue nature) is not feasible.

This is due to the generality of the PropertyAssignment and ElementList productions' AssignmentExpressions, which may embed function expressions and thus most of the grammar. We do not want to add an explicit and nearly-complete parse tree or AST to the spec.

The committee seemed to agree that the cover grammar approach seems like the best technique.

== Review/resolve open issues and change requests for 6 edition ==

bugs.ecmascript.org/buglist.cgi?order=Importance&list_id=384&field0-0-0=flagtypes.name&resolution=---&query_format=advanced&type0-0-0=substring&value0-0-0=TC39&product=Draft for 6th Edition

(Bug rows from the above query follow, starting with bug numbers.)

145 nor Normal All allen at wirfs-brock.com CONF --- eliminate uint32 length restriction the the length of array objects. 146 nor Normal All allen at wirfs-brock.com CONF --- Array generic array methods should not ToUint32 covert the length of non-generic arrays

We deferred these, agreeing that they seem worth trying to make as relaxations of existing index/length semantics for arrays, to align with String and avoid bogus uint32-domain work that cannot handle the "overflow" case of length == 2^32. They will change edge-case behavior. The changes may break only testsuites, but you never know.

178 nor Normal All allen at wirfs-brock.com CONF --- Must settle scoping details for block-scoped bindings

Much discussion here. The issue is whether let and const bindings hoist to block top, or start a new implicit scope (the let* or, let's call it, C++ rule). The prior work was nicely diagrammed by Waldemar in:

esdiscuss/2008-October/007807

Quoting from Waldemar's message (note the future-proofing for guards):

--- begin quote ---

There are four ways to do this: A1. Lexical dead zone. References textually prior to a definition in the same block are an error. A2. Lexical window. References textually prior to a definition in the same block go to outer scope. B1. Temporal dead zone. References temporally prior to a definition in the same block are an error. B2. Temporal window. References temporally prior to a definition in the same block go to outer scope.

Let's take a look at an example:

let x = "outer"; function g() {return "outer"}

{ g(); function f() { ... x ... g ... g() ... } f(); var t = some_runtime_type; const x:t = "inner"; function g() { ... x ... } g(); f(); }

B2 is bad because then the x inside g would sometimes refer to "outer" and sometimes to "inner".

A1 and A2 introduce extra complexity but doesn't solve the problem. You'd need to come up with a value for x to use in the very first call to g(). Furthermore, for A2 whether the window occurred or not would also depend on whether something was a function or not; users would be surprised that x shows through the window inside f but g doesn't.

That leaves B1, which matches the semantic model (we need to avoid referencing variables before we know their types and before we know the values of constants).

--- end quote ---

In the September 2010 meeting, however, we took a wrong turn (my fault for suggesting it, but in my defense, just about everyone did prefer it -- we all dislike hoisting!) away from hoisted let and const bindings, seemingly achieving consensus for the C++ rule.

Allen, it turned out, did not agree, and he was right. Mixing non-hoisting (the C++ rule) with hoisting (function in block must hoist, for mutual recursion "letrec" use-cases and to match how function declarations at body/program level hoist) does not work. In the example above, g's use of x either refers to an outer x for the first call to g() in the block, but not the second in the block (and various for the indirect call via f()) -- dynamic scope! -- or else the uses before |const x|'s C++-style implicit scope has opened must be errors (early or not), which is indistinguishable from hoisting.

So at last week's meeting, we finally agreed to the earlier rules: all block-scoped bindings hoist to top of block, with a temporal dead zone for use of let and const before iniitalization.

The initialization point is also important. Some folks wondered if we could not preserve var's relative simplicity: var x = 42; is really var x; x = 42, and then the var hoists (this makes for insanity within 'with', which recurs with 'let' in block vs. 'var' of same name in inner block -- IIRC we agreed to make such vars that hoist past same-named let bindings be early errors).

With var, the initialization is just an assignment expression. A name use before that assignment expression has been evaluated results in the default undefined value of the var, assuming it was fresh. There is no read and write barrier requirement, as there is (in general, due to closures) for the temporal dead zone semantics.

But if we try to treat let like var, then let and const diverge. We cannot treat const like var and allow any assignment as "initialization", and we must forbid assignments to const bindings -- only the mandatory initializer in the declaration can initialize. Trying to allow the "first assignment to a hoisted const" to win quickly leads to two or more values for a single const binding:

{ x = 12; if (y) return x; const x = 3; ... }

The situation with let is constrained even ignoring const. Suppose we treat let like var, but hoisted to block top instead of body/program top, with use before set reading undefined, or in an alternative model that differs from var per temporal dead zone, throwing. So:

{ print(x); x = 12; let x; }

would result in either print being called with undefined or an error on the use of x before it was set by the assignment expression-statement -- those are the two choices given hoisting.

But then:

{ x = 12; print(x); let x; }

would result in either 12 being printed or an error being thrown assigning to x before its declaration was evaluated.

Any mixture of error with non-error (printing undefined or 12) is inconsistent. One could defend throwing in the use-before-assignment case, but it's odd. And throwing in both cases is the earlier consensus semantics of temporal dead zone with a distinct state for lack of initialization (even if the initialization is implicit, e.g., in a declaration such as let x; being evaluated). Here "initialization" is distinguished from assignment expressions targeting the binding.

Trying to be like var, printing undefined or 12, is possible but future-hostile to guards and gratuitously different from const:

{ x = 12; const G = ...; let x ::G = "hi"; }

We want to be future-proof for guards, and even more important: we want to support refactoring from let to const. Ergo, only temporal dead zone with its barriers is tenable.

There remains an open issue: without closures obscuring analysis, it is easy to declare use before initialization within the direct expression-statement children of a given block to be early errors, rather than runtime errors:

{ x = 12; // can be early error print(x); // can be early error function f() { return x; // may or may not be error } escape(f); // did this call f? let x = 42; escape2(f); // did this call f? }

Some on TC39 favor normative specification of early errors for the easily-decided cases. Others want runtime-only error checking all around and point out how even the easy cases (within straight-line code in the block's direct expression-statement children) testing that reaches the block will fail fast. The question remains: what if the block is not covered by tests?

Dave Herman brought up the let/var at top level equivalence implemented in SpiderMonkey, specifically in connection with <script> tags. Sketching in pseudo-HTML:

<script type=harmony> alert = 12; // reassign built-in alert </script>

<script type=harmony> let alert = 13; // shadow built-in alert var quux = 14; // this.quux = 14 let quux = 15; // alternative: in scope for later scripts? </script>

<script> alert(quux); </script>

Dave's point was not to commend the SpiderMonkey equating of let and var at top level, but to observe that if "let is the new var", then depending on how multiple successive script elements' contents are scoped, you may still need to use var in Harmony -- let won't be enough, if it binds only within the containing <script> element's scope.

Recall that Harmony removes the global (window in browsers) object from the scope chain, replacing it with a lexical environment with (generally) writable bindings. Each script starts with a fresh lexical environment, although it might be nested (see next paragraph).

For scripts that do not opt into Harmony, there's no issue. The global object is on the scope chain and it is used serially by successive script elements.

The question for Harmony scripts boils down to: should successive Harmony scripts nest lexical scopes in prior scripts' scopes, like matryoshka dolls? Or should each script opted into Harmony be its own module-like scope, in which case to propagate bindings to later scripts, one would have to

<script type=harmony> export let quux = 14; // available here and in later scripts </script>

This remains an open question in TC39. Some liked the explicit 'export' requirement, the implicit module scope. Others objected that migrating code would expect the nested semantics, which was not inherently evil or unsafe.

--- end of block scope discussion ---

173 enh Normal All allen at wirfs-brock.com CONF --- FutureReservedWords should not be allowed as a function name or argument name of a strict func.

Deferred but this was considered straightforward, per the comment 0.

157 min --- All allen at wirfs-brock.com CONF --- "do{;}while(false)false" prohibited in spec but allowed in consensus reality

Approved -- this is the de-facto standard whereby do;while(0)x will have a semicolon inserted before x.

== Minimal Classes ==

Dave presented his pitch for minimal classes, posted to es-discuss previously here:

esdiscuss/2011-June/015559

This subset includes class C {...}, class D extends C {...}, super calls in constructors, and method syntax for defining non-enumerable function-valued data properties on the class's prototype object.

The premise is that classes have significant open issues that will take time to resolve, at high opportunity cost, without clear consensus in sight for some of the issues; whereas the minimal "profile" has consensus already and will do good in ES.next without question.

Dave quickly acknowledged Mark M.'s long-running and indefatigable effort to get classes as sugar into Harmony, and how this was not in any way lost forever via minimal classses. Some of the early work used the closure pattern, which is not the main pattern supported by the current proposal (but it is supported if you write public methods in the constructor). So, credit to Mark for his work.

Waldemar thought the approach too minimal, and suggested zero-inheritance as a different axis on which to miminize. Others disagreed with that, noting how the existing pattern (the cowpath to pave) has at least subclassing and super-call boilerplate in library code and generated JS to absorb.

General agreement to work through open issues (recorded in the wiki or not) with the harmony:classes proposal.

Open issues and discussion/resolution summaries:

  1. return allowed from constructor?

Dave H.: This is a lesser cowpath possible with functions-as-constructors-with-prototypes in JS today, we should pave it.

Mark M., others: we want minimal object layout or "shape" declarative guarantees with classes, return {unshaped: "haha"}; in the middle of a constructor for a class with public x, y, z; properties defeats this goal.

Dave: no shape guarantees.

Others: must have shape guarantees, at least "the declared properties exist, at the moment the constructor returns" (ignoring const classes, which have frozen prototypes, instances, constructors, and at least sealed instance properties).

Mark M.: argument by analogy to module objects.

Dave, Sam, Brendan: module system is second class, module object reflections do not correspond to class-as-factory instances. This is not to say "no shape guarantees", however (Brendan at least).

Alex R.: minority-use-case users can fall back to declaring constructor functions if they need to return from constructor.

Consensus is: RESOLVED, return disallowed from class constructor body.

  1. private: section vs. private prefix keyword

Some favor sections, others do not. Waldemar cites original-JS2/ES4 private {...} braced forms to distribute privaste, static (class), etc. across a group of declarations, with the braced body being a declaration list, not an object literal. Bob Nystrom proposed C++-style section syntax here:

www.mail-archive.com/[email protected]/msg09070.html

No resolution.

  1. private variable use-syntax

The private(this).x, private(other).x syntax in the wiki'ed proposal is an intentional non-starter: too verbose, wrongly suggests that private variables are properties of some "private data record" object.

But what to use instead? @ as prefix (this-based) and infix (restricted, no LineTerminator to left, for other-based) private-keyed property refs has been mooted but is not proposed in the classes proposal, and my sense is the committee is not ready to annex @.

Meanwhile, Allen proposed (see PDF link harmony:private-name-alternatives.pdf at bottom of harmony:private_name_objects) that we allow private name objects to be used in object literals like so:

return { [MyPrivateKey]: ..., publicNameHere: ... };

and this was agreed to at last week's meeting.

Given this extension, whose [] syntax mirrors the computed property name "indexing" used with private name objects as property keys, we agreed to defer private(this)/private(other) replacement syntax from the classes proposal and revisit later, based on usability experience with private name objects including this extension to object literal syntax.

  1. class-side inheritance (method, this, super)

Some (Smalltalk and therefore Ruby matter to these folks) on TC39 want, others are indifferent. No one was hostile, but we did not resolve to add class-side inheritance yet.

People agree that "static" is the wrong keyword, but may have weight for some coming from C++ and Java. General desire to use "class" but not as prefix keyword.

  1. no magic syntax for constructor body

This matters more if we attempt to unify class body syntax with object literal extended syntax, or somehow make a desugaring from one to the other. The general form of this open issue is item (6), but to focus on instance properties/variables, we split this one in two:

5a. public x = x inside a constructor taking parameter x. We do not have a better alternative at this time. The C++-style public: section idea discussed on the list (proposed by Bob Nystrom, see (2) above) separates declaration from likely initialization based on constructor parameters or other constructor/instance-dependent computation.

5b. private w = ... instead a constructor. Per 3, we removed this for now, deferring to private name objects and agreeing to revisit later.

  1. do not abuse lexical binding declarative forms to define properties (prototype, class)

This was contentious at first, because modules use 'export function f(...){...}' and 'export const K = ...' declarative syntax extended by prefixing with 'export', so Mark at least found the use in class syntax of declarative forms to bind prototype properties plausible:

class C { function f() {} // C.prototype.f? let x; // C.prototype.x? const k; // C.prototype.k? class Inner{} // C.prototype.Inner, (c = new C, c.Inner) m(){} // method m, no controversy, no comma after get gs(){} // getter gs, no controversy, no semicolon after set gs(x){...} // setter gs, optional, no controversy pp = 42; // prototype property pp, controversial syntax -- assignment? constructor(){} // just a method, unless the body is special syntax (1) }

After some discussion it became clear that there is no symmetry with module instances without 'const class': module exports are sealed properties, module instance objects are not extensible. Class instances by default are extensible, and class prototypes in particular are as mutable as today. In general Harmony moves binding forms away from defining properties on objects (except via reflection, as in the module instance case) and to lexical scope.

This item was not resolved, but it left all of the variations sketched above with comments ending in ? as open issues. Methods (including constructor, even if its body is a special form), getters, and setters are ok. All others are not yet resolved as consensus features of classes in ES.next.

I observed that at the rate of progress resolving open issues in the classes proposal (counting generously), we needed 2.5 more meetings to resolve the rest. But the remaining issues are actually bigger, and lack live alternative proposals that helped resolve (1) and (5b).

To make progress, we need to avoid "hovering" at a bogus "consensus" where people agree with the idea or general goal of classes, without getting concrete final agreement on all the details.

# Oliver Hunt (13 years ago)

On Aug 4, 2011, at 2:29 PM, Brendan Eich wrote:

  1. return allowed from constructor? ... Consensus is: RESOLVED, return disallowed from class constructor body.

What about return with no value -- there are cases where such early returns are useful, and you would not be allowed to specify your own value (eg. there's an implicit return of |this|). Most class-oriented languages have this behaviour (C++, Java, C#, etc), those that don't support early returns, frequently don't support return-statements at all (eg. the various object-pascal dialects).

  1. private variable use-syntax

My 2c: i've always felt a prefix should be required to access a globally scoped variable/property in general, but no doubt people would complain about that degree of verbosity. That said I'm unsure why there's such a desire to avoid standard scoping rules of every other language (where the properties of |this| are implicitly in scope), with a mechanism to disambiguate access to the global scope if necessary (As C++ does, although i'm not suggesting C++'s syntax).

# Brendan Eich (13 years ago)

On Aug 4, 2011, at 3:15 PM, Oliver Hunt wrote:

On Aug 4, 2011, at 2:29 PM, Brendan Eich wrote:

  1. return allowed from constructor? ... Consensus is: RESOLVED, return disallowed from class constructor body.

What about return with no value -- there are cases where such early returns are useful, and you would not be allowed to specify your own value (eg. there's an implicit return of |this|). Most class-oriented languages have this behaviour (C++, Java, C#, etc), those that don't support early returns, frequently don't support return-statements at all (eg. the various object-pascal dialects).

That is a good point, one I think someone raised at the meeting (my note-taking failed there). This resolution was about banning return expression; in constructors, but not banning return; used well to avoid tortured and over-indented control flow.

The grammar at harmony:classes does not restrict Statement at all, currently. The return-the-result-of-an-expression restriction could be done grammatically but it is easier to do with semantics, prose in this case.

  1. private variable use-syntax

My 2c: i've always felt a prefix should be required to access a globally scoped variable/property in general, but no doubt people would complain about that degree of verbosity. That said I'm unsure why there's such a desire to avoid standard scoping rules of every other language (where the properties of |this| are implicitly in scope),

You must mean languages that have static typing.

We have covered this before:

esdiscuss/2011-June/015125, esdiscuss/2011-June/015129, esdiscuss/2011-June/015196

with a mechanism to disambiguate access to the global scope if necessary (As C++ does, although i'm not suggesting C++'s syntax).

Object != scope, the Harmony scoping model is lexical. Mutable instances with mutable prototypes violate lexical scope if injected into scope environments.

Even if you managed to put just the private names that were declared by the class as private instance variable names (we agreed to take out the syntax for this, for the moment), how would you know whether other.foo where foo was private in the class should be the private name object, or the string-equated 'foo'?

Think of Point with private x, y and an equals method. Another method has to access a plain old object with public property x. You can't try private and then public (won't be right for edge cases with both names mapped, possibly differently along the prototype chain; won't be efficient). Unlike languages with static type information about |this| and other parameters, in JS you need to say when you mean to access a private-name-keyed property.

# Oliver Hunt (13 years ago)

On Aug 4, 2011, at 6:45 PM, Brendan Eich wrote:

On Aug 4, 2011, at 3:15 PM, Oliver Hunt wrote:

On Aug 4, 2011, at 2:29 PM, Brendan Eich wrote:

  1. return allowed from constructor? ... Consensus is: RESOLVED, return disallowed from class constructor body.

What about return with no value -- there are cases where such early returns are useful, and you would not be allowed to specify your own value (eg. there's an implicit return of |this|). Most class-oriented languages have this behaviour (C++, Java, C#, etc), those that don't support early returns, frequently don't support return-statements at all (eg. the various object-pascal dialects).

That is a good point, one I think someone raised at the meeting (my note-taking failed there). This resolution was about banning return expression; in constructors, but not banning return; used well to avoid tortured and over-indented control flow.

The grammar at harmony:classes does not restrict Statement at all, currently. The return-the-result-of-an-expression restriction could be done grammatically but it is easier to do with semantics, prose in this case.

Indeed, one way to support this would be to have "modes" in the grammar. If memory serves me correctly ANTLR supports such a concept, although I'm not sure how widely the idea ever spread. Logically this is syntactic sugar around the large expansion of productions that would typically be necessary.

  1. private variable use-syntax

My 2c: i've always felt a prefix should be required to access a globally scoped variable/property in general, but no doubt people would complain about that degree of verbosity. That said I'm unsure why there's such a desire to avoid standard scoping rules of every other language (where the properties of |this| are implicitly in scope),

You must mean languages that have static typing.

We have covered this before:

esdiscuss/2011-June/015125, esdiscuss/2011-June/015129, esdiscuss/2011-June/015196

with a mechanism to disambiguate access to the global scope if necessary (As C++ does, although i'm not suggesting C++'s syntax).

Object != scope, the Harmony scoping model is lexical. Mutable instances with mutable prototypes violate lexical scope if injected into scope environments.

Even if you managed to put just the private names that were declared by the class as private instance variable names (we agreed to take out the syntax for this, for the moment), how would you know whether other.foo where foo was private in the class should be the private name object, or the string-equated 'foo'?

Think of Point with private x, y and an equals method. Another method has to access a plain old object with public property x. You can't try private and then public (won't be right for edge cases with both names mapped, possibly differently along the prototype chain; won't be efficient). Unlike languages with static type information about |this| and other parameters, in JS you need to say when you mean to access a private-name-keyed property.

I think part of what's confusing is I am still unclear whether class member functions are expected to apply to arbitrary objects. My assumption has always been that member functions would throw when applied to an object of the wrong class, and I don't see a good argument for an instance of a class having (essentially) a mutable shape.

To me when a developer says "class ... {" they are opting out of "an object is a generic map" semantics, and actually want a fixed object shape. Essentially any class definition and any instance of a class is |sealed| -- at this point lexical scoping can be performed as it is in other languages. This isn't about having mutable objects on the scope chain, if you see a property access that references a property name that is present in the lexical scope, you statically bind to it -- the scope chain is not involved, you know that if you're a member of class X, and you access a property P that is defined in the class X, you will be performing a lookup off of |this|, you're not exposed to mutation.

I don't buy the parameter vs. property name argument as most modern languages have this "problem" and it doesn't appear to be a significant issue. The remaining problem is the "equals" issue, and while i recognise that as a problem that would need to be solved (syntactically or otherwise) but i don't see why we must therefore add a penalty to the common case where you want to access a member. Given the amount of noise made about removing parentheses, etc it seems stunning to then say adding syntactic overhead to basic member access is a good thing.

If you want classes to produce mutable objects, and to prefix access to members of said class in instances then you might as well drop pre-definition of properties and follow the python or ruby models where you define member functions, but all member properties are dynamic. eg.

class Point def initialize(x, y) @x = x @y = y end end

or

class Point: def init(self, x, y): self.x = x self.y = y

Both of these seem to have the syntactic behaviour you're prefering, but neither language supports declarative definition of member properties -> objects of a class are still essentially property bags. If we were to go in one these directions I guess I would prefer a ruby-esque model, but only because then i'd define the behaviour as hoisting all member property rights to the class body as if they were declared separately, and then error out if there's a read of a undeclared property.

# Brendan Eich (13 years ago)

On Aug 4, 2011, at 9:50 PM, Oliver Hunt wrote:

The grammar at harmony:classes does not restrict Statement at all, currently. The return-the-result-of-an-expression restriction could be done grammatically but it is easier to do with semantics, prose in this case.

Indeed, one way to support this would be to have "modes" in the grammar. If memory serves me correctly ANTLR supports such a concept, although I'm not sure how widely the idea ever spread. Logically this is syntactic sugar around the large expansion of productions that would typically be necessary.

Parameterized productions are cool, but we have an LR(1) normative grammar and the reasons for keeping and validating LR(1) have been covered quite a bit, even this year, on es-discuss.

Generally, losing parameterization means duplication (the -NoIn vs. unsuffixed productions in ES1-5's grammar), or a semantic restriction (which can be an early error, e.g. return outside of a function, break outside of a loop, switch, or labeled statement, etc. -- even now we do not overcomplicate the grammar to forbid such things from parsing).

I think part of what's confusing is I am still unclear whether class member functions are expected to apply to arbitrary objects. My assumption has always been that member functions would throw when applied to an object of the wrong class, and I don't see a good argument for an instance of a class having (essentially) a mutable shape.

Please read the classes proposal. It's "just syntax", this is required for Harmony. We are not adding new runtime or (horrors) static semantics by which classes would make objects you could not make otherwise.

True, we could freeze -- 'const class' does some freezing and sealing -- but not by default. And we could insert |this| class checks in all methods, but those too are the exception, not the rule. I'm not sure 'const class' binds methods.

If we accept arrow function syntax, then using it in classes to allow this-binding is plausible, but this creates a bound method object per method per class instance. Heavy tax, akin to closuer pattern. Doing this avoids the |this| class check, of course.

To me when a developer says "class ... {" they are opting out of "an object is a generic map" semantics, and actually want a fixed object shape.

Including the class prototype? I think not.

Anyway, classes that restrict instance and prototype shape, thereby allowing private foo to be referenced via 'foo', do not address the other.foo issue. Ruby after Smalltalk has instance-private instance variables, not class-private as we propose. That's why @x works in Ruby and you don't need other at x.

We agreed to class-private not instance-private, and that's what the harmony:classes proposal aims at (even with the straw and now burned private(this)/private(other) syntax). But per last week's meeting, we're going to take out private magic syntax from classes and try using private name objects: this[foo], other[foo] for private name object denoted foo.

I'm open to @ for private access, personally. (We can also support decorators using @ syntax if need be, but that's not currently proposed.) But any such @ for private access would also be an infix operator, so you could say other at foo. Private name objects used with or without magic syntax for privacy in class instance variables are class-private, not instance-private. You could use private name objects freshly generated per instance, too, if you wanted to -- but we aren't even deferring syntax for that use-case.

# Andreas Rossberg (13 years ago)

On 4 August 2011 19:34, Brendan Eich <brendan at mozilla.com> wrote:

On Jul 31, 2011, at 1:04 PM, Sean Eagan wrote:

A 'receiver' argument is not needed because it would never be different than the proxy, and the proxy can either be passed as an argument or stored either as an own property of the handler, or as a value keyed by the handler in a weak map, which there seems to have been TC39 concensus on.

Ok, right -- even without the extra proxy parameter in addition to receiver, dropping receiver makes sense. Sorry to go in a circle on this.

It's a trap API change, and I agree with Mark that we need Tom to bless it.

I would welcome removing the extra receiver (or proxy) arguments from get and set traps. However, it seems to me that the main reason, currently, for having them is that they are needed by the default traps, in case the respective descriptor returned by getOwnPropertyDescriptor has a getter/setter (which need a receiver).

Arguably, making a proxy trap return getters/setters seems a somewhat pointless use case anyway. But nevertheless we need to have some reasonable semantics for it.

# Kevin Reid (13 years ago)

On Mon, Aug 8, 2011 at 08:50, Andreas Rossberg <rossberg at google.com> wrote:

I would welcome removing the extra receiver (or proxy) arguments from get and set traps. However, it seems to me that the main reason, currently, for having them is that they are needed by the default traps, in case the respective descriptor returned by getOwnPropertyDescriptor has a getter/setter (which need a receiver).

This is almost the rationale I gave earlier. To be precise, the default traps themselves need not have behavior which is implementable as an explicit trap (since they are not exposed as being functions which take the same parameters as user-supplied traps do). I feel the receiver should be provided so that user-supplied traps can mimic the default traps, with variations or optimizations.

Arguably, making a proxy trap return getters/setters seems a somewhat pointless use case anyway. But nevertheless we need to have some reasonable semantics for it.

It allows a proxy to pretend to be an object which supports Object.defineOwnProperty normally.

It allows a proxy to emulate, or wrap, an ordinary object which happens to have some accessor properties, while still being transparent to reflection (which I understand is one of the goals of the proxy facility).

(As it happens, this doesn't affect the use case which made me notice this problem; there, I am defining an emulation of DOM nodes (i.e. the accessors are such things as .innerHTML), and DOM nodes, being host objects, are allowed to do anything, I am given to understand. However, it is convenient to define my emulations as ordinary objects, which are incidentally wrapped by a proxy to implement the particularly magical parts.)

# Brendan Eich (13 years ago)

On Aug 8, 2011, at 9:46 AM, Kevin Reid wrote:

Arguably, making a proxy trap return getters/setters seems a somewhat pointless use case anyway. But nevertheless we need to have some reasonable semantics for it.

It allows a proxy to pretend to be an object which supports Object.defineOwnProperty normally.

It allows a proxy to emulate, or wrap, an ordinary object which happens to have some accessor properties, while still being transparent to reflection (which I understand is one of the goals of the proxy facility).

Good point. So we are not going in a circle -- we're spiraling toward a conclusion which is not that proxy should be provided to all traps, or none. The get and set derived traps are special: they call through to accessors.

(Proxy gurus, please check me if I'm misstating this. We need to build up non-guru understanding of the full design's rationale.)

# Andreas Rossberg (13 years ago)

On 8 August 2011 18:46, Kevin Reid <kpreid at google.com> wrote:

On Mon, Aug 8, 2011 at 08:50, Andreas Rossberg <rossberg at google.com> wrote:

I would welcome removing the extra receiver (or proxy) arguments from get and set traps. However, it seems to me that the main reason, currently, for having them is that they are needed by the default traps, in case the respective descriptor returned by getOwnPropertyDescriptor has a getter/setter (which need a receiver).

This is almost the rationale I gave earlier. To be precise, the default traps themselves need not have behavior which is implementable as an explicit trap (since they are not exposed as being functions which take the same parameters as user-supplied traps do). I feel the receiver should be provided so that user-supplied traps can mimic the default traps, with variations or optimizations.

Arguably, making a proxy trap return getters/setters seems a somewhat pointless use case anyway. But nevertheless we need to have some reasonable semantics for it.

It allows a proxy to pretend to be an object which supports Object.defineOwnProperty normally.

It allows a proxy to emulate, or wrap, an ordinary object which happens to have some accessor properties, while still being transparent to reflection (which I understand is one of the goals of the proxy facility).

Sure, but is that necessarily something that the default traps have to be able to mimic? There is no problem programming it up yourself if you want it.

I'm not saying yes or no, just raising the question. At least the additional arguments seem like a significant complication (and asymmetry) to the proxy interface for very limited benefit.

# Kevin Reid (13 years ago)

On Tue, Aug 9, 2011 at 01:17, Andreas Rossberg <rossberg at google.com> wrote:

On 8 August 2011 18:46, Kevin Reid <kpreid at google.com> wrote:

On Mon, Aug 8, 2011 at 08:50, Andreas Rossberg <rossberg at google.com> wrote:

Arguably, making a proxy trap return getters/setters seems a somewhat pointless use case anyway. But nevertheless we need to have some reasonable semantics for it.

It allows a proxy to pretend to be an object which supports Object.defineOwnProperty normally.

It allows a proxy to emulate, or wrap, an ordinary object which happens to have some accessor properties, while still being transparent to reflection (which I understand is one of the goals of the proxy facility).

Sure, but is that necessarily something that the default traps have to be able to mimic? There is no problem programming it up yourself if you want it.

Are you proposing a revised division of fundamental vs. derived traps? If not, what do you propose the default derived get or set trap do in the event that it gets an accessor property descriptor in response to getOwnPropertyDescriptor?

# Andreas Rossberg (13 years ago)

On 9 August 2011 19:25, Kevin Reid <kpreid at google.com> wrote:

On Tue, Aug 9, 2011 at 01:17, Andreas Rossberg <rossberg at google.com> wrote:

On 8 August 2011 18:46, Kevin Reid <kpreid at google.com> wrote:

On Mon, Aug 8, 2011 at 08:50, Andreas Rossberg <rossberg at google.com> wrote:

Arguably, making a proxy trap return getters/setters seems a somewhat pointless use case anyway. But nevertheless we need to have some reasonable semantics for it.

It allows a proxy to pretend to be an object which supports Object.defineOwnProperty normally.

It allows a proxy to emulate, or wrap, an ordinary object which happens to have some accessor properties, while still being transparent to reflection (which I understand is one of the goals of the proxy facility).

Sure, but is that necessarily something that the default traps have to be able to mimic? There is no problem programming it up yourself if you want it.

Are you proposing a revised division of fundamental vs. derived traps? If not, what do you propose the default derived get or set trap do in the event that it gets an accessor property descriptor in response to getOwnPropertyDescriptor?

I guess my point was that there is no natural law demanding that the default traps have "perfect" semantics. So we could e.g. pass null to the accessors in that case. If you need a different semantics, program it. I'm not necessarily proposing that path, just pointing out the possibility.

On the other hand, it is not at all necessary that we are able to express the default traps as closed JS functions. If the proxy object is simply bound to a free variable in the get/set default code, for example, I don't see that as a real problem. So that might be an alternative solution as far as the spec is concerned (without resorting to something more low-level).

Your argument against the latter kind of approach, as I understood it, was that you as a programmer want to be able to simulate the exact behaviour of the individual default traps in real code, in isolation. I'm not sure why this has to be a goal. As you long as you can simulate the overall behaviour easily, that seems good enough.

# Tom Van Cutsem (13 years ago)

2011/7/28 Brendan Eich <brendan at mozilla.com>

== Handler access to proxies ==

Proxy handler traps need to receive the proxy as a parameter: first, or last?

Last allows trap implementors to leave |proxy| off. It's also a compatible extension to the proposal and its prototype implementations. Putting |proxy| last may also steer implementors away from touching proxy, reducing the bugs where you infinitely diverge.

First is more normal-order (proxy, name) and some find it more aesthetically pleasing.

We've been over this at the March meeting. The consensus then was to settle on adding proxy as a last, optional argument to minimize impact on the API and recursion hazards.

Another alternative: the proxy could be "passed" via a data property on the handler. But the only use for the proxy reference is as a key in a weakmap, and if the handler references it, the handler could just as well be the key -- or the handler could simply hold the value associated by the weakmap.

Conclusion: no rationale for adding a |proxy| parameter to all traps.

There are a couple of motivating goals that may have been overlooked:

  • as David pointed out, giving traps access to |proxy| also gives them access to the proxy's prototype, which is the enabler to turn getPropertyDescriptor and getPropertyNames into derived traps (cf. < strawman:proxy_derived_traps>)

  • passing |proxy| as an argument to the derived traps allows a straightforward Javascript implementation of these traps to more closely reflect the actual default behavior. For instance, the following default |get| trap doesn't perform proper normalization for property descriptors:

    get: function(receiver, name) { var desc = this.getPropertyDescriptor(name); // desc is not normalized if (desc === undefined) { return undefined; } if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } },

While the following does:

get: function(name, proxy) { var desc = Object.getPropertyDescriptor(proxy, name); if (desc === undefined) { return undefined; } if ('value' in desc) { return desc.value; } else { // note: no need to store desc.get into a local var, it's guaranteed // to be a data property on normalized property descriptors if (desc.get === undefined) { return undefined; } return desc.get.call(proxy); } },

This ties in with Kevin's request of being able to more accurately emulate default trap implementations in Javascript. This becomes important when writing wrappers for handler objects. I've come across this myself when working on the FixedHandler.

Were these goals considered? My feeling is that 2 less fundamental traps for the price of an optional |proxy| argument is a good deal. Also, given that |get| and |set| require the proxy/receiver argument anyway, passing the proxy to all traps makes for a more consistent API.

# Tom Van Cutsem (13 years ago)

2011/7/28 Andreas Rossberg <rossberg at google.com>

On 28 July 2011 10:35, David Bruant <david.bruant at labri.fr> wrote:

I think we discussed already the idea of "proxy" being passed as a data property to the handler and came to the conclusion that it may not be a good idea, because it breaks the stratification. If two proxies use the same handler as in [2], then, there is an ambiguity on what the value of this property should be.

The solution we discussed is to simply use prototypes. That is, share handler methods by putting them on a (single) prototype object, and have per-proxy instances that carry the individual proxy references (or other per-proxy data, for that matter).

I agree that's a good pattern to achieve application-specific per-proxy handler state, but it doesn't standardize the proxy-backlink, so handler authors cannot in general rely on the existence of e.g. a |handler.proxy| property. That precludes the two use cases outlined in my previous mail, which relate to generic trap code that should work with any proxy handler.

# Tom Van Cutsem (13 years ago)

2011/8/8 Brendan Eich <brendan at mozilla.com>

Good point. So we are not going in a circle -- we're spiraling toward a conclusion which is not that proxy should be provided to all traps, or none. The get and set derived traps are special: they call through to accessors.

(Proxy gurus, please check me if I'm misstating this. We need to build up non-guru understanding of the full design's rationale.)

Indeed, get and set are special because they call through to accessors. But in that mindset, we can think of getPropertyDescriptor and getPropertyNames as special derived traps because they walk the prototype chain (requiring access to the proxy, or more precisely: the proxy's prototype). Why would we pass |proxy| to get/set but not to getPD/getPNs?

So, counting getPD and getPNs as derived, that leaves us with four derived traps that require access to the proxy. If we leave it at these four, we can make a note in the documentation explaining why they need |proxy|. Or, we could generalize and provide |proxy| as an argument to all traps. My feeling is that developers will find this natural, since the Proxy API does allow the creation of multiple proxies that share the exact same handler object. The additional |proxy| argument allows such a shared handler object to naturally tell apart the proxies for which it is used.

# Brendan Eich (13 years ago)

On Aug 11, 2011, at 11:20 AM, Tom Van Cutsem wrote:

We've been over this at the March meeting. The consensus then was to settle on adding proxy as a last, optional argument to minimize impact on the API and recursion hazards.

Right, and I agree putting proxy last is best. Not all agreed at the July meeting, but we may have to revisit, since we tried to get rid of proxy parameters altogether and thereby dodged the at end vs. at start issue.

Optional at end is best because most traps do not need to declare or use proxy, and furthermore, doing so (as one must if proxy comes first, except for single-parameter traps) raises the odds of mistakenly trapping through the proxy, creating runaway recursion.

But see below on receiver vs. proxy being a distinction without a difference for get and set.

Another alternative: the proxy could be "passed" via a data property on the handler. But the only use for the proxy reference is as a key in a weakmap, and if the handler references it, the handler could just as well be the key -- or the handler could simply hold the value associated by the weakmap.

Conclusion: no rationale for adding a |proxy| parameter to all traps.

There are a couple of motivating goals that may have been overlooked:

  • as David pointed out, giving traps access to |proxy| also gives them access to the proxy's prototype, which is the enabler to turn getPropertyDescriptor and getPropertyNames into derived traps (cf. strawman:proxy_derived_traps)

That is a point we cleanly missed in the July meeting. Thanks for raising it. I hope we can discuss here in advance of the September meeting and strive for consensus.

  • passing |proxy| as an argument to the derived traps allows a straightforward Javascript implementation of these traps to more closely reflect the actual default behavior. For instance, the following default |get| trap doesn't perform proper normalization for property descriptors:

    get: function(receiver, name) { var desc = this.getPropertyDescriptor(name); // desc is not normalized if (desc === undefined) { return undefined; } if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } },

While the following does:

get: function(name, proxy) { var desc = Object.getPropertyDescriptor(proxy, name); if (desc === undefined) { return undefined; } if ('value' in desc) { return desc.value; } else { // note: no need to store desc.get into a local var, it's guaranteed // to be a data property on normalized property descriptors if (desc.get === undefined) { return undefined; } return desc.get.call(proxy); } },

This seems to miss Sean's point that receiver is never other than the proxy for get and set. That follows from how native object [[Get]] works in ES5.

This ties in with Kevin's request of being able to more accurately emulate default trap implementations in Javascript. This becomes important when writing wrappers for handler objects. I've come across this myself when working on the FixedHandler.

I'm sympathetic to this point, but it doesn't require proxy trailing parameters for all traps -- just for some.

Were these goals considered? My feeling is that 2 less fundamental traps for the price of an optional |proxy| argument is a good deal. Also, given that |get| and |set| require the proxy/receiver argument anyway, passing the proxy to all traps makes for a more consistent API.

If we put proxy last for get and set (replacing the leading receiver), we have a global consistency, but the "local" (w.r.t. underlying spec meta-methods, and base-level accessor functions) parameter order is oddly reversed: (name, proxy) instead of (proxy, name).

Consistency is never consistent or complete along all the dimensions. (h/t Goedel ;-)

# Mark S. Miller (13 years ago)

On Thu, Aug 11, 2011 at 12:02 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Aug 11, 2011, at 11:20 AM, Tom Van Cutsem wrote:

There are a couple of motivating goals that may have been overlooked:

  • as David pointed out, giving traps access to |proxy| also gives them access to the proxy's prototype, which is the enabler to turn getPropertyDescriptor and getPropertyNames into derived traps (cf. < strawman:proxy_derived_traps>)

That is a point we cleanly missed in the July meeting. Thanks for raising it. I hope we can discuss here in advance of the September meeting and strive for consensus.

Hi Tom, good to have you back!

At that meeting and later on email, I had a disturbing sense that there was some crucial point you had explained to me that I was forgetting. This was it. Thanks.

# Tom Van Cutsem (13 years ago)

2011/8/11 Brendan Eich <brendan at mozilla.com>

This seems to miss Sean's point that receiver is never other than the proxy for get and set. That follows from how native object [[Get]] works in ES5.

You're right. Using the "get" trap as an example was silly, since it already has access to receiver, which is the proxy. Your comment made me revisit all derived traps again, and I think I found a compelling and easy-to-understand rule for determining whether or not a trap needs access to proxy/receiver: if the trap deals with inherited properties, it needs access to |proxy|.

Using that rule, the following traps require access to |proxy|: get, set, getPropertyDescriptor, getPropertyNames, has, enumerate (incidentally, all of these traps are or can be made derived)

All other traps deal only with "own" properties, and do not need |proxy|: getOwnPropertyDescriptor, getOwnPropertyNames, defineProperty, delete, fix, hasOwn, keys

getPropertyDescriptor and getPropertyNames require access to |proxy| to walk the prototype-chain. The default behavior of get, set, has and enumerate is specified in terms of either or both of these traps, so they also need access to |proxy|. Note that get and set depend on |proxy| for two reasons:

  1. to correctly set |this| for accessors, 2) to be able to lookup a property on the prototype chain (via |Object.getPropertyDescriptor(proxy, name)| )
# David Bruant (13 years ago)

Le 12/08/2011 13:53, Tom Van Cutsem a écrit :

2011/8/11 Brendan Eich <brendan at mozilla.com <mailto:brendan at mozilla.com>>

This seems to miss Sean's point that receiver is never other than
the proxy for get and set. That follows from how native object
[[Get]] works in ES5.

You're right. Using the "get" trap as an example was silly, since it already has access to receiver, which is the proxy. Your comment made me revisit all derived traps again, and I think I found a compelling and easy-to-understand rule for determining whether or not a trap needs access to proxy/receiver: if the trap deals with inherited properties, it needs access to |proxy|.

Using that rule, the following traps require access to |proxy|: get, set, getPropertyDescriptor, getPropertyNames, has, enumerate (incidentally, all of these traps are or can be made derived)

All other traps deal only with "own" properties, and do not need |proxy|: getOwnPropertyDescriptor, getOwnPropertyNames, defineProperty, delete, fix, hasOwn, keys

getPropertyDescriptor and getPropertyNames require access to |proxy| to walk the prototype-chain. The default behavior of get, set, has and enumerate is specified in terms of either or both of these traps, so they also need access to |proxy|. Note that get and set depend on |proxy| for two reasons: 1) to correctly set |this| for accessors, 2) to be able to lookup a property on the prototype chain (via |Object.getPropertyDescriptor(proxy, name)| )

If the reason access is given to the proxy is to give access to the prototype, we could as well just give access to the prototype, couldn't we?

In the proxy API, the handler, prototype and call/construct traps are all related to an internal property (things defined in ES5.1 - 8.6.2 es5.github.com/#x8.6.2) What about passing one handler-like object with everything? This way, all internals will be available in the argument. The API could be reduced to Proxy.create(handler). Regarding [[prototype]], handler.prototype could be just a initialzation value for a copy of the handler, or handler.prototype could be imposed to be a non-configurable, non-writable data property (TypeError if not the case) which will not require to do a handler copy. handler.call and handler.construct would have the rules that callTrap and constructTrap have. And typeof Proxy.create(handler) === 'function' <=> typeof handler.call === 'function' (to fit the ES5.1 definition of a

function)

# Andreas Rossberg (13 years ago)

On 12 August 2011 13:53, Tom Van Cutsem <tomvc.be at gmail.com> wrote:

I think I found a compelling and easy-to-understand rule for determining whether or not a trap needs access to proxy/receiver: if the trap deals with inherited properties, it needs access to |proxy|.

Using that rule, the following traps require access to |proxy|: get, set, getPropertyDescriptor, getPropertyNames, has, enumerate (incidentally, all of these traps are or can be made derived)

All other traps deal only with "own" properties, and do not need |proxy|: getOwnPropertyDescriptor, getOwnPropertyNames, defineProperty, delete, fix, hasOwn, keys

Although that rule seems fairly simple, I still find a half/half situation unnecessarily confusing and error-prone. I would strongly vote for making the API consistent. That is, either equip all methods with a proxy argument (preferably as first), or none.

# Mark S. Miller (13 years ago)

On Fri, Aug 12, 2011 at 5:23 AM, Andreas Rossberg <rossberg at google.com>wrote:

On 12 August 2011 13:53, Tom Van Cutsem <tomvc.be at gmail.com> wrote:

I think I found a compelling and easy-to-understand rule for determining whether or not a trap needs access to proxy/receiver: if the trap deals with inherited properties, it needs access to |proxy|.

Using that rule, the following traps require access to |proxy|: get, set, getPropertyDescriptor, getPropertyNames, has, enumerate (incidentally, all of these traps are or can be made derived)

All other traps deal only with "own" properties, and do not need |proxy|: getOwnPropertyDescriptor, getOwnPropertyNames, defineProperty, delete, fix, hasOwn, keys

Although that rule seems fairly simple, I still find a half/half situation unnecessarily confusing and error-prone. I would strongly vote for making the API consistent. That is, either equip all methods with a proxy argument (preferably as first), or none.

I agree, except for the "(preferably as first)". For me, this discussion has successfully reconstructed the rationale for the strawman as it was, including placing the parameter last. The reasons to put it last still hold:

  • To encourage its omission from parameter lists unless it is needed (the rare case)
  • To encourage that case to be rare, since touching the proxy during a trap is surprisingly likely to lead to infinite regress unless great care is taken.

+1 for the strawman as it was. Tom, apologies for forgetting this rationale when presenting at the July meeting, delaying it all another two months.

# Tom Van Cutsem (13 years ago)

2011/8/12 David Bruant <david.bruant at labri.fr>

If the reason access is given to the proxy is to give access to the prototype, we could as well just give access to the prototype, couldn't we?

Except |get| and |set| also require the proxy itself. Passing |proxy| to some traps and |prototype| to others complicates matters even more.

In the proxy API, the handler, prototype and call/construct traps are all related to an internal property (things defined in ES5.1 - 8.6.2 es5.github.com/#x8.6.2) What about passing one handler-like object with everything? This way, all internals will be available in the argument. The API could be reduced to Proxy.create(handler). Regarding [[prototype]], handler.prototype could be just a initialzation value for a copy of the handler, or handler.prototype could be imposed to be a non-configurable, non-writable data property (TypeError if not the case) which will not require to do a handler copy. handler.call and handler.construct would have the rules that callTrap and constructTrap have. And typeof Proxy.create(handler) === 'function' <=> typeof handler.call === 'function' (to fit the ES5.1 definition of a function)

I see your point but still prefer the current API. |Proxy.create(handler, proto)| makes it much clearer that |proto| is intended to be fixed at creation time than requiring |handler| to define a non-writable data property named "prototype".

# Tom Van Cutsem (13 years ago)

2011/8/12 Andreas Rossberg <rossberg at google.com>

On 12 August 2011 13:53, Tom Van Cutsem <tomvc.be at gmail.com> wrote:

I think I found a compelling and easy-to-understand rule for determining whether or not a trap needs access to proxy/receiver: if the trap deals with inherited properties, it needs access to |proxy|.

[...]

Although that rule seems fairly simple, I still find a half/half situation unnecessarily confusing and error-prone. I would strongly vote for making the API consistent. That is, either equip all methods with a proxy argument (preferably as first), or none.

Despite the simple rule, I too would prefer passing |proxy| to all traps (but not as first argument), even though, as Brendan noted, it's not strictly necessary and consistency in one dimension sacrifices consistency in another. It would be good to hear more opinions on this.

# Kevin Reid (13 years ago)

On Fri, Aug 12, 2011 at 07:51, Tom Van Cutsem <tomvc.be at gmail.com> wrote:

2011/8/12 David Bruant <david.bruant at labri.fr>

Regarding [[prototype]], handler.prototype could be just a initialzation value for a copy of the handler, or handler.prototype could be imposed to be a non-configurable, non-writable data property (TypeError if not the case) which will not require to do a handler copy.

handler.call and handler.construct would have the rules that callTrap and

constructTrap have. And typeof Proxy.create(handler) === 'function' <=> typeof handler.call === 'function' (to fit the ES5.1 definition of a function)

I see your point but still prefer the current API. |Proxy.create(handler, proto)| makes it much clearer that |proto| is intended to be fixed at creation time than requiring |handler| to define a non-writable data property named "prototype".

This reminds me of what I did in my revision of the E proxy API. If it were to be the case that the proxy is completely defined by a single handler, then it would be (less im)plausible to have it be the case that

Proxy.create(handler) === Proxy.create(handler)

That is, a proxy's identity is fully specified by its parameters, the handler. In the E context this has benefits for our equality semantics and garbage collection. If this were the case in JavaScript, it would mean that traps would never need a proxy parameter, because Proxy.create(this) would substitute for it. (The handler methods in E indeed don't get the proxy as a parameter.)

You probably don't want to do this, but the observation was interesting to me.

# Brendan Eich (13 years ago)

On Aug 12, 2011, at 7:55 AM, Tom Van Cutsem wrote:

2011/8/12 Andreas Rossberg <rossberg at google.com> On 12 August 2011 13:53, Tom Van Cutsem <tomvc.be at gmail.com> wrote:

I think I found a compelling and easy-to-understand rule for determining whether or not a trap needs access to proxy/receiver: if the trap deals with inherited properties, it needs access to |proxy|.

[...]

Although that rule seems fairly simple, I still find a half/half situation unnecessarily confusing and error-prone. I would strongly vote for making the API consistent. That is, either equip all methods with a proxy argument (preferably as first), or none.

Despite the simple rule, I too would prefer passing |proxy| to all traps (but not as first argument), even though, as Brendan noted, it's not strictly necessary and consistency in one dimension sacrifices consistency in another. It would be good to hear more opinions on this.

Mark repeated the reasons I gave to put proxy last, which Andreas has yet to rebut :-P. Our reasons are about usability, which trumps aesthetics every time (in my youth I favored little-endian byte order because LSbyte on Lowest-address was prettier due to the least/lowest rhyming than alternatives; then I had to read hexdumps...).

# David Bruant (13 years ago)

Le 12/08/2011 16:55, Tom Van Cutsem a écrit :

2011/8/12 Andreas Rossberg <rossberg at google.com <mailto:rossberg at google.com>>

On 12 August 2011 13:53, Tom Van Cutsem <tomvc.be at gmail.com
<mailto:tomvc.be at gmail.com>> wrote:
> I think I found a compelling and easy-to-understand
> rule for determining whether or not a trap needs access to
proxy/receiver:
> if the trap deals with inherited properties, it needs access to
|proxy|.

[...]

Although that rule seems fairly simple, I still find a half/half
situation unnecessarily confusing and error-prone. I would strongly
vote for making the API consistent. That is, either equip all methods
with a proxy argument (preferably as first), or none.

Despite the simple rule, I too would prefer passing |proxy| to all traps (but not as first argument), even though, as Brendan noted, it's not strictly necessary and consistency in one dimension sacrifices consistency in another. It would be good to hear more opinions on this.

We have identified that |proxy| could be used to retrieve the prototype. What other "useful" information could be retrieved from |proxy|? Its [[Prototype]], its [[Class]], its identity (as |this| for getters/setters in get/set traps and as WeakMap key, however this has never had a lot of support on es-discuss)? [[call]] would be accessible via proxy() or proxy.call() [[construct]] would be accessible via new proxy() All other internal things seem accessible via the handler (|this| from within a handler method context). Maybe that there is another dimension of inconsistency which is that internal properties and methods can be accessed via two things: the handler and the proxy. "two things"... rather than one (the handler) as it was originally. Hence my previous e-mail about bringing everything back to the handler (prototype, call, construct, etc.)

Also, it's a bit strange that all traps can be changed dynamically while call and construct can't. Well, that's not entirely true: call and construct could contain some context in a closure that could change their behavior over time.

var b = true;

function f(){ if (b) f1(); else f2(); }

b = Math.random() < 0.5;

i can provide f as a call trap, it is not more "fixed" than any other trap provided in the handler.

As another argument in favor of putting call and construct in the handler, there is the meta-handler-based membrane. With the current handlerMaker, the resulting object can still be called and constructed. I just gave above a solution for that issue, but having call and construct in the handler would allow the generic solution to work without extra effort.

# David Bruant (13 years ago)

Le 08/08/2011 17:50, Andreas Rossberg a écrit :

On 4 August 2011 19:34, Brendan Eich <brendan at mozilla.com> wrote:

On Jul 31, 2011, at 1:04 PM, Sean Eagan wrote:

A 'receiver' argument is not needed because it would never be different than the proxy, and the proxy can either be passed as an argument or stored either as an own property of the handler, or as a value keyed by the handler in a weak map, which there seems to have been TC39 concensus on. Ok, right -- even without the extra proxy parameter in addition to receiver, dropping receiver makes sense. Sorry to go in a circle on this.

It's a trap API change, and I agree with Mark that we need Tom to bless it. I would welcome removing the extra receiver (or proxy) arguments from get and set traps. However, it seems to me that the main reason, currently, for having them is that they are needed by the default traps, in case the respective descriptor returned by getOwnPropertyDescriptor has a getter/setter (which need a receiver).

Arguably, making a proxy trap return getters/setters seems a somewhat pointless use case anyway. (...)

Sorry for the late answer. i read your message and disagreed, but in lack of argument, i let it go. But i have now an argument which is unrelated to proxies and state some difference between data and accessors properties and the necessity of the latter for some use cases:

I think that if the "cookie" property of the document element (or the relevant object in its prototype chain) could be removed, it could prevent cookie theft. For this to happen, i'd need "cookie" to be configurable. One issue is that if i remove document.cookie, not only untruted scripts cannot get/set cookies, but i can't either... unless cookies are a getter/setter pair i can extract before removing the property. This, done in trusted code allows me to remove access to untrusted code while keeping the capability for myself and keeping the possibility to hand the right to get/set the cookie string to scripts i trust. This (selective access to getting/setting the cookie string) cannot be done with proxies returning data descriptors. So I think that making a proxy trap return a getter/setter is not pointless.

Code: gist.github.com/1144875 For the record, IE9 has a getter/setter pair for cookies (i don't remember if it's configurable and can't test it now). Firefox and Chrome have a data descriptor. Opera doesn't have Object.getOwnPropertyDescriptor.

That wasn't really my plan, but where is the right place to discuss ECMAScript-related topic of the DOM (like property descriptor of DOM properties)? public-script-coord?

# Tom Van Cutsem (13 years ago)

2011/8/14 David Bruant <david.bruant at labri.fr>

We have identified that |proxy| could be used to retrieve the prototype. What other "useful" information could be retrieved from |proxy|? Its [[Prototype]], its [[Class]], its identity (as |this| for getters/setters in get/set traps and as WeakMap key, however this has never had a lot of support on es-discuss)? [[call]] would be accessible via proxy() or proxy.call() [[construct]] would be accessible via new proxy() All other internal things seem accessible via the handler (|this| from within a handler method context). Maybe that there is another dimension of inconsistency which is that internal properties and methods can be accessed via two things: the handler and the proxy.

I really would not call this an inconsistency. This is really a consequence of the original design, i.e. the fact that we separate proxy from handler: that introduces an "application-level" entry point (the proxy), and a "meta-level" entry point (the handler).

"two things"... rather than one (the handler) as it was originally. Hence my previous e-mail about bringing everything back to the handler (prototype, call, construct, etc.)

Also, it's a bit strange that all traps can be changed dynamically while call and construct can't. Well, that's not entirely true: call and construct could contain some context in a closure that could change their behavior over time.

Your arguments to put |call| and |construct| in the handler are reasonable. I think that can be done. I recall we decided against it earlier, as it makes the |handler| reusable across both object and function proxies (they have the exact same API). However, passing a |handler| with call and construct traps to an object proxy should be fine.

There would be some consequences though: handler functions are currently "methods" that are invoked with |this| bound to the handler. Not so with the call and construct trap (which are thought of as functions and have |this| bound to undefined). Also, fixing a function proxy requires a change: the fix() trap should then also return a fixed call and construct trap to use, in addition to the property map (cf. an earlier proposal: < strawman:uniform_proxies>). That is a

pity because now the fix() trap is no longer uniform across object/function proxies. From what I hear from VM implementors, the current distinction between object/function proxies aligns well with the fixing process.

As for [[Prototype]], [[Class]], identity: I much prefer our current design guideline, which is that any information that is supposed to be stable/fixed is better passed as a separate argument to Proxy.create{Function}, rather than either being queried once from the handler, or requiring the handler to provide it via a non-writable, non-configurable data property. Passing |proxy| as an argument to at least the inheritance-related traps seems to cover all of the use cases we came up with thus far.

# Brendan Eich (13 years ago)

On Aug 14, 2011, at 6:55 AM, David Bruant wrote:

That wasn't really my plan, but where is the right place to discuss ECMAScript-related topic of the DOM (like property descriptor of DOM properties)? public-script-coord?

public-script-coord is good for WebIDL discussion, and also JS bindings of WebIDL (people tend to cross-post JS binding issues to public-script-coord and es-discuss).

The document.cookie property's IDL is at

www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-26809268

to wit:

       attribute DOMString       cookie;

According to the latest WebIDL spec,

www.w3.org/TR/2011/WD-WebIDL-20110712/#interface-prototype-object

attributes in WebIDL correspond to accessor properties on the interface prototype object. So once browsers conform to WebIDL and implement ES5 fully, you should be able to inspect accordingly.