Title: [259800] trunk
Revision
259800
Author
shvaikal...@gmail.com
Date
2020-04-09 08:13:07 -0700 (Thu, 09 Apr 2020)

Log Message

getOwnPropertyDescriptor() is incorrect with Proxy of exotic object
https://bugs.webkit.org/show_bug.cgi?id=200560

Reviewed by Yusuke Suzuki.

JSTests:

* test262/expectations.yaml: Mark 14 test cases as passing.

Source/_javascript_Core:

PropertyAttribute::CustomValue path in JSObject::getOwnPropertyDescriptor() needs to perform
getDirect() on correct target. A correct target may be different since getOwnPropertySlot()
may return not *own* property.

This change removes a hack that was covering only JSProxy case and invokes getDirect() on
slotBase(), ensuring ProxyObject instances with exotic targets return correct descriptors
and aligning JSC with V8 and SpiderMonkey.

getDirect() can be safely called on slotBase(): if getOwnPropertySlot() result is returned
from JS code of ProxyObject's trap, it will never be a PropertyAttribute::CustomValue.

This patch also moves setCustomDescriptor() down below to avoid mutating a descriptor when
returning `false`.

* runtime/JSObject.cpp:
(JSC::JSObject::getOwnPropertyDescriptor):

LayoutTests:

* js/getOwnPropertyDescriptor-host-object-proxy-expected.txt: Added.
* js/getOwnPropertyDescriptor-host-object-proxy.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (259799 => 259800)


--- trunk/JSTests/ChangeLog	2020-04-09 14:48:31 UTC (rev 259799)
+++ trunk/JSTests/ChangeLog	2020-04-09 15:13:07 UTC (rev 259800)
@@ -1,3 +1,12 @@
+2020-04-09  Alexey Shvayka  <shvaikal...@gmail.com>
+
+        getOwnPropertyDescriptor() is incorrect with Proxy of exotic object
+        https://bugs.webkit.org/show_bug.cgi?id=200560
+
+        Reviewed by Yusuke Suzuki.
+
+        * test262/expectations.yaml: Mark 14 test cases as passing.
+
 2020-04-08  Yusuke Suzuki  <ysuz...@apple.com>
 
         [JSC] MultiDeleteByOffset should tell correct result AbstractValue in AI

Modified: trunk/JSTests/test262/expectations.yaml (259799 => 259800)


--- trunk/JSTests/test262/expectations.yaml	2020-04-09 14:48:31 UTC (rev 259799)
+++ trunk/JSTests/test262/expectations.yaml	2020-04-09 15:13:07 UTC (rev 259800)
@@ -672,9 +672,6 @@
 test/built-ins/Array/prototype/splice/clamps-length-to-integer-limit.js:
   default: 'Test262Error: Length is 2**53 - 1 Expected SameValue(«4294967295», «9007199254740991») to be true'
   strict mode: 'Test262Error: Length is 2**53 - 1 Expected SameValue(«4294967295», «9007199254740991») to be true'
-test/built-ins/Array/prototype/splice/create-proxy.js:
-  default: 'TypeError: Attempted to assign to readonly property.'
-  strict mode: 'TypeError: Attempted to assign to readonly property.'
 test/built-ins/Array/prototype/splice/create-species-length-exceeding-integer-limit.js:
   default: 'Test262Error: length and deleteCount were correctly clamped to 2^53-1 Expected SameValue(«4294967295», «9007199254740991») to be true'
   strict mode: 'Test262Error: length and deleteCount were correctly clamped to 2^53-1 Expected SameValue(«4294967295», «9007199254740991») to be true'
@@ -1255,15 +1252,6 @@
 test/built-ins/Proxy/defineProperty/trap-is-undefined-target-is-proxy.js:
   default: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
   strict mode: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
