⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 286069 in webkit


Ignore:
Timestamp:
Nov 19, 2021, 11:17:04 AM (5 years ago)
Author:
commit-queue@webkit.org
Message:

[JSC] Shadow realms: set correct Function prototype on wrapped functions
https://bugs.webkit.org/show_bug.cgi?id=233143

Patch by Joseph Griego <jgriego@igalia.com> on 2021-11-19
Reviewed by Yusuke Suzuki.

At present, the Function prototype set on each of the returned wrapped
functions will be the Function object from the realm the shadow realm
builtin is from--to comply with the latest draft of the shadow realms
spec [1], wrapped function objects should have the Function prototype
from the realm the wrapper object is destined for, instead.

At present, this requires tracking both the calling (destination) and
target (source) realm and switching between the two as function
arguments are wrapped (when the notion of source and destination realm
also flips)

Adds a simple builtin (moveFunctionToRealm) that can switch the Function
prototype given only the Shadow Realm object corresponding to the
correct global object.

Also marks the corresponding part of test262 as passing.

JSTests:

  • test262/expectations.yaml:

Source/JavaScriptCore:

[1] https://tc39.es/proposal-shadowrealm/ sections 2.1, 2.2

  • builtins/BuiltinNames.h:
  • builtins/ShadowRealmPrototype.js:

(wrapped):
(globalPrivate.wrap):
(evaluate):
(importValue):
(globalPrivate.wrap.wrapped): Deleted.

  • bytecode/LinkTimeConstant.h:
  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::init):

  • runtime/ShadowRealmPrototype.cpp:

