tc39 / proposal-shadowrealm

ECMAScript Proposal, specs, and reference implementation for Realms
https://tc39.es/proposal-shadowrealm/
1.43k stars 67 forks source link

Add WrappedFunction.[[Realm]] internal slot #329

Closed legendecas closed 3 years ago

legendecas commented 3 years ago

GetFunctionRealm in Step 4 of #sec-wrapped-function-exotic-objects-call-thisargument-argumentslist requires the function object to own an internal slot of [[Realm]] or the current execution realm record will be returned instead, which can differ from the creation realm.

leobalter commented 3 years ago

This is good and makes a good distinction from the wrapped functions vs the instances of shadowrealm. Thanks!

leobalter commented 3 years ago

@erights this doesn't look like dynamic scoping. I believe the change reflects what we are already doing today:

const srealm = new ShadowRealm();
const wrapped = srealm.evaluate('() => {}');

The wrapped function is created outside of the srealm. The value for srealm.[[Realm]] internal is the Realm Record where wrapped was created and it is visible.

erights commented 3 years ago

To what does the PR description's phrase

"or the current execution realm record will be returned instead, which can differ from the creation realm."

apply?

leobalter commented 3 years ago

Let me expand the example using the spec steps creating the wrapped function:

const realm = new ShadowRealm();
const wrapped = realm.evaluate('x => x * 2');

realm.evaluate will run the PerformShadowRealmEval:

PerformShadowRealmEval ( sourceText, callerRealm, evalRealm )

callerRealm is the realm where realm.evaluate is called, evalRealm is where the code inside the shadowRealm runs. We can roughly say that callerRealm is the same value in realm.evaluate.[[Realm]].

This abstraction will see the evaluation completion is a callable (x => x *2) and will wrap it with the following abstraction, and using the callerRealm:

WrappedFunctionCreate ( _callerRealm_, _targetFunction_ )

Assert: _callerRealm_ is a Realm Record.
Assert: IsCallable(_targetFunction_) is *true*.
 ...
Let _obj_ be ! MakeBasicObject(_internalSlotsList_).
Set _obj_.[[Prototype]] to _callerRealm_.[[Intrinsics]].[[%Function.prototype%]].
Set _obj_.[[Call]] as described in <emu-xref href="#sec-wrapped-function-exotic-objects-call-thisargument-argumentslist"></emu-xref>.
Set _obj_.[[WrappedTargetFunction]] to _targetFunction_.
Set _obj_.[[Realm]] to _callerRealm_.
...

This abstraction will create the wrapped function exotic that has the exotic [[Call]] and targets the completion x => x * 2 as the value for wrapped.[[WrappedTargetFunction]].

The callerRealm is set to the wrapped.[[Realm]], without touching the value of the wrapped.[[WrappedTargetFunction]].[[Realm]]. They are always pointing to different Realm Records.


This PR is important otherwise the wrapped function might be a hazard with dynamic scoping, i.e. execution of Promises and thenables.

and it seems like the same pointed out by @mhofman. I believe we're all on the same page here.

caridy commented 3 years ago

This is good! We had that at some point, on my original spec.

https://github.com/tc39/proposal-shadowrealm/commit/228026d4f307501244ffe8837ee29c6327385b31#diff-181371b08d71216599b0acccbaabd03c306da6de142ea6275c2135810999805aR67-R77

It was removed in https://github.com/tc39/proposal-shadowrealm/commit/33017f50e3d889622881e8efa7b6add1fc28571a

leobalter commented 3 years ago

I take the blame for removing [[Realm]] in an earlier iteration. I'll get this merged to end this issue.