-test/built-ins/Proxy/getOwnPropertyDescriptor/trap-is-missing-target-is-proxy.js:
-  default: "TypeError: undefined is not an object (evaluating 'originalDesc.enumerable')"
-  strict mode: "TypeError: undefined is not an object (evaluating 'originalDesc.enumerable')"
-test/built-ins/Proxy/getOwnPropertyDescriptor/trap-is-null-target-is-proxy.js:
-  default: "TypeError: undefined is not an object (evaluating 'originalDesc.value')"
-  strict mode: "TypeError: undefined is not an object (evaluating 'originalDesc.value')"
-test/built-ins/Proxy/getOwnPropertyDescriptor/trap-is-undefined-target-is-proxy.js:
-  default: "TypeError: undefined is not an object (evaluating 'originalDesc.value')"
-  strict mode: "TypeError: undefined is not an object (evaluating 'originalDesc.value')"
 test/built-ins/Proxy/ownKeys/trap-is-undefined-target-is-proxy.js:
   default: 'Test262Error: Expected [length, foo, 0, Symbol()] and [Symbol(), length, foo, 0] to have the same contents. '
   strict mode: 'Test262Error: Expected [length, foo, 0, Symbol()] and [Symbol(), length, foo, 0] to have the same contents. '
@@ -1273,15 +1261,6 @@
 test/built-ins/Proxy/set/trap-is-missing-receiver-multiple-calls.js:
   default: 'Test262Error: Expected [foo] and [foo, foo, foo] to have the same contents. getOwnPropertyDescriptor: key present on [[ProxyTarget]]'
   strict mode: 'TypeError: Attempted to assign to readonly property.'
-test/built-ins/Proxy/set/trap-is-missing-target-is-proxy.js:
-  default: 'Test262Error: Expected SameValue(«0», «1») to be true'
-  strict mode: 'TypeError: Attempting to change configurable attribute of unconfigurable property.'
-test/built-ins/Proxy/set/trap-is-null-target-is-proxy.js:
-  default: 'Test262Error: Expected [1, 2, 3] and [] to have the same contents. '
-  strict mode: 'TypeError: Attempting to change configurable attribute of unconfigurable property.'
-test/built-ins/Proxy/set/trap-is-undefined-target-is-proxy.js:
-  default: 'Test262Error: Expected true but got false'
-  strict mode: 'Test262Error: Expected true but got false'
 test/built-ins/Reflect/ownKeys/order-after-define-property.js:
   default: 'Test262Error: Expected [Symbol(b), Symbol(a)] and [Symbol(a), Symbol(b)] to have the same contents. '
   strict mode: 'Test262Error: Expected [Symbol(b), Symbol(a)] and [Symbol(a), Symbol(b)] to have the same contents. '

Modified: trunk/LayoutTests/ChangeLog (259799 => 259800)


--- trunk/LayoutTests/ChangeLog	2020-04-09 14:48:31 UTC (rev 259799)
+++ trunk/LayoutTests/ChangeLog	2020-04-09 15:13:07 UTC (rev 259800)
@@ -1,3 +1,13 @@
+2020-04-09  Alexey Shvayka  <shvaikal...@gmail.com>
+
+        getOwnPropertyDescriptor() is incorrect with Proxy of exotic object
+        https://bugs.webkit.org/show_bug.cgi?id=200560
+
+        Reviewed by Yusuke Suzuki.
+
+        * js/getOwnPropertyDescriptor-host-object-proxy-expected.txt: Added.
+        * js/getOwnPropertyDescriptor-host-object-proxy.html: Added.
+
 2020-04-09  Diego Pino Garcia  <dp...@igalia.com>
 
         [GTK] Gardening, update TestExpectations

Added: trunk/LayoutTests/js/getOwnPropertyDescriptor-host-object-proxy-expected.txt (0 => 259800)


--- trunk/LayoutTests/js/getOwnPropertyDescriptor-host-object-proxy-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/getOwnPropertyDescriptor-host-object-proxy-expected.txt	2020-04-09 15:13:07 UTC (rev 259800)
@@ -0,0 +1,9 @@
+
+PASS Window target, no trap, "name" attribute 
+PASS Window target, forwarding trap, [Unforgeable] "document" attribute 
+PASS Window proxy target, custom trap, "onclick" event handler attribute 
+PASS Document target, forwarding trap, [Unforgeable] "location" attribute 
+PASS Document proxy target, custom trap, non-existent value attribute 
+PASS Location target, no trap, [Unforgeable] "hash" attribute 
+PASS Location proxy target, forwarding trap, [Unforgeable] "reload" method 
+

