Title: [286069] trunk
Revision
286069
Author
[email protected]
Date
2021-11-19 11:17:04 -0800 (Fri, 19 Nov 2021)

Log Message

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

Patch by Joseph Griego <[email protected]> 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/_javascript_Core:

[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:

Modified Paths

Diff

Modified: trunk/JSTests/ChangeLog (286068 => 286069)


--- trunk/JSTests/ChangeLog	2021-11-19 19:05:53 UTC (rev 286068)
+++ trunk/JSTests/ChangeLog	2021-11-19 19:17:04 UTC (rev 286069)
@@ -1,3 +1,29 @@
+2021-11-19  Joseph Griego  <[email protected]>
+
+        [JSC] Shadow realms: set correct Function prototype on wrapped functions
+        https://bugs.webkit.org/show_bug.cgi?id=233143
+
+        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.
+
+        * test262/expectations.yaml:
+
 2021-11-19  Angelos Oikonomopoulos  <[email protected]>
 
         [JSC] Workaround for failing mips tests

Modified: trunk/JSTests/stress/shadow-realm-evaluate.js (286068 => 286069)


--- trunk/JSTests/stress/shadow-realm-evaluate.js	2021-11-19 19:05:53 UTC (rev 286068)
+++ trunk/JSTests/stress/shadow-realm-evaluate.js	2021-11-19 19:17:04 UTC (rev 286069)
@@ -77,6 +77,7 @@
     let wrappedInvokeAndAdd = realm.evaluate("function invokeAndAdd(xFn, yFn) { return xFn() + yFn(); }; invokeAndAdd");
     shouldBe(wrappedInvokeAndAdd(() => { return 1 }, () => { return 2 }), 3);
     shouldBe($.globalObjectFor(wrappedInvokeAndAdd), globalThis);
+    shouldBe(Object.getPrototypeOf(wrappedInvokeAndAdd), Function.prototype);
 
     // name and length properties from wrapped function are absent
     shouldBe(Object.getOwnPropertyDescriptor(wrappedInvokeAndAdd, "length"), undefined);
@@ -122,6 +123,7 @@
         let f = doEval(realm, '(x) => { return x() + globalThis.secret; }');
         shouldBe($.globalObjectFor(f), globalThis);
         shouldBe(f(() => { return 41; }), 42);
+        shouldBe(Object.getPrototypeOf(f), Function.prototype);
     }
     // (potential) inlining of wrapped function uses correct global object
     let f = doEval(realm, '(x) => { return x() + globalThis.secret; }');
@@ -128,6 +130,7 @@
     for (var i = 0; i < 10000; ++i) {
         shouldBe($.globalObjectFor(f), globalThis);
         shouldBe(f(() => { return 41; }), 42);
+        shouldBe(Object.getPrototypeOf(f), Function.prototype);
     }
     // (potential) inlining inside a realm uses correct global object
     let loopInside = doEval(realm, '(x) => { let acc = 0; for (var i = 0; i < 10000; ++i) { acc += x(); }; return acc; }');
@@ -151,3 +154,23 @@
     shouldBe(evaluateLength.writable, false);
     shouldBe(evaluateLength.configurable, true);
 }
+
+// Enclosing realm is hidden from shaodw realm even when playing Function prototype tricks
+{
+    let realm = new ShadowRealm();
+    foo = 42;
+
+    realm.evaluate("foo = false");
+
+    let realmFn = realm.evaluate(`(f) => {
+      let ourFn = Object.getPrototypeOf(f).constructor;
+      return (new ourFn("return this"))().foo
+    }`);
+
+    let retrievedFoo = realmFn(() => {});
+    let aFunction = Object.getPrototypeOf(realmFn).constructor;
+    let anotherFoo = (new aFunction("return this"))().foo;
+
+    shouldBe(retrievedFoo, false);
+    shouldBe(anotherFoo, 42);
+}

Modified: trunk/JSTests/test262/expectations.yaml (286068 => 286069)


--- trunk/JSTests/test262/expectations.yaml	2021-11-19 19:05:53 UTC (rev 286068)
+++ trunk/JSTests/test262/expectations.yaml	2021-11-19 19:17:04 UTC (rev 286069)
@@ -1137,9 +1137,6 @@
 test/built-ins/RegExp/quantifier-integer-limit.js:
   default: 'SyntaxError: Invalid regular _expression_: number too large in {} quantifier'
   strict mode: 'SyntaxError: Invalid regular _expression_: number too large in {} quantifier'
