Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define exotic object from first principles #1453

Closed
annevk opened this issue Feb 26, 2019 · 60 comments · Fixed by #1460
Closed

Define exotic object from first principles #1453

annevk opened this issue Feb 26, 2019 · 60 comments · Fixed by #1460

Comments

@annevk
Copy link
Member

annevk commented Feb 26, 2019

From @jmdyck in whatwg/webidl#655 (comment):

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.

@devsnek
Copy link
Member

devsnek commented Feb 26, 2019

The algorithm of ObjectCreate can't produce an exotic object. It always produces a "newly created object", the same way creating an array happens via producing a "newly created Array exotic object".

@domenic
Copy link
Member

domenic commented Feb 26, 2019

The question is, can you turn the result into an exotic object? E.g. is this legal?

  1. Let X be ObjectCreate(...)
  2. Set X's [[DefineOwnPropertyMethod]] to the one defined in section Y.

Would the result then be that X is an exotic object? Or, did we do something illegal in step 2?

The same question for if we replace step 1 by "Let X be a newly created object".

@devsnek
Copy link
Member

devsnek commented Feb 26, 2019

@domenic regardless of if its allowed, i can imagine people trying to implement the spec would be annoyed, because it would become much less explicit when you were allocating a specific exotic type.

@zenparsing
Copy link
Member

The current spec is not consistent here:

https://tc39.github.io/ecma262/#sec-modulenamespacecreate

I agree that the current editorial convention of creating an exotic object by named type and then imperatively overwriting the essential methods is a bit confusing. On the other hand, I think framing all objects as being "cut from the same cloth" might be equally confusing (as @devsnek is pointing out).

I wonder if it's possible to go the other direction and make things less imperative.

Take ArrayCreate, for example:

  • Let A be a newly created Array exotic object.
  • Set A's essential internal methods except for [[DefineOwnProperty]] to the default ordinary object definitions specified in 9.1.
  • Set A.[[DefineOwnProperty]] as specified in 9.4.2.1.

Does it help to have the second and third steps at all? Or could we just have the first step, with a link to the definition of those exotic objects?

@domenic
Copy link
Member

domenic commented Feb 26, 2019

That direction seems somewhat promising, but at its core it seems to just push the problem back into the definitio nof "a newly created Array exotic object". How would you define that?

@allenwb
Copy link
Member

allenwb commented Feb 26, 2019

I thought I had this response whatwg/webidl#655 (comment) here but it end up on the WebIDL thread.

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 26, 2019

The question is, can you turn the result into an exotic object? E.g. is this legal?

  1. Let X be ObjectCreate(...)
  2. Set X's [[DefineOwnPropertyMethod]] to the one defined in section Y.

Would the result then be that X is an exotic object? Or, did we do something illegal in step 2?

I'm pretty sure there's nothing in the spec that would make step 2 illegal.

The same question for if we replace step 1 by "Let X be a newly created object".

It's legal (and is used in the spec), but you'd need to also say how the other essential internal methods are set. (See BoundFunctionCreate, IntegerIndexedObjectCreate, ModuleNamespaceCreate, and ProxyCreate.)

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 26, 2019

@devsnek:

regardless of if its allowed, i can imagine people trying to implement the spec would be annoyed, because it would become much less explicit when you were allocating a specific exotic type.

The pseudocode would still invoke ArrayCreate or IntegerIndexedObjectCreate etc. Isn't that explicit enough?

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 26, 2019

That direction seems somewhat promising, but at its core it seems to just push the problem back into the definition of "a newly created Array exotic object". How would you define that?

9.4.2 Array Exotic Objects has the paragraph:

Array exotic objects provide an alternative definition for the [[DefineOwnProperty]] internal method. Except for that internal method, Array exotic objects provide all of the other essential internal methods as specified in 9.1.

This is almost specific enough to indicate what a newly created Array exotic object means, and also what _X_ is an Array exotic object means. (Personally, I think I prefer a more explicit, imperative approach.)

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 26, 2019

@annevk:

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.

That's pretty much how it is defined: an object with non-default behavior for any of its essential internal methods. Is the distinction between "overridden" and "not the default" important to the simplification?

@allenwb
Copy link
Member

allenwb commented Feb 26, 2019

Would the result then be that X is an exotic object? Or, did we do something illegal in step 2?

I'm pretty sure there's nothing in the spec that would make step 2 illegal.

