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

Normative: fix Function toString on builtins #1948

Merged
merged 1 commit into from
Aug 5, 2020

Conversation

devsnek
Copy link
Member

@devsnek devsnek commented Apr 14, 2020

@michaelficarra michaelficarra added normative change Affects behavior required to correctly evaluate some ECMAScript source text needs consensus This needs committee consensus before it can be eligible to be merged. labels Apr 14, 2020
@bathos
Copy link
Contributor

bathos commented Apr 14, 2020

Summarizing a bit from the original issue thread:

  • JSC & V8 currently don’t conform to these rules (builtin accessors end up having two identifiers)
  • Spidermonkey does conform (by omitting the "get"/"set" prefixes)
  • this change would clarify the expected behavior by codifying Spidermonkey’s solution

@ljharb ljharb added the needs test262 tests The proposal should specify how to test an implementation. Ideally via github.com/tc39/test262 label Apr 14, 2020
@michaelficarra
Copy link
Member

Why don't we just have SetFunctionName set an internal slot with the name parameter instead of using the strange "without the prefix" wording?

@devsnek devsnek force-pushed the fix-func-tostring branch 3 times, most recently from c7358d2 to b9f22de Compare April 16, 2020 02:24
@devsnek devsnek force-pushed the fix-func-tostring branch from b9f22de to c050d55 Compare June 26, 2020 15:21
@devsnek devsnek force-pushed the fix-func-tostring branch 2 times, most recently from 03e9ece to 374eece Compare June 26, 2020 15:41
@devsnek
Copy link
Member Author

devsnek commented Jun 26, 2020

What if we got rid of the prose about length and name in the built in function definition area and added SetFunctionLength and SetFunctionName calls to CreateBuiltinFunction?

@ljharb
Copy link
Member

ljharb commented Jun 26, 2020

The prose ensures name and length are the implicit values that would be intuitive, if I’m understanding what you’re referring to. How would you envision it looking with your change?

@devsnek
Copy link
Member Author

devsnek commented Jun 26, 2020

@ljharb i was thinking something like this:

1. Let length be the length of the function specified by _steps_. Unless otherwise specified, this is largest number of named arguments shown in the subclause headings for _steps_. Optional parameters (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form «...name») are not included in the default argument count.
1. Perform ! SetFunctionLength(_F_, _length_).
1. Let name be this value is the name that is given to the function specified by _steps_. Functions that are identified as anonymous functions use the empty String as the value of the "name" property. For functions that are specified as properties of objects, the name value is the property name string used to access the function.
1. If _F_ is specified as a get or set assessor function, let _prefix_ be "get " or "set ".
1. Perform ! SetFunctionName(_F_, _name_, _prefix_).

@devsnek devsnek force-pushed the fix-func-tostring branch from 374eece to c0eb838 Compare June 26, 2020 16:15
spec.html Outdated Show resolved Hide resolved
@devsnek devsnek force-pushed the fix-func-tostring branch from c0eb838 to 27af2e0 Compare June 26, 2020 16:29
spec.html Outdated Show resolved Hide resolved
@devsnek devsnek force-pushed the fix-func-tostring branch from 27af2e0 to fd78f5c Compare June 26, 2020 18:08
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
@gibson042
Copy link
Contributor

#1941 (comment)

There appears to be a majority desire on the committee for native accessors to be represented similarly to author-defined accessors, which manifest differently based on how they are defined:

  1. get PropertyName () { FunctionBody } in an object initializer evaluates with name matching "get " + PropertyName and [[SourceText]] containing the matched source text starting with "get" per 14.3 Method Definitions.
  2. get () { FunctionBody } in a property descriptor evaluates with name "get" and [[SourceText]] containing the matched source text starting with "get" per the same.
  3. get: function ( FormalParameters ) { FunctionBody } in a property descriptor evaluates with name "get" and [[SourceText]] containing the function expression starting with "function" per 12.2.6.8 Runtime Semantics: PropertyDefinitionEvaluation.

Given that built-in accessors have "get " or "set " prepended to their name per 17 ECMAScript Standard Built-in Objects, it seems like option 1 is the best fit and Map.prototype.__lookupGetter__('size').toString() should return something like "get size() { [native code] }" (without a "function" substring).

spec.html Outdated Show resolved Hide resolved
spec.html Outdated
@@ -26469,7 +26475,7 @@ <h1>Function.prototype.toString ( )</h1>
<p>When the `toString` method is called, the following steps are taken:</p>
<emu-alg>
1. Let _func_ be the *this* value.
1. If _func_ is a <emu-xref href="#sec-bound-function-exotic-objects">bound function exotic object</emu-xref> or a <emu-xref href="#sec-built-in-function-objects">built-in function object</emu-xref>, then return an implementation-defined String source code representation of _func_. The representation must have the syntax of a |NativeFunction|. Additionally, if _func_ is a <emu-xref href="#sec-well-known-intrinsic-objects">Well-known Intrinsic Object</emu-xref> and is not identified as an anonymous function, the portion of the returned String that would be matched by |PropertyName| must be the initial value of the *"name"* property of _func_.
1. If _func_ is a <emu-xref href="#sec-bound-function-exotic-objects">bound function exotic object</emu-xref> or a <emu-xref href="#sec-built-in-function-objects">built-in function object</emu-xref>, then return an implementation-defined String source code representation of _func_. The representation must have the syntax of a |NativeFunction|. Additionally, if _func_ has an [[InitialName]] internal slot and _func_.[[InitialName]] is a String, the portion of the returned String that would be matched by |NativeFunctionAccessor?| |PropertyName| must be the value of _func_.[[InitialName]].
Copy link
Contributor

@ExE-Boss ExE-Boss Jul 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be changed to:

Suggested change
1. If _func_ is a <emu-xref href="#sec-bound-function-exotic-objects">bound function exotic object</emu-xref> or a <emu-xref href="#sec-built-in-function-objects">built-in function object</emu-xref>, then return an implementation-defined String source code representation of _func_. The representation must have the syntax of a |NativeFunction|. Additionally, if _func_ has an [[InitialName]] internal slot and _func_.[[InitialName]] is a String, the portion of the returned String that would be matched by |NativeFunctionAccessor?| |PropertyName| must be the value of _func_.[[InitialName]].
1. If _func_ is a <emu-xref href="#sec-built-in-function-objects">built-in function object</emu-xref>, then return an implementation-defined String source code representation of _func_. The representation must have the syntax of a |NativeFunction|. Additionally, if _func_ has an [[InitialName]] internal slot and _func_.[[InitialName]] is a String, the portion of the returned String that would be matched by |NativeFunctionAccessor?| |PropertyName| must be the value of _func_.[[InitialName]].

Since Bound Function exotic objects can be handled by the same step that handles Proxy exotic objects and they don’t have [[InitialName]].

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should [[InitialName]] be carried over by bound function exotic objects? It seems like that was implied by the "initial name property"

Copy link
Contributor

@ExE-Boss ExE-Boss Jul 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think so given that bound function exotic objects aren’t intrinsics, and thus are already not covered by the current requirement for the PropertyName portion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this is correct as is. The text puts the propertyname requirement only on functions which have an [[InitialName]] slot that has a string.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That being said the bound functions are all over the place too

'use strict';
const F = Object.getOwnPropertyDescriptor(Map.prototype, 'size').get;
print('1', F.toString());
print('2', F.bind(null).toString());
┌────────────────┬───────────────────────────────────────────────┐
│ ChakraCore     │ 1 get size                                    │
│                │ 2 function() {                                │
│                │     [native code]                             │
│                │ }                                             │
├────────────────┼───────────────────────────────────────────────┤
│ engine262      │ 1 function get size() { [native code] }       │
│ engine262+     │ 2 function bound get size() { [native code] } │
├────────────────┼───────────────────────────────────────────────┤
│ GraalJS        │ 1 function get size() { [native code] }       │
│                │ 2 function bound() { [native code] }          │
├────────────────┼───────────────────────────────────────────────┤
│ JavaScriptCore │ 1 function get size() {                       │
│                │     [native code]                             │
│                │ }                                             │
│                │ 2 function get size() {                       │
│                │     [native code]                             │
│                │ }                                             │
├────────────────┼───────────────────────────────────────────────┤
│ Moddable XS    │ 1 function get size (){[native code]}         │
│                │ 2 function bound get size (){[native code]}   │
├────────────────┼───────────────────────────────────────────────┤
│ QuickJS        │ 1 function get size() {                       │
│                │     [native code]                             │
│                │ }                                             │
│                │ 2 function bound get size() {                 │
│                │     [native code]                             │
│                │ }                                             │
├────────────────┼───────────────────────────────────────────────┤
│ SpiderMonkey   │ 1 function size() {                           │
│                │     [native code]                             │
│                │ }                                             │
│                │ 2 function() {                                │
│                │     [native code]                             │
│                │ }                                             │
├────────────────┼───────────────────────────────────────────────┤
│ V8             │ 1 function get size() { [native code] }       │
│                │ 2 function () { [native code] }               │
└────────────────┴───────────────────────────────────────────────┘

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My expectation (which we discussed doing in a followon PR) is that all of those with "get" or "set", would basically be get x() { or bound get x() {.

@devsnek devsnek force-pushed the fix-func-tostring branch from e417f39 to 7a1783b Compare July 22, 2020 15:04
@ljharb ljharb added has consensus This has committee consensus. and removed needs consensus This needs committee consensus before it can be eligible to be merged. labels Jul 22, 2020
@devsnek devsnek force-pushed the fix-func-tostring branch from 7a1783b to 7dd854e Compare July 26, 2020 00:12
@devsnek
Copy link
Member Author

devsnek commented Jul 26, 2020

Tests PR: tc39/test262#2716

@devsnek
Copy link
Member Author

devsnek commented Jul 26, 2020

Based on some testing we could actually refine NativeFunction more, if we wanted:

NativeFunction : `function` NativeFunctionAccessor? NativeFunctionName? `(` `)` `{` `[` `native` `code` `]` `}`
NativeFunctionAccessor :
  `get`
  `set`
NativeFunctionName :
  IdentifierName
  `[` `Symbol` `.` IdentifierName `]`

@bathos
Copy link
Contributor

bathos commented Jul 26, 2020

@devsnek Wouldn't NativeFunctionAccessor? NativeFunctionName? be ambiguous given IdentifierName includes "get" and "set"? (Or maybe that doesn't matter here, since unlike with rules used during parsing, it's not really necessary to know which production was matched, only that any of them would be..?)

@michaelficarra
Copy link
Member

@devsnek That would require approval from committee again. I suggest we move forward with what was approved already.

@michaelficarra
Copy link
Member

@bathos Yes, we do not require an unambiguous parse in this case.

spec.html Outdated
@@ -24918,7 +24924,7 @@ <h1>ECMAScript Standard Built-in Objects</h1>
<p>For example, the function object that is the initial value of the *"map"* property of the Array prototype object is described under the subclause heading &laquo;Array.prototype.map (callbackFn [ , thisArg])&raquo; which shows the two named arguments callbackFn and thisArg, the latter being optional; therefore the value of the *"length"* property of that function object is 1.</p>
</emu-note>
<p>Unless otherwise specified, the *"length"* property of a built-in function object has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *true* }.</p>
<p>Every built-in function object, including constructors, has a *"name"* property whose value is a String. Unless otherwise specified, this value is the name that is given to the function in this specification. Functions that are identified as anonymous functions use the empty String as the value of the *"name"* property. For functions that are specified as properties of objects, the name value is the property name string used to access the function. Functions that are specified as get or set accessor functions of built-in properties have *"get "* or *"set "* prepended to the property name string. The value of the *"name"* property is explicitly specified for each built-in functions whose property key is a Symbol value.</p>
<p>Every built-in function object, including constructors, has a *"name"* property whose value is a String. Unless otherwise specified, this value is the name that is given to the function in this specification. Functions that are identified as anonymous functions use the empty String as the value of the *"name"* property. For functions that are specified as properties of objects, the name value is the property name string used to access the function. Functions that are specified as get or set accessor functions of built-in properties have *"get "* or *"set "* prepended to the property name string. The value of the *"name"* property is explicitly specified for each built-in functions whose property key is a Symbol value. The *"name"* property is set using SetFunctionName.</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What actually calls SetFunctionName for the built-in functions? Does 8.2.2 need to be amended?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed this on the editor call. Clause 17 should be amended to actually set [[InitialName]]. Clause 8.2.2 does not need to change, but could use clarification that it is not steps, but is a description of the how the values in the table were created.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of change are you looking for here? I already amended it to explicitly say what logic should be used to set the name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess with #2116 we don't need to explicitly say how to set the name here because we already say they should be created with CreateBuiltinFunction?

spec.html Outdated Show resolved Hide resolved
spec.html Outdated
@@ -8878,11 +8883,12 @@ <h1>CreateBuiltinFunction ( _steps_, _internalSlotsList_ [ , _realm_ [ , _protot
1. If _realm_ is not present, set _realm_ to the current Realm Record.
1. Assert: _realm_ is a Realm Record.
1. If _prototype_ is not present, set _prototype_ to _realm_.[[Intrinsics]].[[%Function.prototype%]].
1. Let _func_ be a new built-in function object that when called performs the action described by _steps_. The new function object has internal slots whose names are the elements of _internalSlotsList_.
1. Let _func_ be a new built-in function object that when called performs the action described by _steps_. The new function object has internal slots whose names are the elements of _internalSlotsList_, and an [[InitialName]] internal slot.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commenting here as a reminder. Don't need to be acted on for this PR.

This object creation is weird. Built-in objects can either be ordinary function objects or exotic. This AO tries to be generic for both, with loose language about the internal slots in prose. We should clean this up to be similar to other object allocation sites and, and perhaps also split the exotic version from the ordinary versoin.

@syg syg added editor call to be discussed in the next editor call and removed editor call to be discussed in the next editor call labels Jul 29, 2020
@devsnek devsnek added has test262 tests and removed needs test262 tests The proposal should specify how to test an implementation. Ideally via github.com/tc39/test262 labels Aug 5, 2020
@ljharb ljharb self-assigned this Aug 5, 2020
@ljharb ljharb force-pushed the fix-func-tostring branch from 7dd854e to 552407b Compare August 5, 2020 22:22
@ljharb ljharb merged commit 552407b into tc39:master Aug 5, 2020
jmdyck added a commit to jmdyck/ecma262 that referenced this pull request Aug 11, 2020
jmdyck added a commit to jmdyck/ecma262 that referenced this pull request Aug 11, 2020
ljharb pushed a commit to jmdyck/ecma262 that referenced this pull request Aug 11, 2020
h2oche pushed a commit to h2oche/ecma262 that referenced this pull request Aug 16, 2020
@devsnek devsnek deleted the fix-func-tostring branch April 12, 2021 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has consensus This has committee consensus. has test262 tests normative change Affects behavior required to correctly evaluate some ECMAScript source text
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NativeFunction vs web reality
7 participants