Title: [243933] trunk
Revision
243933
Author
ca...@igalia.com
Date
2019-04-05 06:45:08 -0700 (Fri, 05 Apr 2019)

Log Message

JSTests:
[JSC] throw if 'ownKeys' Proxy trap result contains duplicate keys
https://bugs.webkit.org/show_bug.cgi?id=185211

Reviewed by Saam Barati.

This is for the normative spec change in https://github.com/tc39/ecma262/pull/833

This changes several assertions to expect a TypeError to be thrown (in some cases,
changing thee expected message).

* es6/Proxy_ownKeys_duplicates.js:
(handler):
(shouldThrow):
(test):
* stress/Object_static_methods_Object.getOwnPropertyDescriptors-proxy.js:
(shouldThrow):
* stress/proxy-own-keys.js:
(i.catch):
(assert):

LayoutTests/imported/w3c:
[JSC] throw if 'ownKeys' Proxy trap result contains duplicate keys
https://bugs.webkit.org/show_bug.cgi?id=185211

Reviewed by Saam Barati.

This is for the normative spec change in https://github.com/tc39/ecma262/pull/833

Change some test expectations which were previously expected to fail.

* web-platform-tests/fetch/api/headers/headers-record-expected.txt:

Source/_javascript_Core:
[JSC] throw if ownKeys Proxy trap result contains duplicate keys
https://bugs.webkit.org/show_bug.cgi?id=185211

Reviewed by Saam Barati.

Implements the normative spec change in https://github.com/tc39/ecma262/pull/833

This involves tracking duplicate keys returned from the ownKeys trap in yet
another HashTable, and may incur a minor performance penalty in some cases. This
is not expected to significantly affect web performance.

* runtime/ProxyObject.cpp:
(JSC::ProxyObject::performGetOwnPropertyNames):

Modified Paths

Diff

Modified: trunk/JSTests/ChangeLog (243932 => 243933)


--- trunk/JSTests/ChangeLog	2019-04-05 12:42:44 UTC (rev 243932)
+++ trunk/JSTests/ChangeLog	2019-04-05 13:45:08 UTC (rev 243933)
@@ -1,3 +1,25 @@
+2019-04-05  Caitlin Potter  <ca...@igalia.com>
+
+        [JSC] throw if 'ownKeys' Proxy trap result contains duplicate keys
+        https://bugs.webkit.org/show_bug.cgi?id=185211
+
+        Reviewed by Saam Barati.
+
+        This is for the normative spec change in https://github.com/tc39/ecma262/pull/833
+
+        This changes several assertions to expect a TypeError to be thrown (in some cases,
+        changing thee expected message).
+
+        * es6/Proxy_ownKeys_duplicates.js:
+        (handler):
+        (shouldThrow):
+        (test):
+        * stress/Object_static_methods_Object.getOwnPropertyDescriptors-proxy.js:
+        (shouldThrow):
+        * stress/proxy-own-keys.js:
+        (i.catch):
+        (assert):
+
 2019-04-04  Yusuke Suzuki  <ysuz...@apple.com>
 
         [JSC] makeBoundFunction should not assume incoming "length" value is Int32 because it performs some calculation in bytecode

Modified: trunk/JSTests/es6/Proxy_ownKeys_duplicates.js (243932 => 243933)