-test/built-ins/ShadowRealm/prototype/evaluate/wrapped-function-proto-from-caller-realm.js:
-  default: 'Test262Error: callable arguments passed into WrappedFunction should be wrapped in target realm Expected SameValue(«false», «true») to be true'
-  strict mode: 'Test262Error: callable arguments passed into WrappedFunction should be wrapped in target realm Expected SameValue(«false», «true») to be true'
 test/built-ins/Temporal/Instant/prototype/toString/timezone-offset.js:
   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'
   strict mode: 'Test262Error: offset of UTC is +00:00 Expected SameValue(«1970-01-01T00:00:00Z», «1970-01-01T00:00:00+00:00») to be true'

Modified: trunk/Source/_javascript_Core/ChangeLog (286068 => 286069)


--- trunk/Source/_javascript_Core/ChangeLog	2021-11-19 19:05:53 UTC (rev 286068)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-11-19 19:17:04 UTC (rev 286069)
@@ -1,3 +1,45 @@
+2021-11-19  Joseph Griego  <[email protected]>
+
+        [JSC] Shadow realms: set correct Function prototype on wrapped functions
+        https://bugs.webkit.org/show_bug.cgi?id=233143
+
+        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.
+
+        [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:
+
 2021-11-19  Robin Morisset  <[email protected]>
 
         AirFixObviousSpills should be optimized

Modified: trunk/Source/_javascript_Core/builtins/BuiltinNames.h (286068 => 286069)


--- trunk/Source/_javascript_Core/builtins/BuiltinNames.h	2021-11-19 19:05:53 UTC (rev 286068)
+++ trunk/Source/_javascript_Core/builtins/BuiltinNames.h	2021-11-19 19:17:04 UTC (rev 286069)
@@ -118,6 +118,7 @@
     macro(this) \
     macro(importInRealm) \
     macro(evalInRealm) \
+    macro(moveFunctionToRealm) \
     macro(thisTimeValue) \
     macro(newTargetLocal) \
     macro(derivedConstructor) \

Modified: trunk/Source/_javascript_Core/builtins/ShadowRealmPrototype.js (286068 => 286069)


--- trunk/Source/_javascript_Core/builtins/ShadowRealmPrototype.js	2021-11-19 19:05:53 UTC (rev 286068)
+++ trunk/Source/_javascript_Core/builtins/ShadowRealmPrototype.js	2021-11-19 19:17:04 UTC (rev 286069)
@@ -23,8 +23,11 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+// Wrap a value at the boundary between the incubating realm and `shadowRealm`:
+// if `fromShadowRealm` is false, we are wrapping an object from the incubating
+// realm; if true, we are wrapping an object from the shadow realm
 @globalPrivate
-function wrap(target)
+function wrap(fromShadowRealm, shadowRealm, target)
 {
     "use strict";
 
@@ -33,13 +36,23 @@
             var length = arguments.length;
             var wrappedArgs = @newArrayWithSize(length);
             for (var index = 0; index < length; ++index)
-                @putByValDirect(wrappedArgs, index, @wrap(arguments[index]));
+                // Note that for arguments, we flip `fromShadowRealm` since to
+                // wrap a function from realm A to work in realm B, we need to
+                // wrap the arguments (from realm B) to work in realm A before
+                // calling the wrapped function
+                @putByValDirect(wrappedArgs, index, @wrap(!fromShadowRealm, shadowRealm, arguments[index]));
 
             var result = target.@apply(@undefined, wrappedArgs);
-            return @wrap(result);
+            return @wrap(fromShadowRealm, shadowRealm, result);
         };
         delete wrapped['name'];
         delete wrapped['length'];
+
+        // Because this function (wrap) will run with the incubating realm
+        // active, we only need to fix the prototype on `wrapped` if we are
+        // moving the function from the incubating realm to the shadow realm
+        if (!fromShadowRealm)
+            @moveFunctionToRealm(wrapped, shadowRealm);
         return wrapped;
     } else if (@isObject(target)) {
         @throwTypeError("value passing between realms must be callable or primitive");
@@ -58,7 +71,7 @@
         @throwTypeError("`%ShadowRealm%.evaluate requires that the |sourceText| argument be a string");
 
     var result = @evalInRealm(this, sourceText)
-    return @wrap(result);
+    return @wrap(true, this, result);
 }
 
 function importValue(specifier, exportName)
