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

Current Realm record when creating new objects in [[Construct]] #1462

Open
anba opened this issue Mar 1, 2019 · 0 comments
Open

Current Realm record when creating new objects in [[Construct]] #1462

anba opened this issue Mar 1, 2019 · 0 comments

Comments

@anba
Copy link
Contributor

anba commented Mar 1, 2019

The current Realm record in 9.2.2 [[Construct]], step 5 is the Realm of the caller execution context. This has been the way since ES1, which implemented in [[Construct]] in terms of [[Call]]. When ES6 added the notion of different Realms, this part of the spec wasn't updated to handle the case when the caller and callee Realms are different, but I can't tell if no update happened on purpose or if this was just an oversight. (Construct semantics changed multiple times during ES6, so it's hard for me to tell for sure).

This is for example visible when OrdinaryCreateFromConstructor throws an error, because the error has to be created in the caller Realm. Contrary to that, built-in functions always use the callee Realm:

var otherGlobal;
if (typeof newGlobal === "function") {
    otherGlobal = newGlobal();
} else if (typeof createGlobalObject === "function") {
    otherGlobal = createGlobalObject();
} else if (typeof Realm !== "undefined") {
    otherGlobal = Realm.global(Realm.createAllowCrossRealmAccess());
} else if (typeof WScript !== "undefined") {
    otherGlobal = WScript.LoadScript("this", "samethread");
} else {
    throw new Error("can't create global");
}

// Should print "caller" per current spec.
var f = otherGlobal.eval(`(function(){})`);
var {proxy, revoke} = Proxy.revocable(function(){}, {});
revoke();
try {
    Reflect.construct(f, [], proxy);
} catch (e) {
    print(e instanceof TypeError ? "caller" : "callee");
}

// Should print "callee" per current spec.
try {
    Reflect.construct(otherGlobal.Array, [], proxy);
} catch (e) {
    print(e instanceof TypeError ? "caller" : "callee");
}

And this also means the object created in 9.2.2 [[Construct]] has to be created in the caller Realm. This is right now not observable, but IIRC some new proposals like to assign a Realm to each object (and not only function objects), so it could be an issue in the future.

// Should print "caller".
if (typeof newGlobal === "function") {
    let thisGlobal = this;
    let otherGlobal = newGlobal();
    let o = new (otherGlobal.eval(`(function(){})`));
    print(objectGlobal(o) === thisGlobal ? "caller" : objectGlobal(o) === otherGlobal ? "callee" : "unknown");
} else if (typeof Realm !== "undefined") {
    let thisRealm = Realm.current();
    let otherRealm = Realm.createAllowCrossRealmAccess();
    let o = new (Realm.global(otherRealm).eval(`(function(){})`));
    print(Realm.owner(o) === thisRealm ? "caller" : Realm.owner(o) === otherRealm ? "callee" : "unknown");
} else {
    throw new Error("can't retrieve object realm");
}

Currently only V8 passes the first test case (SM/JSC print "callee" twice, Chakra prints "caller" twice). And neither SpiderMonkey nor V8 pass the second test case. (I couldn't test it on JSC/Chakra, because I didn't find any shell builtins to retrieve the Realm/Global of an object).

So the question is, does it make sense to move OrdinaryCreateFromConstructor after PrepareForOrdinaryCall or do we want to keep things as is?

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

No branches or pull requests

1 participant