Added: trunk/LayoutTests/js/getOwnPropertyDescriptor-host-object-proxy.html (0 => 259800)


--- trunk/LayoutTests/js/getOwnPropertyDescriptor-host-object-proxy.html	                        (rev 0)
+++ trunk/LayoutTests/js/getOwnPropertyDescriptor-host-object-proxy.html	2020-04-09 15:13:07 UTC (rev 259800)
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>getOwnPropertyDescriptor() is correct for Proxy with host object target</title>
+  <script src=""
+  <script src=""
+</head>
+<body>
+<script>
+'use strict';
+
+test(() => {
+  let windowProxy = new Proxy(window, {});
+  name = 'old_name';
+  let descriptor = Object.getOwnPropertyDescriptor(windowProxy, 'name');
+
+  assert_equals(descriptor.get.call(window), 'old_name');
+  descriptor.set.call(window, 'new_name');
+  assert_equals(name, 'new_name');
+  assert_true(descriptor.enumerable);
+  assert_true(descriptor.configurable);
+}, 'Window target, no trap, "name" attribute');
+
+test(() => {
+  let windowProxy = new Proxy(window, {});
+
+  assert_object_equals(
+    Object.getOwnPropertyDescriptor(windowProxy, 'document'),
+    Object.getOwnPropertyDescriptor(window, 'document')
+  );
+}, 'Window target, forwarding trap, [Unforgeable] "document" attribute');
+
+test(() => {
+  let trapResult = {get() {}, set(_val) {}, enumerable: false, configurable: true};
+  let windowProxy = new Proxy(new Proxy(window, {}), {
+    getOwnPropertyDescriptor: () => trapResult,
+  });
+
+  assert_object_equals(
+    Object.getOwnPropertyDescriptor(windowProxy, 'onclick'),
+    trapResult
+  );
+}, 'Window proxy target, custom trap, "onclick" event handler attribute');
+
+test(() => {
+  let documentProxy = new Proxy(document, {
+    getOwnPropertyDescriptor: Reflect.getOwnPropertyDescriptor,
+  });
+
+  assert_object_equals(
+    Object.getOwnPropertyDescriptor(documentProxy, 'location'),
+    Object.getOwnPropertyDescriptor(document, 'location')
+  );
+}, 'Document target, forwarding trap, [Unforgeable] "location" attribute');
+
+test(() => {
+  let trapResult = {value: 4, writable: false, enumerable: true, configurable: true};
+  let documentProxy = new Proxy(new Proxy(document, {}), {
+    getOwnPropertyDescriptor: () => trapResult,
+  });
+
+  assert_object_equals(
+    Object.getOwnPropertyDescriptor(documentProxy, 'foo'),
+    trapResult
+  );
+}, 'Document proxy target, custom trap, non-existent value attribute');
+
+test(() => {
+  let locationProxy = new Proxy(location, {});
+  location.hash = '#old';
+  let descriptor = Object.getOwnPropertyDescriptor(locationProxy, 'hash');
+
+  assert_equals(descriptor.get.call(location), '#old');
+  descriptor.set.call(location, '#new');
+  assert_equals(location.hash, '#new');
+  assert_true(descriptor.enumerable);
+  assert_false(descriptor.configurable);
+}, 'Location target, no trap, [Unforgeable] "hash" attribute');
+
+test(() => {
+  let locationProxy = new Proxy(new Proxy(location, {}), {
+    getOwnPropertyDescriptor: Reflect.getOwnPropertyDescriptor,
+  });
+
+  assert_object_equals(
+    Object.getOwnPropertyDescriptor(locationProxy, 'reload'),
+    Object.getOwnPropertyDescriptor(location, 'reload')
+  );
+}, 'Location proxy target, forwarding trap, [Unforgeable] "reload" method');
+</script>
+</body>
+</html>

Modified: trunk/Source/_javascript_Core/ChangeLog (259799 => 259800)


--- trunk/Source/_javascript_Core/ChangeLog	2020-04-09 14:48:31 UTC (rev 259799)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-04-09 15:13:07 UTC (rev 259800)
@@ -1,3 +1,27 @@
+2020-04-09  Alexey Shvayka  <shvaikal...@gmail.com>
+
+        getOwnPropertyDescriptor() is incorrect with Proxy of exotic object
+        https://bugs.webkit.org/show_bug.cgi?id=200560
+
+        Reviewed by Yusuke Suzuki.
+
+        PropertyAttribute::CustomValue path in JSObject::getOwnPropertyDescriptor() needs to perform
+        getDirect() on correct target. A correct target may be different since getOwnPropertySlot()
+        may return not *own* property.
+
+        This change removes a hack that was covering only JSProxy case and invokes getDirect() on
+        slotBase(), ensuring ProxyObject instances with exotic targets return correct descriptors
+        and aligning JSC with V8 and SpiderMonkey.
+
+        getDirect() can be safely called on slotBase(): if getOwnPropertySlot() result is returned
+        from JS code of ProxyObject's trap, it will never be a PropertyAttribute::CustomValue.
+
+        This patch also moves setCustomDescriptor() down below to avoid mutating a descriptor when
+        returning `false`.
+
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::getOwnPropertyDescriptor):
+
 2020-04-09  Angelos Oikonomopoulos  <ange...@igalia.com>
 
         Fix CLOOP build

Modified: trunk/Source/_javascript_Core/runtime/JSObject.cpp (259799 => 259800)


--- trunk/Source/_javascript_Core/runtime/JSObject.cpp	2020-04-09 14:48:31 UTC (rev 259799)
+++ trunk/Source/_javascript_Core/runtime/JSObject.cpp	2020-04-09 15:13:07 UTC (rev 259800)
@@ -3479,7 +3479,7 @@
 {
     VM& vm = globalObject->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
-    JSC::PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
+    PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
 
     bool result = methodTable(vm)->getOwnPropertySlot(this, globalObject, propertyName, slot);
     EXCEPTION_ASSERT(!scope.exception() || !result);
@@ -3486,37 +3486,16 @@
     if (!result)
         return false;
 
-
-    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=200560
-    // This breaks the assumption that getOwnPropertySlot should return "own" property.
-    // We should fix DebuggerScope, ProxyObject etc. to remove this.
-    //
-    // DebuggerScope::getOwnPropertySlot() (and possibly others) may return attributes from the prototype chain
-    // but getOwnPropertyDescriptor() should only work for 'own' properties so we exit early if we detect that
-    // the property is not an own property.
-    if (slot.slotBase() != this && slot.slotBase()) {
-        JSProxy* jsProxy = jsDynamicCast<JSProxy*>(vm, this);
-        if (!jsProxy || jsProxy->target() != slot.slotBase()) {
-            // Try ProxyObject.
-            ProxyObject* proxyObject = jsDynamicCast<ProxyObject*>(vm, this);
-            if (!proxyObject || proxyObject->target() != slot.slotBase())
-                return false;
-        }
-    }
-
     if (slot.isAccessor())
         descriptor.setAccessorDescriptor(slot.getterSetter(), slot.attributes());
     else if (slot.attributes() & PropertyAttribute::CustomAccessor) {
-        descriptor.setCustomDescriptor(slot.attributes());
-
-        JSObject* thisObject = this;
-        if (auto* proxy = jsDynamicCast<JSProxy*>(vm, this))
-            thisObject = proxy->target();
-
         CustomGetterSetter* getterSetter;
         if (slot.isCustomAccessor())
             getterSetter = slot.customGetterSetter();
         else {
+            ASSERT(slot.slotBase());
+            JSObject* thisObject = slot.slotBase();
+
             JSValue maybeGetterSetter = thisObject->getDirect(vm, propertyName);
             if (!maybeGetterSetter) {
                 thisObject->reifyAllStaticProperties(globalObject);
@@ -3530,6 +3509,7 @@
         if (!getterSetter)
             return false;
 
+        descriptor.setCustomDescriptor(slot.attributes());
         if (getterSetter->getter())
             descriptor.setGetter(getCustomGetterSetterFunctionForGetterSetter(globalObject, propertyName, getterSetter, JSCustomGetterSetterFunction::Type::Getter));
         if (getterSetter->setter())
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to