(JSC::JSC_DEFINE_HOST_FUNCTION):

  • runtime/ShadowRealmPrototype.h:
Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r286060 r286069  
     12021-11-19  Joseph Griego  <jgriego@igalia.com>
     2
     3        [JSC] Shadow realms: set correct Function prototype on wrapped functions
     4        https://bugs.webkit.org/show_bug.cgi?id=233143
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        At present, the Function prototype set on each of the returned wrapped
     9        functions will be the Function object from the realm the shadow realm
     10        builtin is from--to comply with the latest draft of the shadow realms
     11        spec [1], wrapped function objects should have the Function prototype
     12        from the realm the wrapper object is destined for, instead.
     13
     14        At present, this requires tracking both the calling (destination) and
     15        target (source) realm and switching between the two as function
     16        arguments are wrapped (when the notion of source and destination realm
     17        also flips)
     18
     19        Adds a simple builtin (moveFunctionToRealm) that can switch the Function
     20        prototype given only the Shadow Realm object corresponding to the
     21        correct global object.
     22
     23        Also marks the corresponding part of test262 as passing.
     24
     25        * test262/expectations.yaml:
     26
    1272021-11-19  Angelos Oikonomopoulos  <angelos@igalia.com>
    228
  • trunk/JSTests/stress/shadow-realm-evaluate.js

    r284435 r286069  
    7878    shouldBe(wrappedInvokeAndAdd(() => { return 1 }, () => { return 2 }), 3);
    7979    shouldBe($.globalObjectFor(wrappedInvokeAndAdd), globalThis);
     80    shouldBe(Object.getPrototypeOf(wrappedInvokeAndAdd), Function.prototype);
    8081
    8182    // name and length properties from wrapped function are absent
     
    123124        shouldBe($.globalObjectFor(f), globalThis);
    124125        shouldBe(f(() => { return 41; }), 42);
     126        shouldBe(Object.getPrototypeOf(f), Function.prototype);
    125127    }
    126128    // (potential) inlining of wrapped function uses correct global object
     
    129131        shouldBe($.globalObjectFor(f), globalThis);
    130132        shouldBe(f(() => { return 41; }), 42);
     133        shouldBe(Object.getPrototypeOf(f), Function.prototype);
    131134    }
    132135    // (potential) inlining inside a realm uses correct global object
     
    152155    shouldBe(evaluateLength.configurable, true);
    153156}
     157
     158// Enclosing realm is hidden from shaodw realm even when playing Function prototype tricks
     159{
     160    let realm = new ShadowRealm();
     161    foo = 42;
     162
     163    realm.evaluate("foo = false");
     164
     165    let realmFn = realm.evaluate(`(f) => {
     166      let ourFn = Object.getPrototypeOf(f).constructor;
     167      return (new ourFn("return this"))().foo
     168    }`);
     169
     170    let retrievedFoo = realmFn(() => {});
     171    let aFunction = Object.getPrototypeOf(realmFn).constructor;
     172    let anotherFoo = (new aFunction("return this"))().foo;
     173
     174    shouldBe(retrievedFoo, false);
     175    shouldBe(anotherFoo, 42);
     176}
  • trunk/JSTests/test262/expectations.yaml

    r285760 r286069  
    11381138  default: 'SyntaxError: Invalid regular expression: number too large in {} quantifier'
    11391139  strict mode: 'SyntaxError: Invalid regular expression: number too large in {} quantifier'
    1140 test/built-ins/ShadowRealm/prototype/evaluate/wrapped-function-proto-from-caller-realm.js:
    1141   default: 'Test262Error: callable arguments passed into WrappedFunction should be wrapped in target realm Expected SameValue(«false», «true») to be true'
    1142   strict mode: 'Test262Error: callable arguments passed into WrappedFunction should be wrapped in target realm Expected SameValue(«false», «true») to be true'
    11431140test/built-ins/Temporal/Instant/prototype/toString/timezone-offset.js:
    11441141  default: 'Test262Error: offset of UTC is +00:00 Expected SameValue(«1970-01-01T00:00:00Z», «1970-01-01T00:00:00+00:00») to be true'
  • trunk/Source/JavaScriptCore/ChangeLog

    r286053 r286069  
     12021-11-19  Joseph Griego  <jgriego@igalia.com>
     2
     3        [JSC] Shadow realms: set correct Function prototype on wrapped functions
     4        https://bugs.webkit.org/show_bug.cgi?id=233143
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        At present, the Function prototype set on each of the returned wrapped
     9        functions will be the Function object from the realm the shadow realm
     10        builtin is from--to comply with the latest draft of the shadow realms
     11        spec [1], wrapped function objects should have the Function prototype
     12        from the realm the wrapper object is destined for, instead.
     13
     14        At present, this requires tracking both the calling (destination) and
     15        target (source) realm and switching between the two as function
     16        arguments are wrapped (when the notion of source and destination realm
     17        also flips)
     18
     19        Adds a simple builtin (moveFunctionToRealm) that can switch the Function
     20        prototype given only the Shadow Realm object corresponding to the
     21        correct global object.
     22
     23        Also marks the corresponding part of test262 as passing.
     24
     25        [1] https://tc39.es/proposal-shadowrealm/ sections 2.1, 2.2
     26
     27
     28
     29        * builtins/BuiltinNames.h:
     30        * builtins/ShadowRealmPrototype.js:
     31        (wrapped):
     32        (globalPrivate.wrap):
     33        (evaluate):
     34        (importValue):
     35        (globalPrivate.wrap.wrapped): Deleted.
     36        * bytecode/LinkTimeConstant.h:
     37        * runtime/JSGlobalObject.cpp:
     38        (JSC::JSGlobalObject::init):
     39        * runtime/ShadowRealmPrototype.cpp:
     40        (JSC::JSC_DEFINE_HOST_FUNCTION):
     41        * runtime/ShadowRealmPrototype.h:
     42
    1432021-11-19  Robin Morisset  <rmorisset@apple.com>
    244
  • trunk/Source/JavaScriptCore/builtins/BuiltinNames.h

    r284435 r286069  
    119119    macro(importInRealm) \
    120120    macro(evalInRealm) \
     121    macro(moveFunctionToRealm) \
    121122    macro(thisTimeValue) \
    122123    macro(newTargetLocal) \
  • trunk/Source/JavaScriptCore/builtins/ShadowRealmPrototype.js

    r284435 r286069  
    2424 */
    2525
     26// Wrap a value at the boundary between the incubating realm and `shadowRealm`:
     27// if `fromShadowRealm` is false, we are wrapping an object from the incubating
     28// realm; if true, we are wrapping an object from the shadow realm
    2629@globalPrivate
    27 function wrap(target)
     30function wrap(fromShadowRealm, shadowRealm, target)
    2831{
    2932    "use strict";
     
    3437            var wrappedArgs = @newArrayWithSize(length);
    3538            for (var index = 0; index < length; ++index)
    36                 @putByValDirect(wrappedArgs, index, @wrap(arguments[index]));
     39                // Note that for arguments, we flip `fromShadowRealm` since to
     40                // wrap a function from realm A to work in realm B, we need to
     41                // wrap the arguments (from realm B) to work in realm A before
     42                // calling the wrapped function
     43                @putByValDirect(wrappedArgs, index, @wrap(!fromShadowRealm, shadowRealm, arguments[index]));
    3744
    3845            var result = target.@apply(@undefined, wrappedArgs);
    39             return @wrap(result);
     46            return @wrap(fromShadowRealm, shadowRealm, result);
    4047        };
    4148        delete wrapped['name'];
    4249        delete wrapped['length'];
     50
     51        // Because this function (wrap) will run with the incubating realm
     52        // active, we only need to fix the prototype on `wrapped` if we are
     53        // moving the function from the incubating realm to the shadow realm
     54        if (!fromShadowRealm)
     55            @moveFunctionToRealm(wrapped, shadowRealm);
    4356        return wrapped;
    4457    } else if (@isObject(target)) {
     
    5972
    6073    var result = @evalInRealm(this, sourceText)
    61     return @wrap(result);
     74    return @wrap(true, this, result);
    6275}
    6376
     
    7790            @throwTypeError("%ShadowRealm%.importValue requires |exportName| to exist in the |specifier|");
    7891
    79         return @wrap(lookup);
     92        return @wrap(true, this, lookup);
    8093    };
    8194
  • trunk/Source/JavaScriptCore/bytecode/LinkTimeConstant.h

    r284435 r286069  
    8181    v(importInRealm, nullptr) \
    8282    v(evalInRealm, nullptr) \
     83    v(moveFunctionToRealm, nullptr) \
    8384    v(isConstructor, nullptr) \
    8485    v(sameValue, nullptr) \
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp

    r285730 r286069  
    14611461            init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 0, String(), evalInRealm));
    14621462        });
     1463    m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::moveFunctionToRealm)].initLater([] (const Initializer<JSCell>& init) {
     1464            init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 0, String(), moveFunctionToRealm));
     1465        });
    14631466    m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::thisTimeValue)].initLater([] (const Initializer<JSCell>& init) {
    14641467            init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 0, String(), dateProtoFuncGetTime, DatePrototypeGetTimeIntrinsic));
  • trunk/Source/JavaScriptCore/runtime/ShadowRealmPrototype.cpp

    r285195 r286069  
    125125}
    126126
     127JSC_DEFINE_HOST_FUNCTION(moveFunctionToRealm, (JSGlobalObject* globalObject, CallFrame* callFrame))
     128{
     129    VM& vm = globalObject->vm();
     130    auto scope = DECLARE_THROW_SCOPE(vm);
     131
     132    JSValue wrappedFnArg = callFrame->argument(0);
     133    JSFunction* wrappedFn = jsDynamicCast<JSFunction*>(vm, wrappedFnArg);
     134    JSValue targetRealmArg = callFrame->argument(1);
     135    ShadowRealmObject* targetRealm = jsDynamicCast<ShadowRealmObject*>(vm, targetRealmArg);
     136    ASSERT(targetRealm);
     137    RETURN_IF_EXCEPTION(scope, { });
     138
     139    bool isBuiltin = false;
     140    JSGlobalObject* targetGlobalObj = targetRealm->globalObject();
     141    wrappedFn->setPrototype(vm, targetGlobalObj, targetGlobalObj->strictFunctionStructure(isBuiltin)->storedPrototype());
     142    RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
     143}
     144
    127145} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/ShadowRealmPrototype.h

    r285730 r286069  
    6464JSC_DECLARE_HOST_FUNCTION(importInRealm);
    6565JSC_DECLARE_HOST_FUNCTION(evalInRealm);
     66JSC_DECLARE_HOST_FUNCTION(moveFunctionToRealm);
    6667
    6768} // namespace JSC
Note: See TracChangeset for help on using the changeset viewer.