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

_intrinsics_.[[%Foo.bar%]] isn't defined #2538

Open
jmdyck opened this issue Sep 30, 2021 · 12 comments
Open

_intrinsics_.[[%Foo.bar%]] isn't defined #2538

jmdyck opened this issue Sep 30, 2021 · 12 comments

Comments

@jmdyck
Copy link
Collaborator

jmdyck commented Sep 30, 2021

I just realized that references of the form _intrinsics_.[[%Foo.bar%]] aren't defined.

9.3.2 CreateIntrinsics says of the intrinsics Record:

The field names are the names listed in column one of [the table of Well-Known Intrinsics]

and that includes %Foo%, but not %Foo.bar%. (That table dropped its rows for 'subordinate' intrinsics in PR #2056, but even before that, %Foo.bar% only appeared in column 3; column 1 had the old-style %FooBar%.)

I think there are only three cases of this:

  • _intrinsics_.[[%Function.prototype%]] in CreateIntrinsics
  • _intrinsics_.[[%Object.prototype%]] in SetRealmGlobalObject
  • _realm_.[[Intrinsics]].[[%Function.prototype%]] in CreateBuiltinFunction

It's not obvious to me how to fix these. E.g.:

  • _intrinsics_.[[%Foo%]].bar is improper pseudo-syntax.
  • GetV(_intrinsics_.[[%Foo%]], "bar") is well defined, but it doesn't work, because it returns the current value of the 'bar' property, whereas %Foo.bar% is defined to be the initial value ("prior to any ECMAScript code being evaluated"). This doesn't make a difference for the first two cases, because CreateIntrinsics and SetRealmGlobalObject only execute before user code, but it's a problem for the third case, because CreateBuiltinFunction sometimes executes after user code.

Maybe we need to just say that the intrinsics Record also has fields for %Function.prototype% and %Object.prototype%.

@ljharb
Copy link
Member

ljharb commented Sep 30, 2021

For those two intrinsics (since it's rare to access things directly off of _intrinsics_), we could also potentially add top-level forms that avoids the dot?

@jmdyck
Copy link
Collaborator Author

jmdyck commented Sep 30, 2021

So reinstate %FunctionPrototype% and %ObjectPrototype% to the table, and then change those 3 cases accordingly?

@ljharb
Copy link
Member

ljharb commented Sep 30, 2021

That seems like the shortest-term solution, albeit a bit of a gross one.

@jmdyck
Copy link
Collaborator Author

jmdyck commented Oct 7, 2021

On second thought, this is just part of a larger problem.

By combining statements from 6.1.7.4 Well-Known Intrinsic Objects and 9.3.2 CreateIntrinsics, we can infer that %Foo% is basically a shorthand for (the current Realm).[[Intrinsics]].[[%Foo%]].

However, there's no corresponding shorthand for %Foo.bar%. Sure, 6.1.7.4 tells us what it means, but there's no way to realize that meaning, given the structures defined in the spec.

Now, it's true that there are other cases where the spec specifies some semantics without giving an explicit structure (e.g., the properties of an object, the bindings in an environment record), and we could say that %Foo.bar% is another such. But any hand-waving we do for %Foo.bar% would work for %Foo% as well, so why bother having _realm_.[[Intrinsics]] at all? And anyway, there's a general trend toward replacing such cases with explicit structures.

So I think we should keep the _realm_.[[Intrinsics]] record, and extend it to have a field for every intrinsic, not just for the ones in the Well-Known Intrinsics table.

@ljharb
Copy link
Member

ljharb commented Oct 7, 2021

That would be unsustainable, since every single thing in the spec is an intrinsic. The whole benefit of the intrinsics syntax is its implicitness.

@jmdyck
Copy link
Collaborator Author

jmdyck commented Oct 9, 2021

I'm not suggesting changing the intrinsics syntax.

@ljharb
Copy link
Member

ljharb commented Oct 9, 2021

Right - I’m saying that explicitly adding every intrinsic to [[Intrinsics]] is unsustainable.

@jmdyck
Copy link
Collaborator Author

jmdyck commented Oct 9, 2021

I'm also not suggesting explicitly adding anything to [[Intrinsics]]. Step 3 of CreateIntrinsics would retain its current implicitness.

@ljharb
Copy link
Member

ljharb commented Oct 9, 2021

I suppose that'd be workable.

@jmdyck
Copy link
Collaborator Author

jmdyck commented Oct 9, 2021

(Sorry, that was my fault. Since my first comment suggested explicitly adding %Function.prototype% and %Object.prototype% to [[Intrinsics]], I should have been clearer that my later suggestion didn't involve explicit adds.)

@michaelficarra michaelficarra added the editor call to be discussed in the next editor call label Oct 12, 2021
@bakkot bakkot removed the editor call to be discussed in the next editor call label Oct 13, 2021
@bakkot
Copy link
Contributor

bakkot commented Oct 13, 2021

So I think we should keep the _realm_.[[Intrinsics]] record, and extend it to have a field for every intrinsic, not just for the ones in the Well-Known Intrinsics table.

Sounds good to the editors, and in particular we think that the logic probably belongs in step 3 of CreateIntrinsics, but we'd need to come up with some wording.

@jmdyck
Copy link
Collaborator Author

jmdyck commented Oct 15, 2021

in particular we think that the logic probably belongs in step 3 of CreateIntrinsics,

That step is already the longest one in the whole spec (by a wide margin), so I'm reluctant to make it much longer. (One possibility: we could make that step a Host operation unto itself. All the sentences of the step could become constraints that the operation must conform to, which I think would allow them to be formatted more readably.)

but we'd need to come up with some wording.

I think we'd benefit from defining 'intrinsics'. The intuition is that the intrinsics are exactly the set of objects created by step 3, but of course we have to define it the other way around. Something like:

Each realm has a set of <dfn>intrinsic objects</dfn>
(distinct from those of every other realm) consisting of:
(a) an object for each of the entries in Table 8 "Well-Known Intrinsic Objects", and
(b) every object that is required by this spec to be the value of a property of an intrinsic object.
    (This rule is applied recursively.)

At (b), I was tempted to also recurse on the value of internal slots, in particular [[Prototype]], since such objects would necessarily also have to be created for the intrinsics to be well-formed. However, it looks like any object that is the [[Prototype]] of an intrinsic is already included via the above rules. (That is, either it's in Table 8, like %Error% or %TypedArray%, or it's pulled in via a "prototype" property, like %Object.prototype%.) And apart from [[Prototype]], intrinsics don't appear to have any other object-valued slots. It might be worth recording these points as an editorial invariant.

We should maybe note that an implementation is allowed to add its own things to (a).

Then step 3 has two parts:

  1. Create the set of intrinsic objects for the realm (subject to various constraints), and
  2. For each intrinsic object, add a field to the [[Intrinsics]] record:
    the field name is the spec-internal name of the intrinsic (e.g., %AggregateError% or %Array.prototype.at%),
    and the field value is the object.

Mind you, the concept of the "spec-internal name" of an intrinsic is theoretically under-defined. E.g.:

  • initially, Set.p.keys and Set.p.values are the same object,
  • so %Set.prototype.keys% and %Set.prototype.values% would both be valid ways for spec algorithms to refer to that object,
  • so it doesn't have a single spec-internal name,
  • so the [[Intrinsics]] field name for that object is uncertain.

Of course, the spec doesn't ever refer to that intrinsic explicitly, so to that extent the answer doesn't matter.

Some possible answers:

  • Just leave it under-defined.
  • Add a note that it's under-defined, but that that doesn't matter in practice.
  • Allow the [[Intinisics]] record to have multiple fields with the same object as the field value.
  • Define a 'canonical name' for each intrinsic, and use that as the [[Intrinsics]] field name.
  • Rather than say that [[Intrinsics]] is a Record, say that it's some implementation-defined structure that holds all the intrinsics and allows (spec-level) access to them via %Foo(.bar)*% notation. But (a) that goes in the opposite direction to this card and (b) there are a few spots in the spec that assume that it's a record (e.g. _realmC_.[[Intrinsics]].[[%Array%]] in ArraySpeciesCreate).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants