Skip to content

Conversation

@jmdyck
Copy link
Collaborator

@jmdyck jmdyck commented Nov 24, 2025

This PR comprises a bunch of changes to InitializeHostDefinedRealm (IHDR). They're mostly independent, but I'm putting them into one PR to reduce churn for IHDR's callers (i.e., HTML and the ShadowRealm proposal, AFAIK).

@annevk: If/when the time comes, I can create a PR to update the HTML spec accordingly.


commit 1: Add customizations parameter

Currently, IHDR has no parameters. However, steps 10 and 12 say "If the host requires ..." so there's implicitly a need for the host to communicate what it requires. In the HTML spec, the algorithm to 'create a new realm' has that spec's only call to IHDR, and says to perform it with "customizations for creating the global object and the global this binding".

This commit makes explicit the communication of these customizations to IHDR. Specifically, it adds a customizations parameter to IHDR. The caller passes it an abstract closure that receives the realm record being initialized, and returns two objects to serve as the global object and the global this binding. (Or it can return undefined in either spot, to take the default.)

For example, where the HTML spec might currently say (very roughly):

1. Perform IHDR given the following customizations:
   - For the global object, create a new Window object.
   - For the global this binding, using _browsingContext_'s WindowProxy object.

it would instead say:

1. Let _customizations_ be the following steps, given a Realm Record _realm_:
   1. Let _global_ be a new Window object.
   1. Let _globalThisBinding_ be _browsingContext_'s WindowProxy object.
   1. Return « _global_, _globalThisBinding_ ».
1. Perform IHDR given _customizations_.

(The abstract closure doesnt appear to make use of the realm passed to it, but the new Window object is presumably somehow created in realm.)

(This commit is basically the same as what I described here: #3274 (comment))


commit 2: Layering: Make IHDR non-throwing

Currently, IHDR is declared to return either a normal completion containing ~unused~ or a throw completion.

The only way that IHDR can throw is if

  • its call to SetDefaultGlobalBindings throws, i.e.
  • one of SetDefaultGlobalBindings' calls to DefinePropertyOrThrow throws, i.e.
  • the new realm's global object's [[DefineOwnProperty]] internal method throws or returns false.

I think this can't happen if the global object is the IHDR-default, so it can only happen if the host provides the global object.

So, looking at IHDR's callers:

(1)
The proposed ShadowRealm function invokes IHDR with no customizations, so the global object is the IHDR-default, so this invocation IHDR can't throw. (So even without this commit, ShadowRealm should invoke IHDR with the '!' prefix instead of '?'.)

(2)
The HTML spec invokes IHDR with customizations, so IHDR could (theoretically) throw depending on what HTML supplies as the global object. Looking at the things it does supply, I haven't been able to prove to myself that they cannot possibly cause IHDR to throw, but it's clear that the HTML spec ignores the possibility that IHDR might throw, which suggests that no-one expects it to throw.

In any event, it seems like it would be self-sabotage for a host to supply a global object that could cause IHDR to throw.

So it seems reasonable for IHDR to become non-throwing, requiring that if the host provides the global object, it must be such that no abrupt completions occur.

It seemed cleanest to me to also make SetDefaultGlobalBindings non-throwing.

(@michaelficarra suggested this idea in #3497 (comment))


commit 3: Editorial: Create the realm execution context slightly later

Currently, IHDR creates+initializes the realm execution context in the middle of initializing the realm. Instead, do all the realm stuff, then do all the execution context stuff, just as a separation of concerns.

This is the same idea as in PR #3497, but here the context stuff can be right at the end of IHDR, because SetDefaultGlobalBindings is now non-throwing (assuming the previous commit).


commit 4: Layering: Return the realm EC from IHDR

Currently, IHDR creates an execution context (EC) for the new realm and pushes it onto the EC stack. Then IHDR's caller does (roughly):

1. Perform IHDR...
1. Let /realm execution context/ be the running execution context.
1. Remove /realm execution context/ from the execution context stack.

It's unnecessary to involve the EC stack (and the concept of the running EC). Just have IHDR return the realm EC. So then IHDR's caller would do:

1. Let /realm execution context/ be IHDR...

This is the same idea as PR #3274, but somewhat simpler given the above commits.

(It was the closure of that PR that prompted me to create this PR.)


commit 5: Layering: Rename IHDR as MakeRealm

To me, the name "InitializeHostDefinedRealm" suggests "the host has defined a realm, now this operation must initialize it", which is not very accurate.

Instead, "MakeRealm" seems appropriate.

The name CreateRealm would also work, except there used to be (before PR #3139 was merged) a CreateRealm operation that covered what's now the first 4 steps of IHDR, so that could possibly cause confusion (for very few people).

Renaming IHDR isn't necessary, of course, but given the changes of the preceding commits, it's a minor additional tweak.

@github-actions
Copy link

The rendered spec for this PR is available at https://tc39.es/ecma262/pr/3728.

<emu-clause id="sec-makerealm" type="abstract operation" oldids="sec-initializehostdefinedrealm,sec-createrealm,sec-setrealmglobalobject">
<h1>
MakeRealm (
_customizations_: an Abstract Closure that takes a Realm Record and returns a List of 2 elements, each of which is either an Object or *undefined*,
Copy link
Member

Choose a reason for hiding this comment

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

It might be slightly nicer for callers if they can omit the second return value (or return a single value or a list). Only window agents need this complexity, everything else has them be the same.

(Also, thanks for working on this, love to see this made more formal!)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In the HTML spec, I believe that would change 2 lines of the form

Return « <var>global</var>, undefined ».

to

Return « <var>global</var> ».

or

Return <var>global</var>.

I suppose it's slightly nicer, but I'm doubtful that it's worth the increased complexity for IHDR.

Copy link
Member

Choose a reason for hiding this comment

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

There's also Service Workers and probably everything else that wants to build on the JS specification that isn't HTML, but fair enough to not optimize for that as it's largely theoretical.

@michaelficarra michaelficarra added normative change Affects behavior required to correctly evaluate some ECMAScript source text layering affects the public spec interface and may require updates to integrating specs labels Nov 25, 2025
@jmdyck
Copy link
Collaborator Author

jmdyck commented Nov 26, 2025

There's an alternative to commit 1: rather than a single customizations parameter that returns a List of 2 values, IHDR could have 2 customization parameters, one for the global object and one for the global this binding. (Each would be either undefined or an abstract closure that returns an object.)

E.g., as it stands (with this PR), ShadowRealm() will have to do:

1. Let _customizations_ be an abstract closure that takes a Realm Record _realm_ and returns « *undefined*, *undefined* ».
1. ... IHDR(_customizations_).

But with the above alternative, it wouldn't have to bother with the AC:

1. ...IHDR(*undefined*, *undefined*).

Also, I think that would make it easier for HTML's "create a new realm" algo to make the global this binding customization optional, if that's desired.

@nicolo-ribaudo
Copy link
Member

With that second approach we could also make both parameters optional, rather than expecting *undefined* to be passed in.

@jmdyck
Copy link
Collaborator Author

jmdyck commented Nov 26, 2025

And another thing: in IHDR, after the invocation of SetDefaultGlobalBindings, there's the step:

1. Create any host-defined global object properties on _global_.

I'm wondering if that should be 'formalized' too, and if so how? I think the two choices (besides do nothing) are pass it in as a customization parameter or declare it as a host hook. In the HTML spec, do all realms have the same set of host-defined global object properties?

@jmdyck
Copy link
Collaborator Author

jmdyck commented Nov 26, 2025

With that second approach we could also make both parameters optional, rather than expecting *undefined* to be passed in.

Yeah, but as far as I can tell, there's exactly 2 calls to IHDR, one in the HTML spec and one in the ShadowRealms proposal. And that one call in ShadowRealm() is the only one that would be affected by making the IHDR params optional. So the benefit seems pretty small.

On the other hand, it's not much difference to IHDR, so I'd be willing if the editors like it.

@annevk
Copy link
Member

annevk commented Nov 26, 2025

I think in HTML all global object properties are taken care of by IDL. As in, they are taken care of when we allocate the object. Except for the deletion of SharedArrayBuffer as described by https://html.spec.whatwg.org/multipage/webappapis.html#creating-a-new-javascript-realm, but that's not creation... So maybe that isn't needed at all?

@jmdyck
Copy link
Collaborator Author

jmdyck commented Nov 26, 2025

I think in HTML all global object properties are taken care of by IDL.

Including the ones that IHDR sets via SetDefaultGlobalBindings? Or just the HTML-specific ones?

As in, they are taken care of when we allocate the object.

So they're already in place when IHDR first 'sees' the object.

So maybe that isn't needed at all?

I.e., maybe the "Create any host-defined global object properties on global" step isn't needed?

@annevk
Copy link
Member

annevk commented Nov 27, 2025

HTML only takes care of the HTML-specific ones (modulo sometimes deleting SharedArrayBuffer).

And yeah, I think step 17 is only needed if a host wanted an ordinary object as global with its own properties, but it might as well create its own ordinary object with its own properties during customizations to get there?

@jmdyck
Copy link
Collaborator Author

jmdyck commented Nov 27, 2025

And yeah, I think step 17 is only needed if a host wanted an ordinary object as global with its own properties, but it might as well create its own ordinary object with its own properties during customizations to get there?

Right.

So anyway, this makes me wonder about possible conflicts between properties on the host-supplied global and properties created in SetDefaultGlobalBindings. Should ES require that a host-supplied global object can't have any 'standard' properties?

More generally, hosts are allowed to "provide additional types, values, objects, properties, and functions beyond those described in this specification", but it's not entirely clear where (in the pseudo-code) this happens. For things connected to the global object, we can imagine they're created as part of creating the global object that the host supplies to IHDR (as @annevk described), but what about things not connected to the global object? (E.g., note that CreateIntrinsics creates only ES-standard objects -- there's no hook/callback/hand-waving to create non-standard intrinsics or non-standard properties on standard intrinsics.)

I'm not sure it's worth having the pseudo-code cover this, but it might be worth a Note saying that the layering here isn't so clear-cut -- there can be other non-standard stuff happening that isn't encompassed by the pseudo-code.

@annevk
Copy link
Member

annevk commented Nov 27, 2025

To some extent ES already requires that I think by running SetDefaultGlobalBindings (as you also observed in OP), but I wouldn't mind having additional requirements there. I would certainly consider it a mistake if the web platform introduced something that ended up replacing a built-in. (And vice versa. That kind of thing warrants coordination.)

@jmdyck
Copy link
Collaborator Author

jmdyck commented Nov 29, 2025

Currently (in this PR, but effectively in the status quo too), realm.[[GlobalObject]] is set when IHDR's invocation of customizations returns, but whatwg/webidl#1547 indicates that HTML can't wait until then. That is, the host should have the responsibility (or at least the option) of setting realm.[[GlobalObject]] itself.

This suggests a couple possibilities:

  • IHDR could still take an abstract closure parameter, but that closure wouldn't return anything. Instead, it has the option of setting realm.[[GlobalObject]] and realm.[[GlobalEnv]]. After it returns, IHDR can check if those are set, and if not, put in the defaults.
  • Alternatively, we could dispense with the abstract closure parameter, and instead split IHDR into two AOs, consisting of the steps before/after the customizations call. Then the host would do something like:
1. Let _realm_ be IHDR_before().
2. Use _realm_ to create objects to add to _realm_ (including _realm_.[[GlobalObject]]).
3. Perform IHDR_after(_realm_).

@annevk
Copy link
Member

annevk commented Dec 1, 2025

I think the former is a clearer API, but since hosts have to do this correctly to get a workable execution environment it might not matter too much which way we go.

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

Labels

layering affects the public spec interface and may require updates to integrating specs 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.

4 participants