--- trunk/JSTests/es6/Proxy_ownKeys_duplicates.js	2019-04-05 12:42:44 UTC (rev 243932)
+++ trunk/JSTests/es6/Proxy_ownKeys_duplicates.js	2019-04-05 13:45:08 UTC (rev 243933)
@@ -1,30 +1,51 @@
+function handler(key) {
+    return {
+        getOwnPropertyDescriptor(t, n) {
+            // Required to prevent Object.keys() from discarding results
+            return {
+                enumerable: true,
+                configurable: true,
+            };
+        },
+        ownKeys(t) {
+            return [key, key];
+        }
+    };
+}
+
+function shouldThrow(op, errorConstructor, desc) {
+    try {
+        op();
+    } catch (e) {
+        if (!(e instanceof errorConstructor)) {
+            throw new Error(`threw ${e}, but should have thrown ${errorConstructor.name}`);
+        }
+        return;
+    }
+    throw new Error(`Expected ${desc || 'operation'} to throw ${errorConstructor.name}, but no exception thrown`);
+}
+
 function test() {
 
 var symbol = Symbol("test");
-var proxy = new Proxy({}, {
-    getOwnPropertyDescriptor(t, n) {
-        // Required to prevent Object.keys() from discarding results
-        return {
-            enumerable: true,
-            configurable: true
-        };
-    },
-    ownKeys: function (t) {
-        return ["A", "A", "0", "0", symbol, symbol];
-    }
-});
-var keys = Object.keys(proxy);
-var names = Object.getOwnPropertyNames(proxy);
-var symbols = Object.getOwnPropertySymbols(proxy);
+var proxyNamed = new Proxy({}, handler("A"));
+var proxyIndexed = new Proxy({}, handler(0));
+var proxySymbol = new Proxy({}, handler(symbol));
 
-if (keys.length === 4 && keys[0] === keys[1] && keys[2] === keys[3] &&
-    keys[0] === "A" && keys[2] === "0" &&
-    names.length === 4 && names[0] === names[1] && names[2] === names[3] &&
-    names[0] === "A" && names[2] === "0" &&
-    symbols.length === 2 && symbols[0] === symbols[1] && symbols[0] === symbol)
-    return true;
-return false;
+shouldThrow(() => Object.keys(proxyNamed), TypeError, "Object.keys with duplicate named properties");
+shouldThrow(() => Object.keys(proxyIndexed), TypeError, "Object.keys with duplicate indexed properties");
+shouldThrow(() => Object.keys(proxySymbol), TypeError, "Object.keys with duplicate symbol properties");
 
+shouldThrow(() => Object.getOwnPropertyNames(proxyNamed), TypeError, "Object.getOwnPropertyNames with duplicate named properties");
+shouldThrow(() => Object.getOwnPropertyNames(proxyIndexed), TypeError, "Object.getOwnPropertyNames with duplicate indexed properties");
+shouldThrow(() => Object.getOwnPropertyNames(proxySymbol), TypeError, "Object.getOwnPropertyNames with duplicate symbol properties");
+
+shouldThrow(() => Object.getOwnPropertySymbols(proxyNamed), TypeError, "Object.getOwnPropertySymbols with duplicate named properties");
+shouldThrow(() => Object.getOwnPropertySymbols(proxyIndexed), TypeError, "Object.getOwnPropertySymbols with duplicate indexed properties");
+shouldThrow(() => Object.getOwnPropertySymbols(proxySymbol), TypeError, "Object.getOwnPropertySymbols with duplicate symbol properties");
+
+return true;
+
 }
 
 if (!test())

Modified: trunk/JSTests/stress/Object_static_methods_Object.getOwnPropertyDescriptors-proxy.js (243932 => 243933)


--- trunk/JSTests/stress/Object_static_methods_Object.getOwnPropertyDescriptors-proxy.js	2019-04-05 12:42:44 UTC (rev 243932)
+++ trunk/JSTests/stress/Object_static_methods_Object.getOwnPropertyDescriptors-proxy.js	2019-04-05 13:45:08 UTC (rev 243933)
@@ -18,6 +18,17 @@
     shouldBe(undefined, expected.set, name + '.set');
 }
 
+function shouldThrow(op, errorConstructor, desc) {
+    try {
+        op();
+        throw new Error(`Expected ${desc || 'operation'} to throw ${errorConstructor.name}, but no exception thrown`);
+    } catch (e) {
+        if (!(e instanceof errorConstructor)) {
+            throw new Error(`threw ${e}, but should have thrown ${errorConstructor.name}`);
+        }
+    }
+}
+
 (function testPropertyFilteringAndOrder() {
     var log = [];
     var sym = Symbol('test');
@@ -80,13 +91,8 @@
     defineProperty() { throw new Error('[[DefineOwnProperty]] trap should be unreachable'); }
   });
 
-  var result = Object.getOwnPropertyDescriptors(P);
-  shouldBe(true, result.A.configurable, 'for result.A.configurable');
-  shouldBe(false, result.A.writable, 'for result.A.writable');
-  shouldBe('VALUE', result.A.value, 'for result.A.value');
-  shouldBe(false, result.A.enumerable, 'for result.A.enumerable');
-  shouldBe(true, Object.hasOwnProperty.call(result, 'A'));
-  shouldBe('ownKeys()|getOwnPropertyDescriptor(A)|getOwnPropertyDescriptor(A)', log.join('|'));
+  shouldThrow(() => Object.getOwnPropertyDescriptors(P), TypeError, 'ownKeys returning duplicates');
+  shouldBe('ownKeys()', log.join('|'));
 })();
 
 (function testUndefinedPropertyDescriptor() {

Modified: trunk/JSTests/stress/proxy-own-keys.js (243932 => 243933)


--- trunk/JSTests/stress/proxy-own-keys.js	2019-04-05 12:42:44 UTC (rev 243932)
+++ trunk/JSTests/stress/proxy-own-keys.js	2019-04-05 13:45:08 UTC (rev 243933)
@@ -187,13 +187,12 @@
     });
 
     for (let i = 0; i < 500; i++) {
-        // FIXME: we may update the spec to make this test not throw.
-        // see: https://github.com/tc39/ecma262/pull/594
+        // Throws per https://github.com/tc39/ecma262/pull/833
         let threw = false;
         try {
             Reflect.ownKeys(p2);
         } catch(e) {
-            assert(e.toString() === "TypeError: Proxy object's 'target' has the non-configurable property 'a' that was not in the result from the 'ownKeys' trap");
+            assert(e.toString() === "TypeError: Proxy handler's 'ownKeys' trap result must not contain any duplicate names");
             threw = true;
         }
         assert(threw);
@@ -222,13 +221,12 @@
     });
 
     for (let i = 0; i < 500; i++) {
-        // FIXME: we may update the spec to make this test not throw.
-        // see: https://github.com/tc39/ecma262/pull/594
+        // Throws per https://github.com/tc39/ecma262/pull/833
         let threw = false;
         try {
             Reflect.ownKeys(p2);
         } catch(e) {
-            assert(e.toString() === "TypeError: Proxy object's non-extensible 'target' has configurable property 'a' that was not in the result from the 'ownKeys' trap");
+            assert(e.toString() === "TypeError: Proxy handler's 'ownKeys' trap result must not contain any duplicate names");
             threw = true;
         }
         assert(threw);
@@ -255,9 +253,16 @@
 
     let proxy = new Proxy(target, handler);
     for (let i = 0; i < 500; i++) {
-        Object.keys(proxy);
+        try {
+            Object.keys(proxy);
+        } catch(e) {
+            assert(e.toString() === "TypeError: Proxy handler's 'ownKeys' trap result must not contain any duplicate names");
+            threw = true;
+        }
         assert(called);
+        assert(threw);
         called = false;
+        threw = false;
     }
 }
 

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (243932 => 243933)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2019-04-05 12:42:44 UTC (rev 243932)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2019-04-05 13:45:08 UTC (rev 243933)
@@ -1,3 +1,16 @@
+2019-04-05  Caitlin Potter  <ca...@igalia.com>
+
+        [JSC] throw if 'ownKeys' Proxy trap result contains duplicate keys
+        https://bugs.webkit.org/show_bug.cgi?id=185211
+
+        Reviewed by Saam Barati.
+
+        This is for the normative spec change in https://github.com/tc39/ecma262/pull/833
+
+        Change some test expectations which were previously expected to fail.
+
+        * web-platform-tests/fetch/api/headers/headers-record-expected.txt:
+
 2019-04-04  Commit Queue  <commit-qu...@webkit.org>
 
         Unreviewed, rolling out r243807 and r243824.

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/headers/headers-record-expected.txt (243932 => 243933)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/headers/headers-record-expected.txt	2019-04-05 12:42:44 UTC (rev 243932)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/headers/headers-record-expected.txt	2019-04-05 13:45:08 UTC (rev 243933)
@@ -9,7 +9,7 @@
 PASS Correct operation ordering with two properties one of which has an invalid value 
 PASS Correct operation ordering with non-enumerable properties 
 PASS Correct operation ordering with undefined descriptors 