My intent in the ES6 revisions was that this formulation would only happen in xxxCreate abstraction operations. Part of the point of create clause 9 was regularize all the adhoc ways that object creation were described in editions prior to ES6. (But hard to say "illegal" as the meta language of ECMA-262 is all convention and subject to change.

The same question for if we replace step 1 by "Let X be a newly created object".

It's legal (and is used in the spec), but you'd need to also say how the other essential internal methods are set. (See BoundFunctionCreate, IntegerIndexedObjectCreate, ModuleNamespaceCreate, and ProxyCreate.)

The only places I find it used the spec. is within the definitions of those four xxxCreate abstraction operations. It is essentially the incantation used within xxxCreate operations for bring a new object identity into existence. Each of those would be better stated as "a newly created <Bound Function, etc> exotic object" similarly to what is done for the other xxxCreates. The fact that they are written that way was probably just sloppy editing on my part. The "exotic object" formulation implies that the "branding" is tied to the initial identify of a new object and reflects the reality that in some cases implementation may need to allocate different internal data structures for various kinds of exotic objects.

@allenwb
Copy link
Member

allenwb commented Feb 26, 2019

Things have to bottom out in somewhere.

Perhaps it might be helpful to replace steps like:

  1. Let S be a newly created String exotic object.

with something like:

  1. Let S be the identity of a newly created String exotic object.

Followed by the imperative steps to complete creation of the object. But I actually prefer, the more declarative style of a set of initial state bullet points under the create step.

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 26, 2019

The thing I find odd about the phrase a newly created Array exotic object is that it doesn't really create an Array exotic object. What actually makes it an Array exotic object is the rest of the ArrayCreate operation: setting internal methods + slots and defining a length property. (In fact, a newly created [...] object doesn't even create an object per se, since it doesn't have any of its essential internal methods set, but I'm willing to let that go.)

Perhaps it might be helpful to replace steps like:
2. Let S be a newly created String exotic object.
with something like:
2. Let S be the identity of a newly created String exotic object.

I don't think inserting "the identity of" would be helpful. (The spec doesn't define "identity", and only uses it once.)

But I actually prefer, the more declarative style of a set of initial state bullet points under the create step.

So something like this?:

    2. Let _A_ be a newly created Array exotic object with the following settings:
      * [[DefineOwnProperty]] as specified in ...
      * other essential internal methods = the default ordinary object definitions.
      * [[Prototype]] = _proto_.
      * [[Extensible]] = *true*.

It's maybe an improvement over the status quo (I see pros and cons), but does it do anything to address @annevk's original concern?

@allenwb
Copy link
Member

allenwb commented Feb 27, 2019

@jmdyck

...with the following settings:

I wouldn't say "settings". Perhaps "with the following characteristics:"

I've kind of lost what @annevk concerns actually are. ObjectCreate was intend to be used as the primarily way to specify the need to create an ordinary object, not as a short cut for creating exotic objects. I think use it that way would only create more potential for misunderstanding. As I mentioned in whatwg/webidl#655 (comment) my intent was that other/future exotic object specifications should follow the general pattern used in 9.4. It isn' t clear to me what issues he does/would have with that pattern particularly if we cleanup the minor irregularities discussed here.

@annevk
Copy link
Member Author

annevk commented Feb 27, 2019

It comes down to #1453 (comment). Platform objects are all defined and created in the same way, but some have overridden internal methods. It would be nice if we could tack those on at the end and not have to know about them upfront.

See https://heycam.github.io/webidl/#internally-create-a-new-object-implementing-the-interface and whatwg/webidl#660 for the Location exotic object.

We could also add branching, probably even to the extent @jmdyck mentions above, but it's not particularly clear to me the distinction has much meaning as quickly skimming through the creation algorithm of an object will tell you the same thing.

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 27, 2019

ObjectCreate was intend to be used as the primarily way to specify the need to create an ordinary object, not as a short cut for creating exotic objects.

Sure, but if someone wants a shortcut for creating exotic objects, and it comes out looking exactly like ObjectCreate, is there a point to having both?

I think use it that way would only create more potential for misunderstanding.

I think we could lessen any misunderstanding by tweaking the prose for ObjectCreate, making it clear that although the object it returns is ordinary, some callers will then make it exotic by changing one or more of its internal methods.

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 27, 2019

@annevk:

We could also add branching, probably even to the extent @jmdyck mentions above

I didn't mention branching, so I don't know what you're talking about there.

@annevk
Copy link
Member Author

annevk commented Feb 27, 2019

