Open Bug 1297179 Opened 5 years ago Updated 3 years ago

ES Proxies should use realm from caller context

Categories

(Core :: JavaScript Engine, defect, P3)

defect

Tracking

()

Tracking Status
firefox51 --- affected

People

(Reporter: anba, Unassigned)

References

(Blocks 1 open bug)

Details

(Keywords: triage-deferred)

Test case:
---
var g = newGlobal();
var {proxy, revoke} = g.eval(`Proxy.revocable(() => {}, {})`);

revoke();

var err;
try {
    proxy();
} catch (e) {
    err = e;
}

assertEq(err instanceof TypeError, true);
---

Expected: The assertEq call succeeds
Actual: The assertEq call fails

Proxy MOP methods don't create a new execution context, which means they simply reuse the current execution context, so step 2 in [1] throws a TypeError from the caller realm and not from the proxy's realm.

[1] https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist


It's even possible to extend the test case using bound functions:
---
var g = newGlobal();
var {proxy, revoke} = g.eval(`Proxy.revocable(() => {}, {})`);

var g2 = newGlobal();
var bf = g2.Function.prototype.bind.call(proxy);

revoke();

var err;
try {
    bf();
} catch (e) {
    err = e;
}

assertEq(err instanceof TypeError, true);
---

Same deal in this case: Proxy MOP methods don't create a new execution context, and neither do bound function objects [2]. So the thrown TypeError is also from the original caller realm. 

[2] https://tc39.github.io/ecma262/#sec-bound-function-exotic-objects-call-thisargument-argumentslist
Blocks: test262
I do not dispute that this is what the current spec says. But if so, then the spec implies (shudder) dynamic scoping and must be fixed. The execution context must be a "lexically" captured part of the thing executing, not an additional hidden implicit parameter passed to it by its caller. In such hidden unreifiable parameters, madness lies.
This is indeed and interesting case, but I think the current spec. is probably right and hence this is indeed a SpiderMonkey implementation bug.

First regarding "dynamic scoping". That's not the case if you consider that the runtime operation that is trying to operate upon the proxy object (really any object) is part of the calling function. The fact that the operation in this case is a function invocation is just obscuring that fact.  So, let's think about a simpler MOP operation such as [[Set]].  For a proxy, the first fhree steps of [[Set]] (or any MOP operation) are exactly the same as for [[Call]] and concludes with a conditional throw of a TypeError.  So,  what Realm is available for accessing TypeError when [[Set]] is performed using a revoked proxy? There is no longer a target object or a collection of trap handler functions that could potentially supply a Realm (or otherwise be used to create a new execution context).  The only realm available for looking up TypeError is the realm of the current execution context. (keep in mind that in the ES spec, only functon objects have a "lexical" realm association). 

Another way to look at this that gets us to the same point is the second paragraph of https://tc39.github.io/ecma262/#sec-object-internal-methods-and-internal-slots which says "If, at runtime, the implementation of an algorithm attempts to use an internal method of an object that the object does not support, a TypeError exception is thrown." Implicitly, this means the TypeError of the active execution context that is performing the abstract operation. A revoked proxy is essentially an object that does not support any internal methods so throwing TypeError from the realm of the current execution context is consistent with this requirement.
Keywords: triage-deferred
Priority: -- → P3
You need to log in before you can comment on or make changes to this bug.