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

Editorial: what is a built-in object? #2608

Open
bergus opened this issue Jan 3, 2022 · 18 comments
Open

Editorial: what is a built-in object? #2608

bergus opened this issue Jan 3, 2022 · 18 comments

Comments

@bergus
Copy link

bergus commented Jan 3, 2022

While writing this StackOverflow answer, I was not quite certain about the definition of built-in objects:

object specified and supplied by an ECMAScript implementation

NOTE: Standard built-in objects are defined in this specification. An ECMAScript implementation may specify and supply additional kinds of built-in objects. A built-in constructor is a built-in object that is also a constructor.

I've tried to dichotomise them with "user-defined objects", where the built-ins are created by the implementation at startup, providing the environment that user code can interact with, and the user-defined ones are constructed by user code at runtime.

In #1540 I also found the related terms primordial and intrinsic as defined at https://github.com/tc39/how-we-work/blob/master/terminology.md, which basically mean "standard builtins" (built-in objects required by the ECMAScript standard) according to my understanding.

However, what confused me is the section 10.4 "Built-in Exotic Object Internal Methods and Slots":

This specification defines several kinds of built-in exotic objects.

Either my understanding of built-in objects as statically created intrinsics is wrong, or this really should say "standard objects" not "built-in objects". Bound functions, Array instances, String instances, arguments objects and typed array instances are all constructed at runtime, by user code. I would not classify them as built-in objects? I think they should be named "standard exotic objects".
The only exception in this section would be Immutable Prototype Exotic Objects, where %Object.prototype% is indeed a builtin (but still a standard exotic built-in object), and Module Namespace Exotic Objects, which cannot be constructed by user code.
(I'm also missing proxy exotic objects under 10.4, no idea why they got a separate section 10.5)

The title of the section "Built-in Exotic Object Internal Methods and Slots" has an ambiguity where "built-in" may refer to either the "exotic object" or to the "internal methods and slots". The latter are indeed built-in, just like there are also "built-in types" or "built-in operators" in the spec, but I'd change it to "Standard …" either way (or omit it altogether). The adjective "Standard" has also been brought up here when discussing 10.3 "Built-in Function Objects".

Or is my understanding completely wrong, and the term "built-in objects" actually does mean to include arrays, bound functions etc, as they are "specified and supplied by an ECMAScript implementation"? But what does count as a non-built-in object then? Is any object supplied by a call to OrdinaryObjectCreate also a built-in object?

@bergus
Copy link
Author

bergus commented Jan 3, 2022

More things that are at odds with my understanding:

@allenwb wrote in #1273 (comment)

a BuitinFunctionObject could be either an ECMAScript function object or an implementation provided exotic function object.

and that section 10.3.3 CreateBuiltinFunction is called also with abstract closures for promise callbacks at runtime, and for class definitions without a constructor.

@ljharb
Copy link
Member

ljharb commented Jan 3, 2022

Abstract closures didn't exist in 2018, when that comment was written; if that helps.

@bergus
Copy link
Author

bergus commented Jan 4, 2022

I didn't mean that those things contradict each other, but that both don't fit in my understanding of the term "built-in". I would have expected

  • "a function object can be either an ECMAScript function object (ordinary function defined by user code) or an exotic function object (implemented in native code, either standard or implementation-defined)"
  • "a builtin function object can be either a standard exotic function object (specified by ECMAScript) or an implementation-defined function object". (Not sure whether a host can build-in an ordinary function object?)
  • CreateBuiltinFunction should be called CreateNativeFunction, and "native function" should be defined as "a function implemented (usually in native code) by the implementation", opposed to ECMAScript function objects that are defined by user code.
  • class {} is just weird, it's a native function with a [[SourceText]] and [[ConstructorKind]] - is this edge case really necessary?

@jmdyck
Copy link
Collaborator

jmdyck commented Jan 4, 2022

... where the built-ins are created by the implementation at startup, providing the environment that user code can interact with, and the user-defined ones are constructed by user code at runtime.

That will run into some problems: search the spec for occurrences of "anonymous built-in function" (other than %ThrowTypeError%) and you'll find functions that are not created at startup, but rather in response to user code.

In #1540 I also found the related terms primordial and intrinsic as defined at https://github.com/tc39/how-we-work/blob/master/terminology.md, which basically mean "standard builtins" (built-in objects required by the ECMAScript standard) according to my understanding.

If an implementation added non-standard objects to the initial environment, I think I'd call those intrinsics too.

However, what confused me is the section 10.4 "Built-in Exotic Object Internal Methods and Slots":

This specification defines several kinds of built-in exotic objects.

Either my understanding of built-in objects as statically created intrinsics is wrong, or this really should say "standard objects" not "built-in objects".

I'm inclined to agree. The spec definitely does define built-in exotic objects (e.g. %Array.prototype%), but they're not really the topic of 10.4.

Bound functions, Array instances, String instances, arguments objects and typed array instances are all constructed at runtime, by user code. I would not classify them as built-in objects?

Right. They're not "supplied" by the implementation, except in the unhelpful sense that everything is supplied by the implementation.

The title of the section "Built-in Exotic Object Internal Methods and Slots" has an ambiguity where "built-in" may refer to either the "exotic object" or to the "internal methods and slots".

Given that the first sentence refers to "built-in exotic objects", my guess is that the title's intended meaning was "Internal Methods and Slots of Built-in Exotic Objects".

The latter are indeed built-in, just like there are also "built-in types" or "built-in operators" in the spec, but I'd change it to "Standard …" either way (or omit it altogether).

I wouldn't omit it, because the section isn't talking about all possible exotics, just the standard ones. So I'd vote for "Internal Methods and Slots of Standard Exotic Objects".

@jmdyck
Copy link
Collaborator

jmdyck commented Jan 4, 2022

I didn't mean that those things contradict each other, but that both don't fit in my understanding of the term "built-in". I would have expected

  • "a function object can be either an ECMAScript function object (ordinary function defined by user code) or an exotic function object (implemented in native code, either standard or implementation-defined)"

That's correct. (Though I think "implemented in native code" is too specific.)

  • "a builtin function object can be either a standard exotic function object (specified by ECMAScript) or an implementation-defined function object".

Hm, no. As I understand it, there are 3 mutually orthogonal dichotomies for objects (not just functions):

  • ordinary vs exotic
  • built-in vs non-built-in
  • standard vs implementation-defined [on second thought, I think this axis is more complicated. Or I could just say standard vs non-standard]

All 8 combinations are allowed, though a given implementation need not exhibit all of them.

(Not sure whether a host can build-in an ordinary function object?)

Yes, a built-in function can be ordinary.

  • CreateBuiltinFunction should be called CreateNativeFunction, and "native function" should be defined as "a function implemented (usually in native code) by the implementation", opposed to ECMAScript function objects that are defined by user code.

No, the spec explicitly says that built-in functions can be ECMAScript function objects.

@bergus
Copy link
Author

bergus commented Jan 4, 2022

Thanks for the clarifications @jmdyck!

built-ins are created by the implementation at startup, providing the environment that user code can interact with

That will run into some problems: search the spec for occurrences of "anonymous built-in function" and you'll find functions that are not created at startup

Ah, so "built-in" really is supposed to mean "built into the implementation" as opposed to "defined by user code", regardless whether it is an intrinsic created at startup or created dynamically.

They're not "supplied" by the implementation, except in the unhelpful sense that everything is supplied by the implementation.

I think this best describes my issue. Where to draw the line?
Would it make sense to distinguish between "built-in objects" (= intrinsics) and "built-in functions" (= functions whose behaviour is built into the implementation)? Also happy to call these "native functions", unless that introduces additional ambiguity.

My guess is that the title's intended meaning was "Internal Methods and Slots of Built-in Exotic Objects".

Yes, that was my guess as well, and it felt wrong.

I wouldn't omit it, because the section isn't talking about all possible exotics, just the standard ones. So I'd vote for "Internal Methods and Slots of Standard Exotic Objects".

Sure, anything that doesn't call them "built-in" is fine for me :-) I guess I just wouldn't call out "standard" since that's the implicit default for everything described in the specification, only non-standard objects need to be marked as "implementation-defined".

the spec explicitly says that built-in functions can be [ordinary] ECMAScript function objects

Oh, I didn't know that. Is it intentional that these can have an arbitrary [[SourceText]], which is not necessarily a NativeFunction? I kinda would have expected Function.prototype.toString to always return [ native code ] for native, uh, built-in functions. (And also would have expected it to throw an exception on non-callable objects, regardless whether they have a [[SourceText]] or not).

"a builtin function object can be either a standard exotic function object (specified by ECMAScript) or an implementation-defined function object".

Hm, no. As I understand it, there are 3 mutually orthogonal dichotomies for objects

Ah, right, I didn't consider built-in ordinary functions. Not having read the 10.3 "Built-in Function Objects" prose properly, I thought they would always need to have exotic [[Call]] / [[Construct]] as defined in 10.3 .

All 8 combinations are allowed, though a given implementation need not exhibit all of them.

Right. I would however distinguish

  • intrinsic (supplied by the engine at startup) vs non-intrinsic (created due to user code execution)
  • for functions: built-in/native (with hardwired behaviour implemented in the engine) vs non-built-in/non-native/user-defined (with as behaviour defined by user code)

where user-defined functions are necessarily non-intrinsic. Also I had some trouble imagining implementation-defined (i.e. non-standard) user-defined (i.e. non-built-in) functions, but I suppose a custom function definition language like WebAssembly would qualify. Those would necessarily need to be exotic though?

Also, isn't an ordinary object also necessarily a standard object? The only thing I could think of would be an object whose existence is non-standard, i.e. it's an intrinsic that but not part of the standard intrinsics…

@jmdyck
Copy link
Collaborator

jmdyck commented Jan 4, 2022

Ah, so "built-in" really is supposed to mean "built into the implementation" as opposed to "defined by user code", regardless whether it is an intrinsic created at startup or created dynamically.

That might be a good way to think of it, although "built into the implementation" isn't that great from a definitional perspective. (Sometimes I think that "built-in function" doesn't really have an intensional definition, it's just whatever the spec says is created by CreateBuiltinFunction.)

Would it make sense to distinguish between "built-in objects" (= intrinsics) and "built-in functions" (= functions whose behaviour is built into the implementation)?

I'm not sure what you're getting at there. In my understanding:

  • Intrinsics are a proper subset of built-in objects.
  • Built-in functions are also a proper subset of built-in objects.
  • Intrinsics and built-in functions have an intersection, but neither is a subset of the other.
  • I'm not sure if there are any built-in objects outside the union of intrinsics and built-in functions.

Is it intentional that these can have an arbitrary [[SourceText]], which is not necessarily a NativeFunction?

Hm. An ordinary [ECMAScript] function is required to have (i.e., behave as if it had) a [[SourceText]] slot. But if a built-in function is implemented as an ordinary function, the spec is kind of vague about how its [[SourceText]] gets set. (The same is true of almost every slot in Table 34: Internal Slots of ECMAScript Function Objects.) I imagine it's intentional that there's no requirement that the [[SourceText]] of an ordinary built-in function conform to the NativeFunction production.

I kinda would have expected Function.prototype.toString to always return [ native code ] for native, uh, built-in functions.

That does not appear to be guaranteed. An ordinary built-in function will have a [[SourceText]] slot, and for an exotic built-in function, the impl can add a [[SourceText]] slot; in either case if it's Unicode text and HostHasSourceTextAvailable returns true, Function.p.toString won't get to step 3.

(And also would have expected it to throw an exception on non-callable objects, regardless whether they have a [[SourceText]] or not).

(Apparently, Function.p.toString wants to defy your expectations.)

I would however distinguish

  • intrinsic (supplied by the engine at startup) vs non-intrinsic (created due to user code execution)

I agree that that's a dichotomy, though it isn't orthogonal to the built-in/non-built-in axis. (There's no such thing as an intrinsic non-built-in. I think.)

  • for functions: built-in/native (with hardwired behaviour implemented in the engine) vs non-built-in/non-native/user-defined (with as behaviour defined by user code)

So if an implementation implements (say) String.p.repeat as an ordinary function, that would fall in the first group?

Also I had some trouble imagining implementation-defined (i.e. non-standard) user-defined (i.e. non-built-in) functions,

Actually, I've lost track of what I had in mind for "standard" yesterday.

but I suppose a custom function definition language like WebAssembly would qualify. Those would necessarily need to be exotic though?

Yup.

Also, isn't an ordinary object also necessarily a standard object?

The definition for standard object is "object whose semantics are defined by this specification". If that means an object whose internal methods have spec-supplied algorithms, then yes every ordinary object is a standard object.

But by that interpretation, every built-in function (regardless of how it's implemented, and regardless of whether it's a function defined in the spec) would be a standard object. And if you can't say "a non-standard built-in function", I'm not sure that's a good meaning for "standard".

The only thing I could think of would be an object whose existence is non-standard, i.e. it's an intrinsic that but not part of the standard intrinsics…

If "standard object" meant something like "object whose existence is guaranteed by the spec", then yeah it would make sense to talk about [non-]standard built-ins and [non-]standard intrinsics.

@bergus
Copy link
Author

bergus commented Jan 4, 2022

Apparently, Function.protoype.toString wants to defy your expectations.

Yeah, so it seems :-) Maybe I'll open another issue to discuss that in particular.

intrinsic (supplied by the engine at startup) vs non-intrinsic (created due to user code execution)

I agree that that's a dichotomy, though it isn't orthogonal to the built-in/non-built-in axis. (There's no such thing as an intrinsic non-built-in. I think.)

Yes, I meant that as a replacement (with different name and meaning) for the built-in/non-built-in axis, which seemed unhelpful with its current definition (as "objects supplied by the implementation").

for functions: built-in/native (with hardwired behaviour implemented in the engine) vs non-built-in/non-native/user-defined (with as behaviour defined by user code)

So if an implementation implements (say) String.prototype.repeat as an ordinary function, that would fall in the first group?

Yes. It only matters that it's implemented by the ECMAScript host implementation, not whether it's an ordinary native function or an exotic native function.

The definition for standard object is "object whose semantics are defined by this specification". If that means an object whose internal methods have spec-supplied algorithms, then yes every ordinary object is a standard object.

But by that interpretation, every built-in function (regardless of how it's implemented, and regardless of whether it's a function defined in the spec) would be a standard object. And if you can't say "a non-standard built-in function", I'm not sure that's a good meaning for "standard".

How so? A built-in function could be a function object with an algorithm not defined by the ECMAScript standard, implemented either as an exotic function (with custom [[Call]]) or as an ordinary function with custom source code.

@jmdyck
Copy link
Collaborator

jmdyck commented Jan 5, 2022

intrinsic (supplied by the engine at startup) vs non-intrinsic (created due to user code execution)

[...] I meant that as a replacement (with different name and meaning) for the built-in/non-built-in axis,

Yup, that seems valid. (intrinsic/non-intrinsic is orthogonal to ordinary/exotic).

for functions: built-in/native (with hardwired behaviour implemented in the engine) vs non-built-in/non-native/user-defined (with as behaviour defined by user code)

So if an implementation implements (say) String.prototype.repeat as an ordinary function, that would fall in the first group?

Yes. It only matters that it's implemented by the ECMAScript host implementation, not whether it's an ordinary native function or an exotic native function.

So isn't that the same dichotomy as built-in vs non-built-in?

The definition for standard object is "object whose semantics are defined by this specification". If that means an object whose internal methods have spec-supplied algorithms, then yes every ordinary object is a standard object.
But by that interpretation, every built-in function (regardless of how it's implemented, and regardless of whether it's a function defined in the spec) would be a standard object. And if you can't say "a non-standard built-in function", I'm not sure that's a good meaning for "standard".

How so? A built-in function could be a function object with an algorithm not defined by the ECMAScript standard, implemented either as an exotic function

Yes.

(with custom [[Call]])

No. In general, an exotic function can have a custom [[Call]] method, but a built-in function implemented as an exotic cannot be just any exotic function. See 10.3 Built-in Function Objects, last paragraph: "If a built-in function object is not implemented as an ECMAScript function [i.e., if it's implemented as an exotic function] it must provide [[Call]] and [[Construct]] internal methods that conform to the following definitions".

I.e., a built-in function's [[Call]] method has exactly two possibilities: the ordinary [[Call]] of 10.2.1 or the exotic [[Call]] of 10.3.1. Either way, all of a built-in function's internal methods are spec-supplied algorithms.

or as an ordinary function with custom source code.

Yes.

@bergus
Copy link
Author

bergus commented Jan 5, 2022

It only matters that it's implemented by the ECMAScript host implementation

So isn't that the same dichotomy as built-in vs non-built-in?

Maybe? That's why I called it "built-in function" or "native function". But the difference to the current definition of "built-in" in the spec is that my term only applies to function objects, and is only concerned with what [[Call]] does.
As established, I don't think it makes sense to call any object built-in just because it has some of its characteristics implemented natively in the engine, or it was created by the engine. The ordinary/exotic and intrinsic/non-intrinsic dichotomies handle those aspects much better with a clear definition. Proxy instances, arrays, iterator result objects, property descriptor objects, collections, typed arrays, promises - I wouldn't call them "built-in" any more than objects created from object literals.
Bringing up the WebAssembly function example again, even if that is an exotic object and may have custom slots or non-default internal methods (necessarily defined by the implementation), it is ultimately a user-defined function to me, not a built-in function.

A built-in function could be a function object with an algorithm not defined by the ECMAScript standard, implemented either as an exotic function (with custom [[Call]])

No. In general, an exotic function can have a custom [[Call]] method, but a built-in function implemented as an exotic cannot be just any exotic function. See 10.3 Built-in Function Objects, last paragraph: "If a built-in function object is not implemented as an ECMAScript function [i.e., if it's implemented as an exotic function] it must provide [[Call]] and [[Construct]] internal methods that conform to the following definitions".

I.e., a built-in function's [[Call]] method has exactly two possibilities: the ordinary [[Call]] of 10.2.1 or the exotic [[Call]] of 10.3.1. Either way, all of a built-in function's internal methods are spec-supplied algorithms.

I understand that "conforming to these definitions" in 10.3 does specify invariants, or at best templates, not concrete algorithms. Given that "An implementation may also provide additional built-in function objects that are not defined in this specification", not all built-in functions have spec-supplied algorithms. Things like setTimeout are non-standard, but built-in, in my book.

Also I think that the sentence "If a built-in function object is not implemented … it must provide …" really should only apply to standard functions. The whole section 10.3 speaks about "The built-in function objects defined in this specification", which "may be implemented as either ECMAScript function objects (10.2) whose behaviour is provided using ECMAScript code or as implementation provided function exotic objects whose behaviour is provided in some other manner."

I do believe that implementations are allowed to provide exotic function objects with [[Call]] and/or [[Construct]] methods that do not conform to 10.3.1/10.3.2. Proxy function objects and bound function objects are examples of this, and I see no reason why an implementation couldn't extend this to other exotic function kinds. Some of which are implemented by user code, others I would classify as built-in.

@jmdyck
Copy link
Collaborator

jmdyck commented Jan 5, 2022

Proxy instances, arrays, iterator result objects, property descriptor objects, collections, typed arrays, promises - I wouldn't call them "built-in" any more than objects created from object literals.

Okay. Are you saying that the current definition would call them built-in?

I understand that "conforming to these definitions" in 10.3 does specify invariants, or at best templates, not concrete algorithms.

But is that particular to 10.3? Are there spec algorithms that you would say are concrete?

Given that "An implementation may also provide additional built-in function objects that are not defined in this specification", not all built-in functions have spec-supplied algorithms.

I think we need some more precision here. Note that there are two levels of algorithm involved. One is what a user would read to find out what the function does (call it 'behavior', say), and another is what a function's [[Call]] is set to. You can have a function whose [[Call]] is spec-defined but whose 'behavior' is not spec-defined. (So I'd say that a built-in function has a spec-defined [[Call]], but might or might not have a spec-defined 'behavior'.)

(Note that there's a similar problem with "semantics defined by this specification" in the definition of 'standard object'. If a function's [[Call]]-level semantics are defined the spec, but its 'behavior'-level semantics aren't, the definition is unclear as to whether the function is 'standard' or not.)

Things like setTimeout are non-standard, but built-in, in my book.

And that seems sensible to me. I was just saying it might not be compatible with an interpretation in which every ordinary object is a standard object.

Also I think that the sentence "If a built-in function object is not implemented … it must provide …" really should only apply to standard functions. The whole section 10.3 speaks about "The built-in function objects defined in this specification", which "may be implemented as either ECMAScript function objects (10.2) whose behaviour is provided using ECMAScript code or as implementation provided function exotic objects whose behaviour is provided in some other manner."

Yes, the first sentence of 10.3 does have the qualification "defined in this specification", but then the last sentence of the paragraph talks about "additional built-in function objects that are not defined in this specification". So, having introduced built-in functions both defined in the spec and not, it would be reasonable to think that subsequent references to "built-in functions" without qualification include both.

I do believe that implementations are allowed to provide exotic function objects with [[Call]] and/or [[Construct]] methods that do not conform to 10.3.1/10.3.2.

Sure they are. I said as much in my previous comment.

Proxy function objects and bound function objects are examples of this, and I see no reason why an implementation couldn't extend this to other exotic function kinds.

Yup.

Some of which are implemented by user code, others I would classify as built-in.

Are you saying that the current definition of "built-in" allows you to classify them that way, or that you would like it to have a definition that allows you to classify them that way?

@bergus
Copy link
Author

bergus commented Jan 6, 2022

Are you saying that the current definition would call them built-in?

Yes. Or: it could at least be misunderstood so. The current spec does (wrongly imo) call proxies and arrays "built-in objects", and I guess the definition "specified and supplied by the implementation" could reasonably be construed to also mean iterator result objects and property descriptor objects. But this stretch is what makes the current definition rather useless.

I understand that "conforming to these definitions" in 10.3 does specify invariants, or at best templates, not concrete algorithms.

But is that particular to 10.3?

Yes, that was particular to 10.3.1 and 10.3.2. They have gaping holes that say "fill in the actual algorithm here" :-) But you're right:

I think we need some more precision here. Note that there are two levels of algorithm involved. One is what a user would read to find out what the function does (call it 'behavior', say), and another is what a function's [[Call]] is set to. You can have a function whose [[Call]] is spec-defined but whose 'behavior' is not spec-defined. (So I'd say that a built-in function has a spec-defined [[Call]], but might or might not have a spec-defined 'behavior'.)