@jmdyck I meant adding branching to the IDL algorithms to enable the kind of prose you proposed. (I.e., instead of overriding some internal methods at the end of creation, branch early on to decide between exotic and ordinary and allocate them in the way you suggest.) I'm not particularly convinced it's worthwhile, but it's all doable.

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 27, 2019

@annevk: You mean the syntax I gave at the bottom of this comment? (Note that I didn't "propose" it, I was just concretizing one of @allenwb's suggestions, for discussion purposes.) Yeah, that syntax wouldn't (easily) allow the abstraction/separation you're asking for, which I consider one of its "cons".

@annevk
Copy link
Member Author

annevk commented Feb 27, 2019

Aye.

@allenwb
Copy link
Member

allenwb commented Feb 27, 2019

@annevk

It comes down to #1453 (comment). Platform objects are all defined and created in the same way, but some have overridden internal methods. It would be nice if we could tack those on at the end and not have to know about them upfront.

It is certainly reasonable to factor out common behavior in your specifications. You could define an abstract operation for creating each unique kind of web platform object. If there is a common pattern (for example, common definitions of some of the essential internal methods) followed different kinds of web platform exotic objects you might define an appropriately parameterized abstract operation that could be used to create any one them. It may even be the case that there is only one kind of exotic object that is used for all web platform objects (recall that different kinds of exotic objects are distinguished by difference in their essential internal methods).

Your operations internally create a new object implementing the interface seems like such a definition and roughly comparable to the xxxCreate abstract operations in ES 9.4. Are you saying that there are other places in your specification that create slightly different platform exotic objects and that you are duplicating the steps of this operation? If so, I don't see any reason you couldn't factor the common steps out into a common operation that is shared by all of those usages.

I'm not exactly sure what you mean by "not have to know about them upfront". Do you mean when writing this specific pseudo-code? Or that you might want to start writing other similar definitions without knowing all the details? What exactly is the inconvenience?

@allenwb
Copy link
Member

allenwb commented Feb 27, 2019

Sure, but if someone wants a shortcut for creating exotic objects, and it comes out looking exactly like ObjectCreate, is there a point to having both?

It would not look the same, in particular, it at least would have a different name indicating a different purpose and a different informative explanation on its intended use within the specification.

The point of ObjectCreate; (and all of the 9.4 xxxCreate operations) is to provide a way for higher abstraction layer operations within the specification to clearly state they are creating an _ordinary object (or specific kind of exotic object). Overloading usage of ObjectCreate would muddy that clear usage intent.

I think we could lessen any misunderstanding by tweaking the prose for ObjectCreate, making it clear that although the object it returns is ordinary, some callers will then make it exotic by changing one or more of its internal methods.

I strongly disagree. Perhaps ObjectCreate should have been named OrdinaryObjectCreate and I would support renaming it. Perhaps, part of it might be factored out into a common definition that might be used by both OrdinaryObjectCreate and some of the xxxCreate exotic object creation operations. But the naming should clearing distinguish operations that are intended for internal use in the xxxCreate operations from the xxxCreate operations that are intended to allow higher abstraction layers of the specification to clearly state what their intent to create specific kinds of objects.

@allenwb
Copy link
Member

allenwb commented Feb 27, 2019

@annevk

Aye.

I still don't get you issue with the "declarative" style. Could you provide a snippet where I can see how that style would be inconvenient?

@zenparsing
Copy link
Member

zenparsing commented Feb 27, 2019

Perhaps ObjectCreate should have been named OrdinaryObjectCreate and I would support renaming it.

I was just about to suggest that same possibility. : )

If we are consistent about always using xxCreate operations when creating ordinary or exotic objects, then what do you think about removing the special "newly created XXX exotic object" language? Within the xxCreate operations, you'd always just say "newly created object" and then set all the internal methods/slots as appropriate.

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 27, 2019

@allenwb:

Perhaps ObjectCreate should have been named OrdinaryObjectCreate [...]. Perhaps, part of it might be factored out into a common definition that might be used by both OrdinaryObjectCreate and some of the xxxCreate exotic object creation operations.

Okay, so what would you call the factored-out operation?

@allenwb
Copy link
Member

allenwb commented Feb 27, 2019

what do you think about removing the special "newly created XXX exotic object" language? Within the xxCreate operations, you'd always just say "newly created object"

This seems to ignore the branding aspect of various xxxCreate operations. There are places in the spec. where it needs to explicitly check for various (but not all) specific kinds of exotic objects. We need to have a way to explicitly state which objects are an XXX exotic object. That is the purpose of XXX in the creation phrase. If we eliminate it then we need to add some other way to say, "BTW, this newly created object is identifiable as a XXX exotic object"

