tc39 / ecma262

Status, process, and documents for ECMA-262
https://tc39.es/ecma262/
Other
15.04k stars 1.28k forks source link

Define exotic object from first principles #1453

Closed annevk closed 4 years ago

annevk commented 5 years ago

From @jmdyck in https://github.com/heycam/webidl/issues/655#issuecomment-467472205:

I think the reason that the ES spec says: Let _A_ be a newly created Array exotic object. rather than: Let _A_ be ObjectCreate(...). is that the latter form doesn't give you anything explicit that says "this is an Array exotic object", which you need, for example, when an algorithm step says: If _argument_ is an Array exotic object, ...

It seems to me that if "exotic object" was instead defined as an object with an overridden internal method, this could all be simplified somewhat. Incidentally, this would also make it easier to define the Location object through a mix of IDL and overridden internal methods.

allenwb commented 5 years ago

That doesn't address my point, because I'm not talking about "specifications that extend ECMA-262". As I said before, an implementation can introduce a new kind of exotic object without writing a specification that extends ECMA-262.

Introducing a new kind of exotic object is an extension to ECMA-262, but one that is allowed by Clause 16. Whether or not a separate specification is written for such an extension isn't really relevant. In either case there must be some sort of motivation and design thought behind that exotic object implementation. It would be incompetent to introduce such an exotic object into an implementation in a manner that causes it to be tagged as an "Array exotic object" without considering the full impact of that designation and to at least understand whether doing so is in conformance with ECMA-262. An implementation can, of course, choose to be out of conformance with the spec.

I think your position is something like: the set of objects that qualify as "Array exotic objects" includes only those objects that are behaviorally indistinguishable from the Array objects created by the spec. Which is certainly a valid stance, but I don't think the spec is clear on the point

Yes, that is essentially my position, particularly if by "behaviorally" you also mean "observably" . I think this was implicit even in ES6 but I agree it is a good idea to make the spec. clearer about this.

What puzzles me is why you didn't object to Domenic's much wider definition of the term in PR #1460.

Yes I now object to that definition. My reaction to it has evolved over the course of this discussion. The problem is that it is not sufficient to discriminate Array exotic objects solely on the basis of [[DefineOwnProperty]]. You also have to take into account all the other characteristics established by ArrayCreate. For example, consider a new kind of exotic object that uses the Array exotic definition of [[DefineOwnProperty]] but also defines its own [[Delete]] that prevents the creation of "holes" among the indexed properties. Programs may fail because they use Array.isArray to infer that they can safely use delete to make the array sparse.

BTW, I regret that I didn't argue more strongly against the last minute ES6 change to make the IsArray abstract operation recognize as an array a Proxy object whose target is IsArray. I think it was an ill-conceived change.

jmdyck commented 5 years ago

It would be incompetent to introduce such an exotic object into an implementation in a manner that causes it to be tagged as an "Array exotic object" without considering the full impact of that designation and to at least understand whether doing so is in conformance with ECMA-262.

Right! So it behooves ECMA-262 to be clear on what consitutes conformant behavior.

I think your position is something like: the set of objects that qualify as "Array exotic objects" includes only those objects that are behaviorally indistinguishable from the Array objects created by the spec.

Yes, that is essentially my position, particularly if by "behaviorally" you also mean "observably".

Well, "observably indistinguishable" sounds odd to me, but yes, the criterion is that one cannot observe any difference in the behavior.

I think this was implicit even in ES6 but I agree it is a good idea to make the spec. clearer about this.

Okay, so I guess the next question is, does the committee agree with this position? And then, if so, how to specify it more clearly.

domenic commented 5 years ago

Okay, so I guess the next question is, does the committee agree with this position? And then, if so, how to specify it more clearly.

I don't agree with the position that "an X exotic object" should be defined by making certain abstract operation names like ArrayCreate magic. I'm not sure if that's what's being proposed, but it's at least one interpretation.

I want predicates on objects, like "X is a Y exotic object", to be defined in terms of things we can inspect about that object. My proposal is that we define it by inspecting their internal methods.

I can understand if you want to ensure that an Array exotic object not only has the specified [[DefineOwnProperty]], but also all the other internal methods must be the default. I think that's either already in my pull request, or just a slight tweak to the wording there.

zenparsing commented 5 years ago

The other option is to just define "X is a Y exotic object" as meaning "X was created by a statement of the form: let X be a newly created Y exotic object".

domenic commented 5 years ago

I find that kind of "looking back to the past" very troubling; I'd rather have something that behaves like the rest of our spec-speak and operates on properties of the object, instead of properties of its creation site.

jmdyck commented 5 years ago

I don't agree with the position that "an X exotic object" should be defined by making certain abstract operation names like ArrayCreate magic. I'm not sure if that's what's being proposed,

By "this position", I meant:

the set of objects that qualify as "Array exotic objects" includes only those objects that are behaviorally indistinguishable from the Array objects created by the spec

and not specific spec language.

I want predicates on objects, like "X is a Y exotic object", to be defined in terms of things we can inspect about that object. My proposal is that we define it by inspecting their internal methods.

I believe that approach is compatible with the quoted position.

jmdyck commented 5 years ago

I want predicates on objects, like "X is a Y exotic object", to be defined in terms of things we can inspect about that object. My proposal is that we define it by inspecting their internal methods.

But note that if one agrees with the "indistinguishable" position, I don't think it would be enough to inspect just the internal methods. You'd also have to look at the properties. E.g., every Array exotic object created by the spec has a length property whose value is an integer Number in the range [0, 2^32-1]. An object whose length property was negative or fractional or greater than 2^32-1 would be distinguishable from an Array object created by the spec, and thus wouldn't qualify as "an Array exotic object".

I'm not sure, but it might be difficult to nail down the 'inspectable' criteria that make an object indistinguishable from an X exotic object.

allenwb commented 5 years ago

@domenic

I don't agree with the position that "an X exotic object" should be defined by making certain abstract operation names like ArrayCreate magic. I'm not sure if that's what's being proposed, but it's at least one interpretation.

There is no magic involved with names such as ArrayCreate, but there are conventions intended to make it easier to read and write specifications. You could inline all the steps of ArrayCreate(along with all the steps of abstract operations it references) at each place it is "called" in ECMA-262 or dependent specs. That would not change the semantics of the language or the requirements it imposes upon upon conforming implementations. It would just make it much harder for readers of the spec to understand and for writers to create and maintain bug-free specifications.

I want predicates on objects, like "X is a Y exotic object", to be defined in terms of things we can inspect about that object. My proposal is that we define it by inspecting their internal methods.

"Inspect" in what sense and what context? Internal methods are not necessarily things that are reified by an implementation. Regardless we are not building an ECMAScript implementation we are maintaining the ECMAScript specification. There is no active engine or storage heap in the specification, there are only words on a screen. There is no difference in the meaning of the words "is X an object created by ArrayCreate" and "is X an object that has the following characteristics: \". But there is a big difference in usability and maintainability of the specification.

I'm sensing that lurking behind your statements is a desire for some sort of internal nominal typing model for objects defined in ECMA-262 and layered specifications. Once upon a time, EMA-262 had such a thing, the [[Class]] internal property that identified most of the different kinds of objects defined in the specification. We eliminated it because it was being misused. In addition to being used inconsistently among implementations it had been allowed to leak to userland and many JavaScript developers were using the [[Class]] value, exposed via {}.toString, with an incorrect understanding of its actual meaning and the guarantees (or lack of) that it implied.

JavaScript objects are, at best, structurally/behaviorally typed. I think we know from experience that trying to overlay a nominal typing system upon JS objects is a loosing effort. At the same time, anyone including JS developers and platform designers are free to invent a branding schemes that can be inspected at runtime and used to dynamically identify "their objects".

domenic commented 5 years ago

This isn't about monimal vs. structural/behavioral typing. This is about whether it's a valid structural/behavioral type to say "was created by the X operation", instead of describing the structure and behavior of the object in question.

jmdyck commented 5 years ago

There is no difference in the meaning of the words "is X an object created by ArrayCreate" and "is X an object that has the following characteristics: [list all the post conditions that are currently established by ArrayCreate]".

If you want "X is an Array exotic object" to be equivalent to "X is an object created by ArrayCreate", then you can't have the above equivalence as well.

For instance, one of the postconditions established by ArrayCreate is that the returned object has exactly one property (length). An Array with other properties (i.e., elements!) would not have that characteristic, and so would not qualify as an Array exotic object (given the above equivalences).

Another postcondition is that the returned object's [[Extensible]] slot is *true*. But a non-extensible Array should still qualify as an Array.