@@ -76,7 +89,7 @@
         if (lookup === @undefined)
             @throwTypeError("%ShadowRealm%.importValue requires |exportName| to exist in the |specifier|");
 
-        return @wrap(lookup);
+        return @wrap(true, this, lookup);
     };
 
     var crossRealmThrow = (error) => {

Modified: trunk/Source/_javascript_Core/bytecode/LinkTimeConstant.h (286068 => 286069)


--- trunk/Source/_javascript_Core/bytecode/LinkTimeConstant.h	2021-11-19 19:05:53 UTC (rev 286068)
+++ trunk/Source/_javascript_Core/bytecode/LinkTimeConstant.h	2021-11-19 19:17:04 UTC (rev 286069)
@@ -80,6 +80,7 @@
     v(thisTimeValue, nullptr) \
     v(importInRealm, nullptr) \
     v(evalInRealm, nullptr) \
+    v(moveFunctionToRealm, nullptr) \
     v(isConstructor, nullptr) \
     v(sameValue, nullptr) \
     v(regExpProtoFlagsGetter, nullptr) \

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (286068 => 286069)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2021-11-19 19:05:53 UTC (rev 286068)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2021-11-19 19:17:04 UTC (rev 286069)
@@ -1460,6 +1460,9 @@
     m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::evalInRealm)].initLater([] (const Initializer<JSCell>& init) {
             init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 0, String(), evalInRealm));
         });
+    m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::moveFunctionToRealm)].initLater([] (const Initializer<JSCell>& init) {
+            init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 0, String(), moveFunctionToRealm));
+        });
     m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::thisTimeValue)].initLater([] (const Initializer<JSCell>& init) {
             init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 0, String(), dateProtoFuncGetTime, DatePrototypeGetTimeIntrinsic));
         });

Modified: trunk/Source/_javascript_Core/runtime/ShadowRealmPrototype.cpp (286068 => 286069)


--- trunk/Source/_javascript_Core/runtime/ShadowRealmPrototype.cpp	2021-11-19 19:05:53 UTC (rev 286068)
+++ trunk/Source/_javascript_Core/runtime/ShadowRealmPrototype.cpp	2021-11-19 19:17:04 UTC (rev 286069)
@@ -124,4 +124,22 @@
     RELEASE_AND_RETURN(scope, JSValue::encode(result));
 }
 
+JSC_DEFINE_HOST_FUNCTION(moveFunctionToRealm, (JSGlobalObject* globalObject, CallFrame* callFrame))
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue wrappedFnArg = callFrame->argument(0);
+    JSFunction* wrappedFn = jsDynamicCast<JSFunction*>(vm, wrappedFnArg);
+    JSValue targetRealmArg = callFrame->argument(1);
+    ShadowRealmObject* targetRealm = jsDynamicCast<ShadowRealmObject*>(vm, targetRealmArg);
+    ASSERT(targetRealm);
+    RETURN_IF_EXCEPTION(scope, { });
+
+    bool isBuiltin = false;
+    JSGlobalObject* targetGlobalObj = targetRealm->globalObject();
+    wrappedFn->setPrototype(vm, targetGlobalObj, targetGlobalObj->strictFunctionStructure(isBuiltin)->storedPrototype());
+    RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
+}
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/ShadowRealmPrototype.h (286068 => 286069)


--- trunk/Source/_javascript_Core/runtime/ShadowRealmPrototype.h	2021-11-19 19:05:53 UTC (rev 286068)
+++ trunk/Source/_javascript_Core/runtime/ShadowRealmPrototype.h	2021-11-19 19:17:04 UTC (rev 286069)
@@ -63,5 +63,6 @@
 
 JSC_DECLARE_HOST_FUNCTION(importInRealm);
 JSC_DECLARE_HOST_FUNCTION(evalInRealm);
+JSC_DECLARE_HOST_FUNCTION(moveFunctionToRealm);
 
 } // namespace JSC
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to