and then set all the internal methods/slots as appropriate.

But not all exotic objects necessarily have all the slots of an ordinary object or and may not have slots at all. The only thing required for an entity to be an object is a distinct identity and definitions for the essential internal methods

More in answer to the next @jmdyck message.

@allenwb
Copy link
Member

allenwb commented Feb 27, 2019

Okay, so what would you call the factored-out operation?

How about AllocateBasicObject.

Where a "basic object" is the foundation for defining ordinary objects and exotic objects that use most of the ordinary object essential internal method definitions.

It would essentially have the current definition of ObjectCreate but should probably explicitly state that its caller needs to explicit reset of any of its essential internal methods that differ from the definitions in clause 9.1.

I'm not sure exactly where in the spec this definition should live. I'm not sure it belongs in 9.1 and I'd be reluctant to renumber the subclauses of 9 to fit it in before it. Perhaps 7.3 would be a good place for it.

Using this new operation, the existing ObjectCreate would just delegate to it, and an operation like ArrayCreate its steps 5-9 would be replaced with something like:

  1. Let A be AllocateBasicObject(proto)
  • A is an array exotic object.
  1. Set A.[[DefineOwnProperty]] as specified in 9.4.2.1.

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 27, 2019

This seems to ignore the branding aspect of various xxxCreate operations. There are places in the spec. where it needs to explicitly check for various (but not all) specific kinds of exotic objects.

Yup.

We need to have a way to explicitly state which objects are an XXX exotic object. That is the purpose of XXX in the creation phrase.

That's what I thought too (see the quote in the original post). E.g., the wording "a newly created Array exotic object" results in an object with some essence that allows you to later identify it as an Array exotic object. And clearly, the wording "a newly created object" doesn't give you that essence. But now I'm thinking maybe we don't need it.

If we eliminate it then we need to add some other way to say, "BTW, this newly created object is identifiable as a XXX exotic object"

Instead, I was thinking that we could replace the last paragraph of 9.4.2 Array Exotic Objects with something like:

An Array exotic object is, by definition, any object whose [[DefineOwnProperty]] internal method is [conforms to] 9.4.2.1, and whose other essential internal methods are the ordinary defaults.

(Might have to say more, not sure.)

Then you can handle the test _x_ is an Array exotic object merely by examining the essential internal methods of _x_, not by relying on a 'brand'.

Then ArrayCreate could start with just "a newly created object" (or a call to AllocateBasicObject) and nevertheless construct an object that qualifies as an Array exotic object.

@allenwb
Copy link
Member

allenwb commented Feb 28, 2019

Instead, I was thinking that we could replace the last paragraph of 9.4.2 Array Exotic Objects with something like:

In my experience, readers who are searching for clarification tend to miss statements like this that are remote from actual algorithms. How about:

  1. Let A be AllocateBasicObject(proto)
  2. Set A.[[DefineOwnProperty]] as specified in 9.4.2.1.
  3. Assert: A is an Array exotic object.

@ljharb
Copy link
Member

ljharb commented Feb 28, 2019

"is an Array exotic object" seems pretty loosely defined in the prose in https://tc39.github.io/ecma262/#sec-array-exotic-objects - is there an abstract op-like algorithm that could be used instead of a prose assertion?

@domenic
Copy link
Member

domenic commented Feb 28, 2019

For anyone who might not be watching this repo, pull request over in #1460 has a concrete proposal. I think it is a nice improvement over the current spec, and look forward to folks' thoughts.

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 28, 2019

@allenwb:

"if A is an Array exotic object" it is equivalent to saying "if A was created by ArrayCreate"

@domenic:

(call this (1)) but there are at least two other meanings which are entirely plausible from the current spec:

@allenwb:

But the spec never does either of those other things so neither can be the meaning in the context of the current specification.

The spec doesn't do (1) either. (It never states that equivalence.)

This would probably be clearer if ObjetCreate was named OrdinaryObjectCreate.

I have no objection to that renaming, but I really don't think it would clarify the meaning of "if A is an Array exotic object".

The meta-discussion here is really about how does somebody know what they should write if they need to add a new kind of exotic object to ECMA-262 or a layered related specification.

That's one possible meta-discussion, but it's not what I'm talking about. An implementation can introduce a new kind of exotic object without changing ECMA-262 or another specification, in which case there isn't a question of how to author new spec-text. But there is the question of how a conforming implementation is required to behave with respect to that object.