-FAIL Correct operation ordering with repeated keys assert_throws: function "function () { var h = new Headers(proxy); }" did not throw
+PASS Correct operation ordering with repeated keys 
 FAIL Basic operation with Symbol keys assert_throws: function "function () { var h = new Headers(proxy); }" did not throw
 FAIL Operation with non-enumerable Symbol keys assert_equals: expected 9 but got 8
 

Modified: trunk/Source/_javascript_Core/ChangeLog (243932 => 243933)


--- trunk/Source/_javascript_Core/ChangeLog	2019-04-05 12:42:44 UTC (rev 243932)
+++ trunk/Source/_javascript_Core/ChangeLog	2019-04-05 13:45:08 UTC (rev 243933)
@@ -1,3 +1,19 @@
+2019-04-05  Caitlin Potter  <ca...@igalia.com>
+
+        [JSC] throw if ownKeys Proxy trap result contains duplicate keys
+        https://bugs.webkit.org/show_bug.cgi?id=185211
+
+        Reviewed by Saam Barati.
+
+        Implements the normative spec change in https://github.com/tc39/ecma262/pull/833
+
+        This involves tracking duplicate keys returned from the ownKeys trap in yet
+        another HashTable, and may incur a minor performance penalty in some cases. This
+        is not expected to significantly affect web performance.
+
+        * runtime/ProxyObject.cpp:
+        (JSC::ProxyObject::performGetOwnPropertyNames):
+
 2019-04-04  Yusuke Suzuki  <ysuz...@apple.com>
 
         [JSC] makeBoundFunction should not assume incoming "length" value is Int32 because it performs some calculation in bytecode

Modified: trunk/Source/_javascript_Core/runtime/ProxyObject.cpp (243932 => 243933)


--- trunk/Source/_javascript_Core/runtime/ProxyObject.cpp	2019-04-05 12:42:44 UTC (rev 243932)
+++ trunk/Source/_javascript_Core/runtime/ProxyObject.cpp	2019-04-05 13:45:08 UTC (rev 243933)
@@ -939,17 +939,30 @@
     ASSERT(resultFilter);
     RuntimeTypeMask dontThrowAnExceptionTypeFilter = TypeString | TypeSymbol;
     HashSet<UniquedStringImpl*> uncheckedResultKeys;
+    HashSet<UniquedStringImpl*> seenKeys;
 
     auto addPropName = [&] (JSValue value, RuntimeType type) -> bool {
         static const bool doExitEarly = true;
         static const bool dontExitEarly = false;
 
+        Identifier ident = value.toPropertyKey(exec);
+        RETURN_IF_EXCEPTION(scope, doExitEarly);
+
+        // If trapResult contains any duplicate entries, throw a TypeError exception.
+        //    
+        // Per spec[1], filtering by type should occur _after_ [[OwnPropertyKeys]], so duplicates
+        // are tracked in a separate hashtable from uncheckedResultKeys (which only contain the
+        // keys filtered by type).
+        //
+        // [1] Per https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeysmust not contain any duplicate names"_s);
+        if (!seenKeys.add(ident.impl()).isNewEntry) {
+            throwTypeError(exec, scope, "Proxy handler's 'ownKeys' trap result must not contain any duplicate names"_s);
+            return doExitEarly;
+        }
+
         if (!(type & resultFilter))
             return dontExitEarly;
 
-        Identifier ident = value.toPropertyKey(exec);
-        RETURN_IF_EXCEPTION(scope, doExitEarly);
-
         uncheckedResultKeys.add(ident.impl());
         trapResult.addUnchecked(ident.impl());
         return dontExitEarly;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to