-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Editorial: clarify ordinary and exotic object definitions and creation #1460
Conversation
spec.html
Outdated
|
||
<emu-alg> | ||
1. Let _obj_ be a newly created object with an internal slot for each name in _internalSlotsList_. | ||
1. Set _obj_'s essential internal methods to the default ordinary object definitions specified in <emu-xref href="#sec-ordinary-object-internal-methods-and-internal-slots"></emu-xref>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should explicitly say that after returning from this operation only the direct caller of
AllocateBaseObject may modify the definition of the returned object's essential internal methods.
Basely, we don't what to imply that it is possible for internal method definitions to change at arbitrary times during the entire lifetime of an object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thank you! Also added more asserts on callers relating to the interaction with non-overridden methods.
spec.html
Outdated
</emu-alg> | ||
|
||
<emu-note> | ||
<p>Apart from being slightly easier to call than AllocateBasicObject, using OrdinaryObjectCreate communicates the intention to create an ordinary object, and not an exotic one. Thus, it must not be called as part as of an algorithm that subsequently modifies the internal methods of the object in ways that would make the result non-ordinary. In such cases, use AllocateBasicObject. (Even if it means having to include [[Prototype]] and [[Extensible]] in the internal slots list manually.)</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that this somewhat covers the point I mentioned about mutating the set of essential internal methods. I think it is good to say it both places.
spec.html
Outdated
|
||
<p>Immutable prototype exotic objects have the same internal slots as ordinary objects. They are exotic only in the following internal methods. All other internal methods of immutable prototype exotic objects that are not explicitly defined below are instead defined as in <a href="#sec-ordinary-object-internal-methods-and-internal-slots">ordinary objects.</a></p> | ||
<emu-note> | ||
<p>Unlike other exotic objects, there is not a dedicated creation abstract operation provided for immutable prototype exotic objects. This is because they are only used by %ObjectPrototype% and by host environments, and in host environments, the relevant objects are potentially exotic in other ways and thus need their own dedicated creation operation.</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wondered whether we should add an ImmutablePrototypeObjCreate, but I'm ok with not having it for now since we would host object to define there on xxxCreate operations that incorporate this. Perhaps the sort of thing that should be explained in a FAQ on defining "host objects"
<p>Unlike other exotic objects, there is not a dedicated creation abstract operation provided for immutable prototype exotic objects. This is because they are only used by %ObjectPrototype% and by host environments, and in host environments, the relevant objects are potentially exotic in other ways and thus need their own dedicated creation operation.</p> | |
<p>Unlike other exotic objects, there is not a dedicated creation abstract operation provided for immutable prototype exotic objects. This is because they are only used by %ObjectPrototype% and by host environments, and in host environments, the relevant objects are potentially exotic in other ways and thus need their own dedicated creation operation.</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General looks good, but see a couple comments.
@domenic, in a comment in issue #1453:
It actually says that ECMAScript function objects are ordinary objects. ECMAScript function objects are a proper subset of the function objects specified by the ECMAScript spec. (See Issue #1273 for some discussion.)
I wouldn't say "overridden". They certainly do have a [[Call]] internal method, but it doesn't override some prior behaviour.
How it seems to work in 6.1.7.2 Object Internal Methods and Internal Slots is that if an object has a [[Call]] internal method, then [[Call]] is an essential internal method for that object. Similarly for [[Construct]] (although some of the wording around Table 6 implies that [[Construct]] is essential for all function objects, which of course it isn't). So you could say that an ordinary object "has the default behaviour for all of its essential internal methods" and that will include [[Call]] and [[Construct]] as appropriate. (Mind you, the spec doesn't currently say it that way.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would definitely like to see the OrdinaryObjectCreate
rename. I think that's a clear, easy win. I'm a bit less sure of the other changes and will need more time to think about them.
Would you be opposed to submitting that change as it's own PR?
I'm sorry to say I would be. Personally, I think the rename is sad, because of all the churn it will cause. But I would be willing to take that hit if we also actually clarified the attendant issues. For example, adding normative requirements that OrdinaryObjectCreate should only use be used to create ordinary objects, and providing clear instructions for how to create exotic objects. Just renaming by itself doesn't improve clarity, and causes lots of specs to need updates. Indeed, I think it would reduce clarity, since now we'd have something named OrdinaryObjectCreate which does the exact same three steps as many exotic object creation routines also include. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@domenic Totally understand, and thanks for this PR. After reading through, I like everything here, except perhaps for a nit over the name "AllocateBasicObject". As a reader, I'm not really sure what "basic" implies, since it's not used elsewhere. Do you have any thoughts on the naming? Would "AllocateObject" be sufficient?
How about AllocateObjectStructure or AllocateObjectFoundation or AllocateObjectCore or AllocateCommonObjectCore or ... Or perhaps substitute "Make" for "Allocate" in any of the above. the point is that this operation isn't trying to introduce a new kind of entity into the spec. It is just there to factor common behavior out of the xxxObjectCreate operations |
I am happy with any of those names, and if the editor(s) can come to a final decision, I will change it. Just don't want to do it twice :). |
That's what I was thinking too. Maybe replace "basic" with "default" (e.g. |
I think I prefer "Make" over "Allocate" because "Allocate" sounds too concrete and suggestive of an implementation detail. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I'll have to think about that. |
I don't find this discussion very productive. This is the usual structural typing that JS is at home with, except operating on spec metafunctions -- the current lack of clarity around exotic objects is creating friction for downstream specs, and AFAICT no downstream spec considers exotic objects to be some behavioral thing and not a branding thing. Absent concrete verbiage suggestions, this PR is ready to merge. |
I didn't actually say that, you did. You have to look at the context to unravel what I meant. What I actually said was: "Yes, that is essentially my position, particularly if by 'behaviorally' you also mean 'observably' . That was referring to the following statement from you:
So, I was actually suggesting replacing the work "behaviorally" with "observability". And this was in the context of me arguing that all instantiations of exotic objects within the specification should be specified as an invocation of a xxxCreate abstract operations such as ArrayCreate. The reason for use of the xxxCreate operations is to be clearer in the specification when the instantiation of a specific kind of exotic object was required. The observable characteristics of such objects include the "meta-branding" of the sort being discussed in this thread.
I think @syg covered this. The spec. isn't an engine or a reference implementation. It doesn't do anything other than establish requirements of conforming implementation. If an implementation wants to define language/library extensions that create objects that are "Array exotic objects" and hence will be recognized as such in all the places that specification requires such recognition then the implementation must ensure that those objects are observability (to the implementation) no different (including meta-branding) from objects created by the implementation in satisfaction of ArrayCreate's requirements. |
@allenwb Thanks for the clarification! I misunderstood your position earlier and retract my statement above that you had also wanted observational equivalence. |
Okay, I think the simplest and most direct way to address this is to rewind to here and change the suggested paragraph to something like this:
(That wording suggests that it's implementation-dependent whether it does or not -- do we want to require that such objects do not satisfy such conditions?) |
Yes, for the same reason as the vtable-as-brand reasoning. If the implementation provides a behaviorally equivalent object that has a different vtable that happens to be behaviorally the same, it should not be recognized as one of the in-spec branded exotic objects. As for your paragraph, it sounds mostly fine to me though a bit verbose. I'll try to cut it down a bit. |
Okay, that's an easy tweak to my suggestion. |
There could also be a Note to the effect that this implies vtable-as-brand semantics, but I'm not sure how one would word it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments relating to lines that don't appear in the diff:
-
line 9384: Need para: "An object is an integer-indexed exotic object if ..."
-
line 9609: Need para: "An object is a module namespace exotic object if ..."
-
line 18114: "ObjectCreate" -> "OrdinaryObjectCreate". (I warned about this here and @ljharb took care of it in 492886d, but it must have regressed in a subsequent rebase.)
-
line 26404: Change "bound function" to "bound function exotic object" as described in the main commit message?
Comments relating to multiple lines:
-
Occurrences of "other internal methods" should probably be "other essential internal methods".
-
Referring to specific versions/variants of the essential internal methods as "implementations" conflicts with the usage in the sense of an ECMAScript implementation or a conforming implementation. Instead, refer to them as "definitions"? "algorithms"?
E.g.: "... and its other essential internal methods use the definitions found in [9.1]"
Comments relating to the main commit message:
-
Change "AllocateBasicObject" to "MakeBasicObject".
-
Drop the line about giving "array index" a definition.
spec.html
Outdated
<p>Bound function objects do not have the internal slots of ECMAScript function objects defined in <emu-xref href="#table-27"></emu-xref>. Instead they have the internal slots defined in <emu-xref href="#table-28"></emu-xref>.</p> | ||
<p>A bound function is an exotic object that wraps another function object. A bound function is callable (it has a [[Call]] internal method and may have a [[Construct]] internal method). Calling a bound function generally results in a call of its wrapped function.</p> | ||
|
||
<p>An object is a <dfn id="bound-function-exotic-object">bound function exotic object</dfn> if its [[Call]] and [[Construct]] internal methods use the following implementations, and its other internal methods use the implementations found in <emu-xref href="#sec-ordinary-object-internal-methods-and-internal-slots"></emu-xref>. These methods are installed in BoundFunctionCreate.</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<p>An object is a <dfn id="bound-function-exotic-object">bound function exotic object</dfn> if its [[Call]] and [[Construct]] internal methods use the following implementations, and its other internal methods use the implementations found in <emu-xref href="#sec-ordinary-object-internal-methods-and-internal-slots"></emu-xref>. These methods are installed in BoundFunctionCreate.</p> | |
<p>An object is a <dfn id="bound-function-exotic-object">bound function exotic object</dfn> if its [[Call]] and (if applicable) [[Construct]] internal methods use the following implementations, and its other internal methods use the implementations found in <emu-xref href="#sec-ordinary-object-internal-methods-and-internal-slots"></emu-xref>. These methods are installed in BoundFunctionCreate.</p> |
(since it doesn't necessarily have a [[Construct]]
internal method)
spec.html
Outdated
<p>An <dfn id="ordinary-object">ordinary object</dfn> is an object that satisfy all of the following criteria:</p> | ||
<ul> | ||
<li> | ||
Its internal methods are those defined in <emu-xref href="#sec-ordinary-object-internal-methods-and-internal-slots"></emu-xref>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Sorry, my suggested wording wasn't quite right.) Saying that its internal methods are those defined in 9.1 means that it doesn't have [[Call]] and [[Construct]], because those aren't defined in 9.1. So it would be better to say something like:
For the internal methods listed in [Table 6], it uses the definitions in [9.1].
Rebased and squashed to fix up commit message. |
</emu-alg> | ||
|
||
<emu-note> | ||
<p>Within this specification, exotic objects are created in abstract operations such as ArrayCreate and BoundFunctionCreate by first calling MakeBasicObject to obtain a basic, foundational object, and then overriding some or all of that object's internal methods. In order to encapsulate exotic object creation, the object's essential internal methods are never modified outside those operations.</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm.
MakeConstructor
is passed a function object and sets its [[Construct]]
internal method, and OrdinaryFunctionCreate
obtains a function object by calling OrdinaryObjectCreate
and sets its [[Call]]
internal method.
I guess those technically are not creating exotic objects, so this is not technically wrong, but I find it misleading (which is to say, it misled me) in light of the above. I believe those are the only two places where the internal slots of an object are manipulated by a method other than the one which invoked MakeBasicObject
. Perhaps (as a followup) we can refactor things so that those two cases go away, so that this note would apply to all objects (and we could get rid of the "Thus, it is not called by any algorithm that subsequently modifies the internal methods of the object in ways that would make the result non-ordinary." part of the note on OrdinaryObjectCreate
below, since it would then be redundant).
I'm not asking for changes right now, just noting I found this misleading.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess those technically are not creating exotic objects
Right. For OrdinaryFunctionCreate
, it's clear from the name. For MakeConstructor
, the first step asserts that F is an ECMAScript (i.e., ordinary) function object.
but I find it misleading (which is to say, it misled me)
What were you misled to think/expect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What were you misled to think/expect?
I assumed that the essential internal methods of any object would never be manipulated except by the method which called MakeBasicObject
. That's not actually true, and is not quite what this line says, but (unless I've missed some other cases) it is very close to being true. And it's what I expected this to say, so on first reading I missed that it says something slightly different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A followup PR sounds good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bakkot: Rather than refactoring to make those two cases go away, an easier alternative (or interim aid) would be to identify them in another Note after OrdinaryObjectCreate (and/or possibly modify the existing one).
</emu-alg> | ||
|
||
<emu-note> | ||
<p>Although OrdinaryObjectCreate does little more than call MakeBasicObject, its use communicates the intention to create an ordinary object, and not an exotic one. Thus, it is not called by any algorithm that subsequently modifies the internal methods of the object in ways that would make the result non-ordinary. Operations that create exotic objects invoke MakeBasicObject directly.</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Is not called" implicitly makes a claim about other specifications. I think this should either be "is not called within this specification", or "should not be called".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually when the spec talks about algorithms, it implicitly means algorithms defined in the spec. But sometimes it makes that explicit, and I agree it's worthwhile to do so here. My suggestion would be to change "Thus," to "Thus, within this specification,".
(The problem with "should not" is that it sounds like a requirement on implementations, which it isn't.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM other than relatively minor comments below. I agree with the "internal methods have meta-identity" approach.
Addressed @bakkot's review and rebased. |
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 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". Co-authored-by: Domenic Denicola <d@domenic.me> Co-authored-by: Shu-yu Guo <syg@chromium.org>
06db647
to
e3707ac
Compare
concerns appear to be addressed; any changes can be done in a followup
These replaced ObjectCreate and the undefined phrases around "newly created ECMAScript objects" in tc39/ecma262#1460.
Closes #1453. Closes #1437.
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.
Drive-by fixes
This will involve changes to both HTML and Web IDL, mostly the renaming of ObjectCreate, but possibly others.
One thing I am waffling on is whether AllocateBasicObject should take a proto parameter at all. I am thinking probably it is best to remove. Since you have to pass [[Prototype]] anyway, AllocateBasicObject(« [[x]], [[Prototype]] », proto) is not much worse than a two steps where the second explicitly sets [[Prototype]], and it makes AllocateBasicObject more basic and less coupled.
Edit: I flipped it to remove the proto parameter.
Note that technically "a newly created TypeError object" and its ilk are still ill-defined after this change, but that's pretty minor.
I'll also note that I didn't touch the built-in function objects section, which is kind of a mess of underdefined and confusing things as witnessed by many previous GitHub and es-discuss threads.
Editorial thoughts especially welcome; I think there were some choices e.g. about referencing tables vs. listing slots explicitly, or how to write the intro paragraphs for each type of exotic object, which could be revisited or better.