Subclassing built-in constructors (was: TC39 meeting Wed 5/23/2012)

# David Bruant (6 years ago)

Le 24/05/2012 20:10, Brendan Eich a écrit :

David Bruant wrote:

Thanks for the notes and complements :-)

Le 24/05/2012 04:43, Brendan Eich a écrit :

Rick Waldron wrote:

MM:

  • From a security perspective, I'd like to move proto out of annex B and into normative body

BE:

  • If MS puts proto in IE, then it becomes defacto standard and we might as well put it in the standard.
  1. proto
  2. grawlix

Resolution: Indefinite postpone\

I.e., we defer/cancel triangle and go with normative mandatory proto. W00t! Kidding, mostly, but I perpetrated it, it is in all browsers but IE, and IE feels the heat on mobile (thank you Thomas Fuchs!). So proto is the winner already, we're just trying to forestall the inevitable. It seems that "delete Object.prototype.proto" would be the first line I'd write if I wanted to prevent proto from being harmful. However, since <| is out, after doing the delete, the use cases it was intended for (array subclassing is zepto.js' use case, right?

Not just array -- DOM too.

DOM is a very interesting case, because the proto operator was not an option for them since most of them are created by method calls (not even constructors) and the DOM API is not at all thought to change the prototype (like JSON.parse).

) cannot be achieved anymore.

So don't delete proto :-P.

Really, you can't have it both ways. If you want Zepto, you are not running SES code. Let's see if someone can "cajole" Zepto and then talk.

What I was getting at by mentionning Zepto was more about the use case that led to the decision to use proto rather than Zepto itself.

The use case of subclassing native constructors (just to clarify I'm only talking about Array, Date, WeakMap, etc. Not DOM constructors) is legitimate and has been raised and detailed in length [1] (the initial message does not point that out, but the rest of the thread does). Being able to securely do that is still a use case. The proto operator was a solution to it but it's out. proto is out because secure JS code first instruction will be to delete it on go on as if it never existed. Extracting just a proto setter is not an option according to what you're saying below. What is there left for the subclassing use case? So far, I don't see anything. The aforementioned thread raised 2 interesting alternatives to proto [2] [3] for the subclassing use case. Could it be considered to standardize any of these functions (or something else as long as it solves the use case)? Unlike grawlix, this function will be polyfillable with proto where it's available. In platform with a native support for this function, it will be possible to both use this function (solving the use case) and delete proto (doing it safely).

Does it sounds like a good alternative?

I think we can only recover is if it's possible to extract a proto setter before the delete. I haven't seen a resolution on the proto-related notes, but considering that <| is out, can it be consider to just do a setter?

No, we agreed that adds a new hazard not present in almost all browsers today or over the last 12 years: an extractable setter. We agreed to spec proto as a magic data property.

Once we're at it, for the sake of completeness there is probably no harm in adding a Reflect.setPrototype at this point, is there?

There is, just as there's a cost to Object.setPrototypeOf (the obvious place to put it to match Object.getPrototypeOf from ES5). Mark pointed out that he'd have to delete that static method too, and from every frame that might run SES code. But when mashing up SES and non-SES code, it would be better not to break the non-SES code by such deletion.

Having only the SES environment's Object.prototype.proto to delete is better.

And I realize that the new hazard is not due to proto in itself, but rather to the capability of arbitrarily changing the prototype of an object, so adding an Object.setPrototypeOf really is a step backward.

David

[1] esdiscuss/2011-March/013131 [2] esdiscuss/2011-March/013141 [3] esdiscuss/2011-March/013154

# Allen Wirfs-Brock (6 years ago)

On May 26, 2012, at 2:37 AM, David Bruant wrote:

Le 24/05/2012 20:10, Brendan Eich a écrit :

...

Not just array -- DOM too. DOM is a very interesting case, because the proto operator was not an option for them since most of them are created by method calls (not even constructors) and the DOM API is not at all thought to change the prototype (like JSON.parse).

Based upon discussions I've had with some folks (browser engineers, not DOM programmers) on the DOM side of the world, my impression is that they don't want DOM objects to be subclassable. They are disinclined to "trust" any objects other than actual "host objects" they construct. I believe the WebIDL spec. actually requires that DOM APIs reject any such "subclass" objects that were created via ES code and passed to DOM methods in parameter positions that are "typed" with specific DOM interfaces.

Personally, I think that perspective is too conservative. However, it is something to keep in mind if we move into discussions of this topic.

) cannot be achieved anymore.

So don't delete proto :-P.

Really, you can't have it both ways. If you want Zepto, you are not running SES code. Let's see if someone can "cajole" Zepto and then talk. What I was getting at by mentionning Zepto was more about the use case that led to the decision to use proto rather than Zepto itself.

I agree, if proto becomes blessed as a normal feature of the language it will get even wider use than it has today. Especially by framework builders. I'm skeptical that routinely killing it will be practical. Flipping the issue: proto is probably of most use to the developers of framework of various sorts. If proto is routinely disabled by hosting environments then such frameworks can't depend upon its availability and hence can't use it.

The use case of subclassing native constructors (just to clarify I'm only talking about Array, Date, WeakMap, etc. Not DOM constructors) is legitimate and has been raised and detailed in length 1 (the initial message does not point that out, but the rest of the thread does). Being able to securely do that is still a use case. The proto operator was a solution to it but it's out. proto is out because secure JS code first instruction will be to delete it on go on as if it never existed. Extracting just a proto setter is not an option according to what you're saying below. What is there left for the subclassing use case? So far, I don't see anything. The aforementioned thread raised 2 interesting alternatives to proto [2] [3] for the subclassing use case. Could it be considered to standardize any of these functions (or something else as long as it solves the use case)? Unlike grawlix, this function will be polyfillable with proto where it's available. In platform with a native support for this function, it will be possible to both use this function (solving the use case) and delete proto (doing it safely).

Does it sounds like a good alternative?

Because max-min classes are inching towards consensus, I suggest the that appropriate way to subclass the built-in constructors will be via |class extends|. eg

class MyArray extends Array { someMyArrayProtoMethod () {...} constructor (tag) { super(); this.tag=tag } }

I'm quite confident this can be made to work in a backwards compatible manner. I will write a strawman explaining the details.

Also I believe Date and many of the other built-ins can be respecified in a manner that enables them to be subclassed using traditional ad hoc techniques. For example, the only real "magic" in Date instance is its [[PrimitiveValue]] internal property. Date can be made subclassables by replacing [[PrimitiveValue]] by a regular property with a private name.

Finally, note that in the Internationalization spec. 1 we have tried to avoid the subclassing problem from the start. Even though the constructor defined in that spec. create instances that make extensive use of [[internal properties]] they have been defined in a manner that enables even ad hoc "subclassing". All it takes is an actual or equivalent super call. EG: Intl.Collator.call(collatorSubclassInstance). I favor adopting the pattern used in 1 for all new built-in constructors.

Allen