JSTests:
[WebKit-https.git] / Source / JavaScriptCore / runtime / ProxyObject.cpp
index bbebf3a..8cafad5 100644 (file)
@@ -939,17 +939,30 @@ void ProxyObject::performGetOwnPropertyNames(ExecState* exec, PropertyNameArray&
     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;
 
-        if (!(type & resultFilter))
-            return dontExitEarly;
-
         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;
+
         uncheckedResultKeys.add(ident.impl());
         trapResult.addUnchecked(ident.impl());
         return dontExitEarly;