@jmdyck
Copy link
Collaborator

jmdyck commented Feb 28, 2019

@ljharb:

Is it worth making a “function exotic object” category, to cover those, so ordinary objects would only be ordinary?

A category by that name already exists (with a different meaning from what you have in mind, I think).

@jmdyck
Copy link
Collaborator

jmdyck commented Mar 7, 2019

@allenwb:

Within ECMA-262, an object is an Array exotic object if and only if it is created using the ArrayCreate abstract operations. All places in the specification where an Array exotic object is created it is done via ArrayCreate.

The second sentence is true, but that doesn't allow you to conclude the first.

That is sufficient for expressing the requirements of the specification.

No, it isn't, because the spec expresses requirements on all objects, not just the ones that it defines.

That is, the criterion "it was created by ArrayCreate" is reasonably well defined for objects whose creation semantics are specified by the spec. But for other objects (novel kinds of exotic objects), what does it even mean? (How does an implementer decide whether any given exotic object is "created by ArrayCreate"?)

@allenwb
Copy link
Member

allenwb commented Mar 7, 2019

@jmdyck

The second sentence is true, but that doesn't allow you to conclude the first.

Then I suggest that the first sentence be placed within the definition of ArrayCreate

That is, the criterion "it was created by ArrayCreate" is reasonably well defined for objects whose creation semantics are specified by the spec. But for other objects (novel kinds of exotic objects),

ArrayCreate, and everything else in the the specification is a statement of requirements. This means that specifications that extend ECMA-262 must explicitly reference 262's ArrayCreate if they need to specify the instantiation of an object as an "Array exotic object" and hence be recognized as such by the parts of the ECMA-262 that test for "Array exotic object".

This is largely the whole point of having operations like ArrayCreate. Other novel kinds of exotic objects are not "Array exotic objets" and must not be recognized as such by conforming ECMA-262 implementations.

Practically speaking, that means that if you introduce some new kind of exotic object not created in conformance with ArrayCreate but for which Array.isArray should return true you will be modifying the specified behavior of Array.isArray. Whether or not such a modification is n conforming extension permitted by Clause 16 is probably a separate discussion.

what does it even mean? (How does an implementer decide whether any given exotic object is "created by ArrayCreate"?)

Implementations define their own mechanisms for fulfilling the requirements of ECMA-262 and other specifications. An implementation may have a single piece of code that implements ArrayCreate or many separately implement it multiple places. It may have some sort of explicit tag meaning this object was created in compliance with ArrayCreate or it may infer it some other way. None of this is relevant to the specification. How an implementation decides to designate and recognize objects "created by ArrayCreate" is its own design decision.

@jmdyck
Copy link
Collaborator

jmdyck commented Mar 8, 2019

Then I suggest that the first sentence [i.e., "Within ECMA-262, an object is an Array exotic object if and only if it is created using the ArrayCreate abstract operations."] be placed within the definition of ArrayCreate

I still don't think that wording is as clear as you think it is.

[...] specifications that extend ECMA-262 must explicitly reference 262's ArrayCreate if they need to specify the instantiation of an object as an "Array exotic object" and hence be recognized as such by the parts of the ECMA-262 that test for "Array exotic object".

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.

This is largely the whole point of having operations like ArrayCreate. Other novel kinds of exotic objects are not "Array exotic objets" and must not be recognized as such by conforming ECMA-262 implementations.