I knew you'd call me out on that - I noticed but I can't come up with a good definition for this "behaviour".

Of course even ordinary, user-defined ECMAScript function objects do have a [[Call]] method with an algorithm that is spelled out in the specification (namely, to interpret the source code of the function).

I was just saying [setTimeout being non-standard] might not be compatible with an interpretation in which every ordinary object is a standard object.

Oh, good call!

Having introduced built-in functions both defined in the spec and not, it would be reasonable to think that subsequent references to "built-in functions" without qualification include both.

Hm, yeah.

I do believe that implementations are allowed to provide exotic function objects with [[Call]] and/or [[Construct]] methods that do not conform to 10.3.1/10.3.2.

Sure they are.

So would you just not count these as built-in functions?

Are you saying that the current definition of "built-in" allows you to classify them that way, or that you would like it to have a definition that allows you to classify them that way?

I would like to have a term with a definition that would classify them that way. I thought "built-in" would be that term, but would also be happy to call them "native" instead or something like that.
It's just that if "built-in" means something else, I don't understand its definition - bringing me back to the titular question "What is a built-in object?". If what you say is true, and it's not meant to have an intensional definition, I would suggest to remove it from 4.4 Terms and Definitions altogether.

@jmdyck
Copy link
Collaborator

jmdyck commented Jan 7, 2022

You can have a function whose [[Call]] is spec-defined but whose 'behavior' is not spec-defined. (So I'd say that a built-in function has a spec-defined [[Call]], but might or might not have a spec-defined 'behavior'.)

I knew you'd call me out on that - I noticed but I can't come up with a good definition for this "behaviour".

Yeah, I'm doubtful I'd be able to either.

Of course even ordinary, user-defined ECMAScript function objects do have a [[Call]] method with an algorithm that is spelled out in the specification (namely, to interpret the source code of the function).

Right, so its [[Call]] is definitely spec-defined, but 'behavior' is typically not.

I do believe that implementations are allowed to provide exotic function objects with [[Call]] and/or [[Construct]] methods that do not conform to 10.3.1/10.3.2.

Sure they are.

So would you just not count these as built-in functions?

(Ah, sorry, when you said "provide", you meant as built-ins (or, to avoid the problematic term, as intrinsics). I had something else in my head when I said "Sure they are".)

As I understand it, if an implementation provides an exotic intrinsic function that doesn't conform to 10.3.1/2, then that's a non-conforming implementation. (So whether I'd count that function as "built-in" is kind of beside the point. I guess if it's a non-conforming implementation, they can call it whatever they like.)

More practically, I wonder if conforming to 10.3.1/2 is much of a burden. It seems like sort of the minimum housekeeping for the function call to make sense.

It's just that if "built-in" means something else, I don't understand its definition - bringing me back to the titular question "What is a built-in object?". If what you say is true, and it's not meant to have an intensional definition,

Note that I'm claiming that, that's just how it seems to me sometimes. (That clarification is more for the benefit of someone dipping into the conversation at this point.)

Some history: The term "built-in object" has been in the spec since ES1. Up to and including ES5.1, the definition included the phrase "that is present at the start of the execution of an ECMAScript program". ES6 dropped that phrase from the definition and introduced "anonymous built-in functions" that are created (if at all) in response to the execution of user code.

I would suggest to remove it from 4.4 Terms and Definitions altogether.

(See also #1278.)

@bergus
Copy link
Author

bergus commented Jan 8, 2022

As I understand it, if an implementation provides an exotic intrinsic function that doesn't conform to 10.3.1/2, then that's a non-conforming implementation.
More practically, I wonder if conforming to 10.3.1/2 is much of a burden. It seems like sort of the minimum housekeeping for the function call to make sense.

I was thinking of something like a Function.prototype.partiallyApply(...args) method that works just like bind except binding only arguments but not the this value, and would return an exotic function object with a custom [[Call]] algorithm. Similar to a bound function, it would not push an extra execution context on the call stack, it would just forward to the [[Call]] method of the target function.
I think an implementation creating such exotic functions should count as conforming.

Some history: The term "built-in object" has been in the spec since ES1. Up to and including ES5.1, the definition included the phrase "that is present at the start of the execution of an ECMAScript program". ES6 dropped that phrase from the definition and introduced "anonymous built-in functions" that are created (if at all) in response to the execution of user code.

Oh, that's an extra source of confusion. So "built-in" basically did mean "intrinsic" until ES5!

Now, how do we resolve this situation? Would you accept a pull request where I try to straighten this out? Do we need to involve anyone else from the committee? Or is it deemed unnecessary to change the specification for this?

@jmdyck
Copy link
Collaborator

jmdyck commented Jan 8, 2022

I was thinking of something like a Function.prototype.partiallyApply(...args) method that works just like bind except binding only arguments but not the this value, and would return an exotic function object with a custom [[Call]] algorithm. Similar to a bound function, it would not push an extra execution context on the call stack, it would just forward to the [[Call]] method of the target function. I think an implementation creating such exotic functions should count as conforming.

Yeah, I don't think that's a problem. The within-standard analogy is Function.prototype.bind, which is a built-in function (and so must have a [[Call]] that conforms to 10.2.1/10.3.1), and yet it creates functions whose [[Call]] conforms to 10.4.1.1 (and so can't be considered "built-in"). (Likewise, the Proxy constructor is a built-in function that can create non-built-in functions.) So, on the non-standard side, I don't think there's anything non-conformant about supplying a built-in F.p.partiallyApply that conforms to 10.{2,3}.1 but creates functions that don't.

Oh, that's an extra source of confusion. So "built-in" basically did mean "intrinsic" until ES5!

Yeah, the distinction we now have between "built-in" and "intrinsic" wasn't really needed until ES6, I think.

Now, how do we resolve this situation? Would you accept a pull request where I try to straighten this out?

It's not up to me to accept or reject, though I'd probably have comments. If you're only straightening out terminology, then you'd theoretically only need approval from the editors.

@bergus
Copy link
Author

bergus commented Jan 8, 2022

Yeah, I don't think that's a problem. […] it creates functions whose [[Call]] conforms to 10.4.1.1 (and so can't be considered "built-in")

Ok, so yes you don't consider these to be built-in. They would be "native" (with implementation-defined behavior) functions though?

It's not up to me to accept or reject, though I'd probably have comments. If you're only straightening out terminology, then you'd theoretically only need approval from the editors.

Good! Then I'll wait for comments from the editors whether they consider this to be a problem worth fixing before drafting a PR.

@jmdyck
Copy link
Collaborator

jmdyck commented Jan 9, 2022

Yeah, I don't think that's a problem. […] it creates functions whose [[Call]] conforms to 10.4.1.1 (and so can't be considered "built-in")

Ok, so yes you don't consider these to be built-in.

(On second thought, it's tricky. One could try to argue that (e.g.) a bound function's [[Call]], while conforming to 10.4.1.1, also conforms to 10.2.1 or 10.3.1, and so could qualify as a built-in function. I think that argument is incorrect, but I'm not positive.)

They would be "native" (with implementation-defined behavior) functions though?

Presumably you would define "native" so that they are.

Note that the spec has used the term "native" in the past, with a different meaning. From ES1 to ES5.1, it went something like:

  • The set of all objects can be partitioned into:
    • host objects (supplied by the host environment), and
    • native objects (supplied by the implementation, independent of any host environment), which can be partitioned into:
      • built-in objects (supplied by impl, independent of host, and present at start of execution), and
      • non-built-in objects (supplied by impl, independent of host, and constructed during the course of execution of a program).

(Not that that precludes a new definition of "native", it's just maybe worth mentioning, so that it's not a source of confusion.)

Then I'll wait for comments from the editors whether they consider this to be a problem worth fixing before drafting a PR.

You might want to re-state your current understanding of the problem(s), so that people can get that without reading the whole preceding discussion.

@bergus
Copy link
Author

bergus commented Jan 10, 2022

Note that the spec has used the term "native" in the past, with a different meaning. From ES1 to ES5.1, it went something like […]

Ah right, that's the dichotomy I remember. I had wanted to check in which revision exactly these definitions changed, didn't get to it until now - it was ES6 apparently. So yeah, maybe re-defining "native" is not the best idea.

You might want to re-state your current understanding of the problem(s), so that people can get that without reading the whole preceding discussion.

I suggest the following editorial changes:

  • remove the current definition of "built-in object" as "object specified and supplied by an ECMAScript implementation" from section 4.4 Terms and Definitions. The current definition is either ill-specified, useless, or both, depending on how you interpret it. An additional source of confusion is that until ES6, it did include the clause "that is present at the start of the execution of an ECMAScript program", thereby meaning an intrinsic object, which does include less objects than the current specification refers to as "built-in objects".
  • accordingly, remove the terms "built-in function" and "built-in method" which depend on the definition of "built-in object".
  • Rename section 10.4 "Built-in Exotic Object Internal Methods and Slots" to just "Exotic Object Internal Methods and Slots". The objects described in that section (arrays, strings, arguments, etc) are not "built-in" (by any sensible usage of that term).
  • accordingly, omit the word "built-in" from the first sentence of that section ("This specification defines several kinds of built-in exotic objects.")
  • Add a term and definition for "intrinsic". It's currently only defined in https://github.com/tc39/how-we-work/blob/master/terminology.md, where only few people will find it. Add a note that intrinsic objects were known as built-in objects in previous editions. (I'm not 100% certain what the difference to primordials is, and how intrinsics might be not observable).
  • If possible, attempt a re-definition of "built-in function". If necessary, use an intensional definition as "every function created by CreateBuiltinFunction", although I wouldn't be happy about that.
    If you think it will lead to less confusion, use the term "native function" instead? I know that "native object" had a different definition in ES5 (basically what is now a "standard object"), and that meaning was eliminated from ES6 with good reason. However I believe that the community expects "native function"s to be exactly functions that are native to the implementation and not defined by a user program.

I suggest the following changes with possible normative impact:

  • Get rid of the sentence "The built-in function objects defined in this specification may be implemented as either ECMAScript function objects whose behaviour is provided using ECMAScript code". While I don't want to restrict implementations by preventing them from implementing built-in functions using ECMAScript code, I don't think it needs to be explicitly stated that these are ECMAScript function objects. It's simply allowed by the as-if rule, an implementation that does this is still conforming. I expect this not to have observable changes, however it does simplify terminology by eliminating one dichotomy axis:
    • any "built-in function" would be an exotic function
    • any "ordinary function" would be user-defined and necessarily created by OrdinaryFunctionCreate
    • "ECMAScript function objects" would become synonymous to "ordinary function"
    • we would not need a term for "implementation provided function exotic objects whose behaviour is provided in some other [not ECMAScript function object] manner."
    • it would be clear which slots a built-in function can have
    • it's not quite clear how CreateBuiltinFunction can create a ECMAScript function object given it does not create the necessary slots for one
  • Clarify the "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" requirement.
    • Is this intended only for built-in functions defined by the ECMAScript standard?
    • Is an implementation allowed to create exotic function objects that are not classified as built-ins? If yes, what term should we use for them? As for host specifications that I know of, it appears that CreateBuiltinFunction is used both by WebIDL and WebAssembly.
    • or, if "built-in" is defined extensionally, should we prevent implementations to create non-standard functions by not going through CreateBuiltinFunction?
    • Is an implementation allowed to provide ordinary functions as (non-standard) intrinsics, or do all intrinsic functions need to be built-in (as ECMAScript intrinsic functions are)?
  • If there will only be an intensional definition of "built-in function" (as "those created by CreateBuiltinFunction"), or no definition at all, consider providing alternative CreateFunction (or FunctionCreate) methods that can be used by host specifications to create exotic non-built-in function objects with algorithm steps. How should other specs indicate that they create built-in functions that support [[Construct]]? #2209 is related.
  • Depending on whether bound function objects should be counted as built-in functions or not, consider creating them with an abstract closure instead of defining a separate type of exotic object.

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

No branches or pull requests

4 participants
@ljharb @bergus @jmdyck and others