-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Editorial: support built-in async functions #2942
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
base: main
Are you sure you want to change the base?
Conversation
spec.html
Outdated
@@ -13866,7 +13866,7 @@ <h1>Built-in Function Objects</h1> | |||
<p>The behaviour specified for each built-in function via algorithm steps or other means is the specification of the function body behaviour for both [[Call]] and [[Construct]] invocations of the function. However, [[Construct]] invocation is not supported by all built-in functions. For each built-in function, when invoked with [[Call]], the [[Call]] _thisArgument_ provides the *this* value, the [[Call]] _argumentsList_ provides the named parameters, and the NewTarget value is *undefined*. When invoked with [[Construct]], the *this* value is uninitialized, the [[Construct]] _argumentsList_ provides the named parameters, and the [[Construct]] _newTarget_ parameter provides the NewTarget value. If the built-in function is implemented as an ECMAScript function object then this specified behaviour must be implemented by the ECMAScript code that is the body of the function. Built-in functions that are ECMAScript function objects must be strict functions. If a built-in constructor has any [[Call]] behaviour other than throwing a *TypeError* exception, an ECMAScript implementation of the function must be done in a manner that does not cause the function's [[IsClassConstructor]] internal slot to have the value *true*.</p> | |||
<p>Built-in function objects that are not identified as constructors do not implement the [[Construct]] internal method unless otherwise specified in the description of a particular function. When a built-in constructor is called as part of a `new` expression the _argumentsList_ parameter of the invoked [[Construct]] internal method provides the values for the built-in constructor's named parameters.</p> | |||
<p>Built-in functions that are not constructors do not have a *"prototype"* property unless otherwise specified in the description of a particular function.</p> | |||
<p>If a built-in function object is not implemented as an ECMAScript function it must provide [[Call]] and [[Construct]] internal methods that conform to the following definitions:</p> | |||
<p>Unless otherwise specified, a built-in function object must provide [[Call]] and [[Construct]] internal methods that conform to the following definitions:</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.
This says that a built-in function is exotic (and synchronous) unless otherwise specified. So then a built-in would be ordinary (i.e., an ECMAScript function object) only if specified to be so. But, of course, none of them are, because the spec explicitly says that built-ins can be ordinary or exotic.
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.
@jmdyck I don't understand this comment. I think I'm missing some assumption. Can you rephrase?
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.
-
10.3 "Built-in Function Objects" says that built-in functions may be implemented as either ECMAScript function objects or as exotic function objects.
-
10.2 "ECMAScript Function Objects" says that an ECMAScript function object has the
[[Call]]
internal method defined in 10.2.1. -
10.3 "Built-in Function Objects" says (in the status quo) that a built-in function that isn't implemented as an ECMAScript function object must have the
[[Call]]
internal method defined in 10.3.1.
So, in the status quo, a built-in's [[Call]]
can be either 10.2.1 or 10.3.1, and the spec leaves the choice to the implementation.
-
This PR replaces the third point with a sentence that says a built-in function object must use 10.3.1 "unless otherwise specified".
-
But for all of the existing built-in functions, the spec never specifies whether to use 10.3.1 or 10.2.1 (because it's leaving that choice to the implementation), so it is never "otherwise specified", so the "unless otherwise specified" clause never holds.
So this new sentence implies that all existing built-in functions must use 10.3.1.
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.
(And I know that, at some point, the idea was floated to get rid of the 10.2.1 possibility for built-ins, but that's clearly not what this PR is trying to accomplish. That would presumably be a separate PR.)
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.
This is not an observable difference. I am okay with it. Besides, we wanted to eliminate all the implications (and explicit statements) that "built-ins can be ECMAScript code, too" because it doesn't matter.
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.
Resolution from editor call is to change the definition of ordinary object to explicitly allow these [[Call]]
and [[Construct]]
methods so they're all considered ordinary for now. We'll do this in a separate PR.
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.
Resolved in #2969.
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.
#2969 expands the definition of "ordinary function" to include all built-in functions (however implemented). But I don't think that resolves this, because my concern doesn't hinge on the definition of "ordinary function". If you look at my response to bakkot above, it all still holds in a post-2969 spec (except that "exotic" in the first point now becomes "non-ECMAScript").
(Yes, it was during discussion of this concern that the editors call decided to expand the definition of "ordinary", but maybe that was just to deal with the discovery that built-ins could be exotic?)
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 PR #3007 would be a basis for resolving my concern.
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.
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 % question and nits
spec.html
Outdated
@@ -13938,7 +13938,10 @@ <h1> | |||
1. If _prototype_ is not present, set _prototype_ to _realm_.[[Intrinsics]].[[%Function.prototype%]]. | |||
1. Let _internalSlotsList_ be a List containing the names of all the internal slots that <emu-xref href="#sec-built-in-function-objects"></emu-xref> requires for the built-in function object that is about to be created. | |||
1. Append to _internalSlotsList_ the elements of _additionalInternalSlotsList_. | |||
1. Let _func_ be a new built-in function object that, when called, performs the action described by _behaviour_ using the provided arguments as the values of the corresponding parameters specified by _behaviour_. The new function object has internal slots whose names are the elements of _internalSlotsList_, and an [[InitialName]] internal slot. | |||
1. If _behaviour_ is described as async, then |
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's the verbiage for describing an async AC? "Let c be a new async Abstract Closure"? Or "Async Abstract Closure" with capital A?
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 say "async Abstract Closure", I think; the idea is what we will say "async function" or "async method" for the built-in function case (as in proposal-iterator-helpers).
We don't actually have any async ACs at the moment so it's not established yet.
This comment was marked as resolved.
This comment was marked as resolved.
FIXME: The check in builtin.js for Object.getPrototypeOf(Array.fromAsync) === Function.prototype might be wrong, depending on tc39/ecma262#2942 (comment)
Per plenary, built-in async functions will continue to have Function.prototype as their prototype. |
Roughly speaking, the status quo says that a function is ordinary if its [[Call]] conforms to either 10.2.1 or 10.3.1. This PR defines a distinct [[Call]] for built-in async function objects, which implies that these objects are not ordinary. Is that intentional, or do you need to tweak the definition of ordinary to include them? |
Recording my thoughts here for the record. While being able to specify built-ins as async functions in spec text significantly aids spec authors and many readers, I've found that it simultaneously really hinders understanding by implementers that work in a non-self hosting implementation, like V8. For non-self hosting implementations, the implementer would need to do a manual transform to some kind of state machine. This manual transformation process increases the likelihood of bugs and subtle non-interop (like getting the number of Promise ticks wrong or something). There are possible paths forward. The long-term solution is for V8 to be build something so that async functions can be written as straightline code as well. The spec can also include the transformed version and offer an alternate formulation of async builtins. |
Once tc39/proposal-array-from-async#50’s reviews are finished and it is merged, Array.fromAsync will be ready to make a pull request against this pull request for formal Stage 3 reviews, as per tc39/proposal-array-from-async#14 (comment). [Edit: Now in #3581.] However, this pull request now seems to have merge conflicts with the main ecma262 branch. Should I write the new Array.fromAsync pull request against this pull request as it is now, or should I wait until this pull request’s conflicts are resolved? |
11bc7a7
to
a545cb8
Compare
@js-choi Rebased. |
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.
This shouldn’t be present, as the presence/absence of [[Construct]]
is used to differentiate constructors from non‑constructors and it’s observable, and all existing and future async‑like built‑in functions lack 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.
I think it's fairly clear that "built-in async functions" are a subset of "built-in functions". However, there are a few things that make it less clear:
-
CreateBuiltinFunction
has a dichotomy between creating "a new built-in async function object" vs "a new built-in function object". The latter could instead be "a new built-in sync function object" (or whatever you want to call it, I don't think there's a precedent). -
10.3.1 [[Call]]
and10.3.2 [[Construct]]
say they are for "a built-in function object F". Similarly,10.3.3 BuiltinCallOrConstruct
says that its parameter_F_
is "a built-in function object". But presumably these are only for synchronous built-ins. -
The new section
10.4 Built-in Async Function Objects
is a sibling of10.3 Built-in Function Objects
. You could instead make it a subsection. At the same time, it might be helpful to factor out the sync-specific parts of 10.3 and put them in a complementary subsection. That is, something like this:
10.3 Built-in Function Objects
10.3.1 CreateBuiltinFunction
10.3.2 Built-in Sync Function Objects
10.3.2.1 [[Call]]
10.3.2.2 [[Construct]]
10.3.2.3 BuiltinCallOrConstruct
10.3.3 Built-in Async Function Objects
10.3.3.1 [[Call]]
@@ -13991,8 +13991,8 @@ <h1>Built-in Function Objects</h1> | |||
<li>[[InitialName]], a String that is the initial name of the function. It is used by <emu-xref href="#sec-function.prototype.tostring"></emu-xref>.</li> | |||
</ul> | |||
<p>The initial value of a built-in function object's [[Prototype]] internal slot is %Function.prototype%, unless otherwise specified.</p> | |||
<p>A built-in function object must have a [[Call]] internal method that conforms to the definition in <emu-xref href="#sec-built-in-function-objects-call-thisargument-argumentslist"></emu-xref>.</p> | |||
<p>A built-in function object has a [[Construct]] internal method if and only if it is described as a “constructor”, or some algorithm in this specification explicitly sets its [[Construct]] internal method. Such a [[Construct]] internal method must conform to the definition in <emu-xref href="#sec-built-in-function-objects-construct-argumentslist-newtarget"></emu-xref>.</p> | |||
<p>A built-in function object must have a [[Call]] internal method that, unless otherwise specified, conforms to the definition in <emu-xref href="#sec-built-in-function-objects-call-thisargument-argumentslist"></emu-xref>.</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.
Rather than leaving it vaguely open-ended like this, wouldn't it help the reader more to say that [[Call]]
must conform to one of two explicit possibilities?
<p>A built-in function object must have a [[Call]] internal method that conforms to the definition in <emu-xref href="#sec-built-in-function-objects-call-thisargument-argumentslist"></emu-xref>.</p> | ||
<p>A built-in function object has a [[Construct]] internal method if and only if it is described as a “constructor”, or some algorithm in this specification explicitly sets its [[Construct]] internal method. Such a [[Construct]] internal method must conform to the definition in <emu-xref href="#sec-built-in-function-objects-construct-argumentslist-newtarget"></emu-xref>.</p> | ||
<p>A built-in function object must have a [[Call]] internal method that, unless otherwise specified, conforms to the definition in <emu-xref href="#sec-built-in-function-objects-call-thisargument-argumentslist"></emu-xref>.</p> | ||
<p>A built-in function object has a [[Construct]] internal method if and only if it is described as a “constructor”, or some algorithm in this specification explicitly sets its [[Construct]] internal method. Unless otherwise specified, such a [[Construct]] internal method must conform to the definition in <emu-xref href="#sec-built-in-function-objects-construct-argumentslist-newtarget"></emu-xref>.</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.
What is this change for? AFAICT, the current spec has no built-in that specifies an alternative [[Construct]]
, and this PR isn't adding one, so is this change for the benefit of a future PR? If so, maybe defer this change to that.
1. Push _calleeContext_ onto the execution context stack; _calleeContext_ is now the running execution context. | ||
1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). | ||
1. Let _resultsClosure_ be a new Abstract Closure with no parameters that captures _F_, _thisArgument_, and _argumentsList_ and performs the following steps when called: | ||
1. Return the Completion Record that is the result of evaluating _F_ in a manner that conforms to the specification of _F_. _thisArgument_ provides the *this* value, _argumentsList_ provides the named parameters, and the NewTarget value is *undefined*. |
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.
The corresponding step in BuiltinCallOrConstruct
wraps the result of evaluating
in an <emu-meta>
element. Does that apply here too?
<h1>Built-in Async Function Objects</h1> | ||
<p><dfn variants="built-in async function object,built-in async function objects">Built-in async function objects</dfn> are built-in function objects that provide a [[Call]] internal method that conforms to the following definition:</p> | ||
|
||
<emu-clause id="sec-built-in-async-function-objects-call" type="internal method"> |
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.
Presumably you will need to add this to the possible definitions of [[Call]]
for an ordinary object as defined in 6.1.7.2.
…s arg to items The behavior of Array.fromAsync must be marked as “async” in order to activate the special “built-in async function” mechanisms in tc39#2942.
This adds support for built-in async functions, as needed by the async iterator helpers and
Array.fromAsync
proposals.