That last sentence is pretty much a tautology: objects other than Array exotic objects are not Array exotic objects. 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 (and there doesn't appear to be committee consensus).

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

(How does an implementer decide whether any given exotic object is "created by ArrayCreate"?)

Implementations define their own mechanisms for fulfilling the requirements of ECMA-262 and other specifications. An implementation may have a single piece of code that implements ArrayCreate or many separately implement it multiple places. It may have some sort of explicit tag meaning this object was created in compliance with ArrayCreate or it may infer it some other way. None of this is relevant to the specification.

Agreed, but you've misunderstood my question again. I wasn't asking how to implement something, I was asking what constitutes conformant behavior.

@allenwb
Copy link
Member

allenwb commented Mar 8, 2019

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
Copy link
Collaborator

jmdyck commented Mar 8, 2019

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
Copy link
Member

domenic commented Mar 8, 2019

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
Copy link
Member

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
Copy link
Member

domenic commented Mar 8, 2019

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
Copy link
Collaborator

jmdyck commented Mar 8, 2019

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
Copy link
Collaborator

jmdyck commented Mar 8, 2019

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
Copy link
Member

allenwb commented Mar 8, 2019

@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: <list all the post conditions that are currently established by ArrayCreate>". 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
Copy link
Member

domenic commented Mar 8, 2019

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
Copy link
Collaborator

jmdyck commented Mar 9, 2019

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.

domenic added a commit to domenic/ecma262 that referenced this issue Apr 26, 2019
Closes tc39#1453.

### Main changes

* Defines ordinary objects as having the ordinary internal methods, and exotic objects as not having the ordinary internal methods.

* Introduces AllocateBasicObject, which now is the only source of object creation, centralizing the undefined phrase "a newly created object" or "newly created X exotic object" into one location.

* Introduces explicit definitions for every type of exotic object in terms of how they override the internal methods. This makes phrases like "x is an Array exotic object" well-defined.

* Renames ObjectCreate to OrdinaryObjectCreate, and clarifies how it should be used.

### Related changes to object creation

* Fixes immutable prototype exotic objects to not inaccurately state that they always have default internal methods besides [[SetPrototypeOf]]; this is not the case for web platform objects, for example. This involved then expanding the definition of %ObjectPrototype% a bit to be more explicit about its internal slots and methods.

* Improves missing or contradictory internal slot installation, e.g. the introduction for function objects said they had "the same internal slots" as other ordinary objects, but FunctionAllocate installed a list of slots that was missing [[Prototype]] and [[Extensible]].

* Deduplicates setting [[Extensible]] to its default true value.

* Clarifies with a note that CreateUnmappedArgumentsObject does not create an exotic object, despite being in the "Arguments Exotic Objects" clause.

* Slightly reduces the coupling between IntegerIndexedObjectCreate and CreateTypedArray by changing how arguments are passed.* Uses the phrase "bound function exotic object" uniformly instead of sometimes "bound function" or "bound function object".

### Drive-by fixes

* Gives "array index" a definition instead of just making it italicized.
syg pushed a commit to domenic/ecma262 that referenced this issue Nov 20, 2019
Closes tc39#1453.

* Defines ordinary objects as having the ordinary internal methods, and exotic objects as not having the ordinary internal methods.

* Introduces AllocateBasicObject, which now is the only source of object creation, centralizing the undefined phrase "a newly created object" or "newly created X exotic object" into one location.

* Introduces explicit definitions for every type of exotic object in terms of how they override the internal methods. This makes phrases like "x is an Array exotic object" well-defined.

* Renames ObjectCreate to OrdinaryObjectCreate, and clarifies how it should be used.

* Fixes immutable prototype exotic objects to not inaccurately state that they always have default internal methods besides [[SetPrototypeOf]]; this is not the case for web platform objects, for example. This involved then expanding the definition of %ObjectPrototype% a bit to be more explicit about its internal slots and methods.

* Improves missing or contradictory internal slot installation, e.g. the introduction for function objects said they had "the same internal slots" as other ordinary objects, but FunctionAllocate installed a list of slots that was missing [[Prototype]] and [[Extensible]].

* Deduplicates setting [[Extensible]] to its default true value.

* Clarifies with a note that CreateUnmappedArgumentsObject does not create an exotic object, despite being in the "Arguments Exotic Objects" clause.

* Slightly reduces the coupling between IntegerIndexedObjectCreate and CreateTypedArray by changing how arguments are passed.* Uses the phrase "bound function exotic object" uniformly instead of sometimes "bound function" or "bound function object".

* Gives "array index" a definition instead of just making it italicized.
syg pushed a commit to domenic/ecma262 that referenced this issue Dec 18, 2019
Closes tc39#1453.

* Defines ordinary objects as having the ordinary internal methods, and exotic objects as not having the ordinary internal methods.

* Introduces AllocateBasicObject, which now is the only source of object creation, centralizing the undefined phrase "a newly created object" or "newly created X exotic object" into one location.

* Introduces explicit definitions for every type of exotic object in terms of how they override the internal methods. This makes phrases like "x is an Array exotic object" well-defined.

* Renames ObjectCreate to OrdinaryObjectCreate, and clarifies how it should be used.

* Fixes immutable prototype exotic objects to not inaccurately state that they always have default internal methods besides [[SetPrototypeOf]]; this is not the case for web platform objects, for example. This involved then expanding the definition of %ObjectPrototype% a bit to be more explicit about its internal slots and methods.

* Improves missing or contradictory internal slot installation, e.g. the introduction for function objects said they had "the same internal slots" as other ordinary objects, but FunctionAllocate installed a list of slots that was missing [[Prototype]] and [[Extensible]].

* Deduplicates setting [[Extensible]] to its default true value.

* Clarifies with a note that CreateUnmappedArgumentsObject does not create an exotic object, despite being in the "Arguments Exotic Objects" clause.

* Slightly reduces the coupling between IntegerIndexedObjectCreate and CreateTypedArray by changing how arguments are passed.* Uses the phrase "bound function exotic object" uniformly instead of sometimes "bound function" or "bound function object".

* Gives "array index" a definition instead of just making it italicized.
ljharb pushed a commit to domenic/ecma262 that referenced this issue Jan 2, 2020
tc39#1460)

Closes tc39#1453.

* Defines ordinary objects as having the ordinary internal methods, and exotic objects as not having the ordinary internal methods.

* Introduces AllocateBasicObject, which now is the only source of object creation, centralizing the undefined phrase "a newly created object" or "newly created X exotic object" into one location.

* Introduces explicit definitions for every type of exotic object in terms of how they override the internal methods. This makes phrases like "x is an Array exotic object" well-defined.

* Renames ObjectCreate to OrdinaryObjectCreate, and clarifies how it should be used.

* Fixes immutable prototype exotic objects to not inaccurately state that they always have default internal methods besides [[SetPrototypeOf]]; this is not the case for web platform objects, for example. This involved then expanding the definition of %ObjectPrototype% a bit to be more explicit about its internal slots and methods.

* Improves missing or contradictory internal slot installation, e.g. the introduction for function objects said they had "the same internal slots" as other ordinary objects, but FunctionAllocate installed a list of slots that was missing [[Prototype]] and [[Extensible]].

* Deduplicates setting [[Extensible]] to its default true value.

* Clarifies with a note that CreateUnmappedArgumentsObject does not create an exotic object, despite being in the "Arguments Exotic Objects" clause.

* Slightly reduces the coupling between IntegerIndexedObjectCreate and CreateTypedArray by changing how arguments are passed.* Uses the phrase "bound function exotic object" uniformly instead of sometimes "bound function" or "bound function object".

* Gives "array index" a definition instead of just making it italicized.

* Add requirement on caller of AllocateBasicObject per Allen's review

* Remove proto argument, and assert more things on callers

* AllocateBasicObject -> MakeBasicObject

Co-authored-by: Domenic Denicola <d@domenic.me>
Co-authored-by: Shu-yu Guo <syg@chromium.org>
ljharb pushed a commit to domenic/ecma262 that referenced this issue Jan 5, 2020
tc39#1460)

Closes tc39#1453.

* Defines ordinary objects as having the ordinary internal methods, and exotic objects as not having the ordinary internal methods.

* Introduces AllocateBasicObject, which now is the only source of object creation, centralizing the undefined phrase "a newly created object" or "newly created X exotic object" into one location.

* Introduces explicit definitions for every type of exotic object in terms of how they override the internal methods. This makes phrases like "x is an Array exotic object" well-defined.

* Renames ObjectCreate to OrdinaryObjectCreate, and clarifies how it should be used.

* Fixes immutable prototype exotic objects to not inaccurately state that they always have default internal methods besides [[SetPrototypeOf]]; this is not the case for web platform objects, for example. This involved then expanding the definition of %ObjectPrototype% a bit to be more explicit about its internal slots and methods.

* Improves missing or contradictory internal slot installation, e.g. the introduction for function objects said they had "the same internal slots" as other ordinary objects, but FunctionAllocate installed a list of slots that was missing [[Prototype]] and [[Extensible]].

* Deduplicates setting [[Extensible]] to its default true value.

* Clarifies with a note that CreateUnmappedArgumentsObject does not create an exotic object, despite being in the "Arguments Exotic Objects" clause.

* Slightly reduces the coupling between IntegerIndexedObjectCreate and CreateTypedArray by changing how arguments are passed.* Uses the phrase "bound function exotic object" uniformly instead of sometimes "bound function" or "bound function object".

* Gives "array index" a definition instead of just making it italicized.

* Add requirement on caller of AllocateBasicObject per Allen's review

* Remove proto argument, and assert more things on callers

* AllocateBasicObject -> MakeBasicObject

Co-authored-by: Domenic Denicola <d@domenic.me>
Co-authored-by: Shu-yu Guo <syg@chromium.org>
syg pushed a commit to domenic/ecma262 that referenced this issue Jan 8, 2020
Closes tc39#1453.

* Defines ordinary objects as having the ordinary internal methods, and exotic objects as not having the ordinary internal methods.

* Introduces AllocateBasicObject, which now is the only source of object creation, centralizing the undefined phrase "a newly created object" or "newly created X exotic object" into one location.

* Introduces explicit definitions for every type of exotic object in terms of how they override the internal methods. This makes phrases like "x is an Array exotic object" well-defined.

* Renames ObjectCreate to OrdinaryObjectCreate, and clarifies how it should be used.

* Fixes immutable prototype exotic objects to not inaccurately state that they always have default internal methods besides [[SetPrototypeOf]]; this is not the case for web platform objects, for example. This involved then expanding the definition of %ObjectPrototype% a bit to be more explicit about its internal slots and methods.

* Improves missing or contradictory internal slot installation, e.g. the introduction for function objects said they had "the same internal slots" as other ordinary objects, but FunctionAllocate installed a list of slots that was missing [[Prototype]] and [[Extensible]].

* Deduplicates setting [[Extensible]] to its default true value.

* Clarifies with a note that CreateUnmappedArgumentsObject does not create an exotic object, despite being in the "Arguments Exotic Objects" clause.

* Slightly reduces the coupling between IntegerIndexedObjectCreate and CreateTypedArray by changing how arguments are passed.* Uses the phrase "bound function exotic object" uniformly instead of sometimes "bound function" or "bound function object".

* Gives "array index" a definition instead of just making it italicized.
syg pushed a commit to domenic/ecma262 that referenced this issue Jan 30, 2020
Closes tc39#1453.

* Defines ordinary objects as having the ordinary internal methods, and exotic objects as not having the ordinary internal methods.

* Introduces MakeBasicObject, which now is the only source of object creation, centralizing the undefined phrase "a newly created object" or "newly created X exotic object" into one location.

* Introduces explicit definitions for every type of exotic object in terms of how they override the internal methods. This makes phrases like "x is an Array exotic object" well-defined.

* Renames ObjectCreate to OrdinaryObjectCreate, and clarifies how it should be used.

* Fixes immutable prototype exotic objects to not inaccurately state that they always have default internal methods besides [[SetPrototypeOf]]; this is not the case for web platform objects, for example. This involved then expanding the definition of %ObjectPrototype% a bit to be more explicit about its internal slots and methods.

* Improves missing or contradictory internal slot installation, e.g. the introduction for function objects said they had "the same internal slots" as other ordinary objects, but FunctionAllocate installed a list of slots that was missing [[Prototype]] and [[Extensible]].

* Deduplicates setting [[Extensible]] to its default true value.

* Clarifies with a note that CreateUnmappedArgumentsObject does not create an exotic object, despite being in the "Arguments Exotic Objects" clause.

* Slightly reduces the coupling between IntegerIndexedObjectCreate and CreateTypedArray by changing how arguments are passed.* Uses the phrase "bound function exotic object" uniformly instead of sometimes "bound function" or "bound function object".
syg pushed a commit to domenic/ecma262 that referenced this issue Feb 2, 2020
Closes tc39#1453.

* Defines ordinary objects as having the ordinary internal methods, and exotic objects as not having the ordinary internal methods.

* Introduces MakeBasicObject, which now is the only source of object creation, centralizing the undefined phrase "a newly created object" or "newly created X exotic object" into one location.

* Introduces explicit definitions for every type of exotic object in terms of how they override the internal methods. This makes phrases like "x is an Array exotic object" well-defined.

* Renames ObjectCreate to OrdinaryObjectCreate, and clarifies how it should be used.

* Fixes immutable prototype exotic objects to not inaccurately state that they always have default internal methods besides [[SetPrototypeOf]]; this is not the case for web platform objects, for example. This involved then expanding the definition of %ObjectPrototype% a bit to be more explicit about its internal slots and methods.

* Improves missing or contradictory internal slot installation, e.g. the introduction for function objects said they had "the same internal slots" as other ordinary objects, but FunctionAllocate installed a list of slots that was missing [[Prototype]] and [[Extensible]].

* Deduplicates setting [[Extensible]] to its default true value.

* Clarifies with a note that CreateUnmappedArgumentsObject does not create an exotic object, despite being in the "Arguments Exotic Objects" clause.

* Slightly reduces the coupling between IntegerIndexedObjectCreate and CreateTypedArray by changing how arguments are passed.* Uses the phrase "bound function exotic object" uniformly instead of sometimes "bound function" or "bound function object".
@ljharb ljharb closed this as completed in e3707ac Feb 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants