Title: [217529] trunk
Revision
217529
Author
[email protected]
Date
2017-05-28 18:30:58 -0700 (Sun, 28 May 2017)

Log Message

[WebIDL] @@iterator should only be accessed once when disambiguating a union type
https://bugs.webkit.org/show_bug.cgi?id=172684

Patch by Sam Weinig <[email protected]> on 2017-05-28
Reviewed by Yusuke Suzuki.

Source/_javascript_Core:

* runtime/IteratorOperations.cpp:
(JSC::iteratorMethod):
(JSC::iteratorForIterable):
* runtime/IteratorOperations.h:
(JSC::forEachInIterable):
Add additional iterator helpers to allow union + sequence conversion code
to check for iterability by getting the iterator method, and iterate using
that method later on.

Source/WebCore:

WebIDL specifies that when determining if the value you are converting to a union
is a sequence, you must get the @@iterator property and, should it exist, use it
to iterate the sequence. While we correctly accessing the property to make the
determination, we were not passing it into the sequence conversion code, and thus
the sequence conversion code re-accessed it, which is observable and wrong.

This patch pipes the @@iterator method through the sequence conversion code to avoid
this.

Test: js/dom/sequence-in-union-iterator-access.html

* bindings/js/JSDOMConvertSequences.h:
(WebCore::Detail::GenericSequenceConverter::convert):
(WebCore::Detail::NumericSequenceConverter::convertArray):
(WebCore::Detail::NumericSequenceConverter::convert):
(WebCore::Detail::SequenceConverter::convertArray):
(WebCore::Detail::SequenceConverter::convert):
(WebCore::Detail::SequenceConverter<IDLLong>::convert):
(WebCore::Detail::SequenceConverter<IDLFloat>::convert):
(WebCore::Detail::SequenceConverter<IDLUnrestrictedFloat>::convert):
(WebCore::Detail::SequenceConverter<IDLDouble>::convert):
(WebCore::Detail::SequenceConverter<IDLUnrestrictedDouble>::convert):
(WebCore::Converter<IDLSequence<T>>::convert):
(WebCore::Converter<IDLFrozenArray<T>>::convert):
Add variants of convert that take a JSObject* (sequence) / JSValue (iterator method)
rather than just the JSValue (sequence). To avoid too much duplication, split some
parts of SequenceConverter and NumericSequenceConverter up so they could be reused.

* bindings/js/JSDOMConvertUnion.h:
- Fix incorrect step 3 (WebIDL got updated at some point and we didn't notice) to remove
  records.
- Update sequence and FrozenArray checking/conversion to get the iterator method and pass
  it along, using the new ConditionalSequenceConverter helper which forwards to the new
  sequence converters that accept the iterator method.

LayoutTests:

* js/dom/sequence-in-union-iterator-access-expected.txt: Added.
* js/dom/sequence-in-union-iterator-access.html: Added.
Add test case showing that @@iterator is only accessed once when converting a sequence
as part of a union.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (217528 => 217529)


--- trunk/LayoutTests/ChangeLog	2017-05-28 14:11:56 UTC (rev 217528)
+++ trunk/LayoutTests/ChangeLog	2017-05-29 01:30:58 UTC (rev 217529)
@@ -1,3 +1,15 @@
+2017-05-28  Sam Weinig  <[email protected]>
+
+        [WebIDL] @@iterator should only be accessed once when disambiguating a union type
+        https://bugs.webkit.org/show_bug.cgi?id=172684
+
+        Reviewed by Yusuke Suzuki.
+
+        * js/dom/sequence-in-union-iterator-access-expected.txt: Added.
+        * js/dom/sequence-in-union-iterator-access.html: Added.
+        Add test case showing that @@iterator is only accessed once when converting a sequence
+        as part of a union.
+
 2017-05-27  Chris Dumez  <[email protected]>
 
         imported/w3c/web-platform-tests/html/semantics/forms/form-control-infrastructure/form_attribute.html is crashing

Added: trunk/LayoutTests/js/dom/sequence-in-union-iterator-access-expected.txt (0 => 217529)


--- trunk/LayoutTests/js/dom/sequence-in-union-iterator-access-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/dom/sequence-in-union-iterator-access-expected.txt	2017-05-29 01:30:58 UTC (rev 217529)
@@ -0,0 +1,4 @@
+
+PASS @@iterator should only be accessed once, tested via replacement of @@iterator on instance. 
+PASS @@iterator should only be accessed once, tested via replacement of @@iterator on prototype. 
+

Added: trunk/LayoutTests/js/dom/sequence-in-union-iterator-access.html (0 => 217529)


--- trunk/LayoutTests/js/dom/sequence-in-union-iterator-access.html	                        (rev 0)
+++ trunk/LayoutTests/js/dom/sequence-in-union-iterator-access.html	2017-05-29 01:30:58 UTC (rev 217529)
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src=""
+    <script src=""
+</head>
+<body>
+<script>
+    const originalIterator = Object.getOwnPropertyDescriptor(Array.prototype, Symbol.iterator);
+
+    test(() => {
+      let callCount = 0;
+      let array = [2.0, 0.0, 0.0, 2.0, 10.0, 10.0];
+      Object.defineProperty(array, Symbol.iterator, {
+        get() {
+          ++callCount;
+          return originalIterator.value;
+        }
+      });
+      
+      let matrix = new DOMMatrix(array);
+      
+      assert_equals(callCount, 1, "@@iterator must only be accessed once.");
+      assert_equals(matrix.toString(), "matrix(2, 0, 0, 2, 10, 10)", "DOMMatrix constructor should work correctly.");
+    }, "@@iterator should only be accessed once, tested via replacement of @@iterator on instance.");
+
+    test((t) => {
+      t.add_cleanup(() => {
+        Object.defineProperty(Array.prototype, Symbol.iterator, originalIterator);
+      });
+
+      let callCount = 0;
+      const array = [2.0, 0.0, 0.0, 2.0, 10.0, 10.0];
+      Object.defineProperty(Array.prototype, Symbol.iterator, {
+        get() {
+          ++callCount;
+          return originalIterator.value;
+        }
+      });
+
+      let matrix = new DOMMatrix(array);
+
+      assert_equals(callCount, 1, "@@iterator must only be accessed once.");
+      assert_equals(matrix.toString(), "matrix(2, 0, 0, 2, 10, 10)", "DOMMatrix constructor should work correctly.");
+     }, "@@iterator should only be accessed once, tested via replacement of @@iterator on prototype.");
+</script>
+</body>
+</html>

Modified: trunk/Source/_javascript_Core/ChangeLog (217528 => 217529)


--- trunk/Source/_javascript_Core/ChangeLog	2017-05-28 14:11:56 UTC (rev 217528)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-05-29 01:30:58 UTC (rev 217529)
@@ -1,3 +1,19 @@
+2017-05-28  Sam Weinig  <[email protected]>
+
+        [WebIDL] @@iterator should only be accessed once when disambiguating a union type
+        https://bugs.webkit.org/show_bug.cgi?id=172684
+
+        Reviewed by Yusuke Suzuki.
+
+        * runtime/IteratorOperations.cpp:
+        (JSC::iteratorMethod):
+        (JSC::iteratorForIterable):
+        * runtime/IteratorOperations.h:
+        (JSC::forEachInIterable):
+        Add additional iterator helpers to allow union + sequence conversion code
+        to check for iterability by getting the iterator method, and iterate using
+        that method later on.
+
 2017-05-28  Yusuke Suzuki  <[email protected]>
 
         Unreviewed, build fix for Windows

Modified: trunk/Source/_javascript_Core/runtime/IteratorOperations.cpp (217528 => 217529)


--- trunk/Source/_javascript_Core/runtime/IteratorOperations.cpp	2017-05-28 14:11:56 UTC (rev 217528)
+++ trunk/Source/_javascript_Core/runtime/IteratorOperations.cpp	2017-05-29 01:30:58 UTC (rev 217529)
@@ -176,6 +176,43 @@
     return !applyMethod.isUndefined();
 }
 
+JSValue iteratorMethod(ExecState& state, JSObject* object)
+{
+    auto& vm = state.vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    CallData callData;
+    CallType callType;
+    JSValue method = object->getMethod(&state, callData, callType, vm.propertyNames->iteratorSymbol, ASCIILiteral("Symbol.iterator property should be callable"));
+    RETURN_IF_EXCEPTION(scope, jsUndefined());
+
+    return method;
+}
+
+JSValue iteratorForIterable(ExecState& state, JSObject* object, JSValue iteratorMethod)
+{
+    VM& vm = state.vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    CallData iteratorMethodCallData;
+    CallType iteratorMethodCallType = getCallData(iteratorMethod, iteratorMethodCallData);
+    if (iteratorMethodCallType == CallType::None) {
+        throwTypeError(&state, scope);
+        return { };
+    }
+
+    ArgList iteratorMethodArguments;
+    JSValue iterator = call(&state, iteratorMethod, iteratorMethodCallType, iteratorMethodCallData, object, iteratorMethodArguments);
+    RETURN_IF_EXCEPTION(scope, { });
+
+    if (!iterator.isObject()) {
+        throwTypeError(&state, scope);
+        return { };
+    }
+
+    return iterator;
+}
+
 JSValue iteratorForIterable(ExecState* state, JSValue iterable)
 {
     VM& vm = state->vm();

Modified: trunk/Source/_javascript_Core/runtime/IteratorOperations.h (217528 => 217529)


--- trunk/Source/_javascript_Core/runtime/IteratorOperations.h	2017-05-28 14:11:56 UTC (rev 217528)
+++ trunk/Source/_javascript_Core/runtime/IteratorOperations.h	2017-05-29 01:30:58 UTC (rev 217529)
@@ -42,10 +42,15 @@
 
 Structure* createIteratorResultObjectStructure(VM&, JSGlobalObject&);
 
+JS_EXPORT_PRIVATE JSValue iteratorMethod(ExecState&, JSObject*);
+JS_EXPORT_PRIVATE JSValue iteratorForIterable(ExecState&, JSObject*, JSValue iteratorMethod);
+
+JS_EXPORT_PRIVATE JSValue iteratorMethod(ExecState&, JSObject*);
 JS_EXPORT_PRIVATE bool hasIteratorMethod(ExecState&, JSValue);
+
 JS_EXPORT_PRIVATE JSValue iteratorForIterable(ExecState*, JSValue iterable);
 
-template <typename CallBackType>
+template<typename CallBackType>
 void forEachInIterable(ExecState* exec, JSValue iterable, const CallBackType& callback)
 {
     auto& vm = exec->vm();
@@ -70,4 +75,29 @@
     }
 }
 
+template<typename CallBackType>
+void forEachInIterable(ExecState& state, JSObject* iterable, JSValue iteratorMethod, const CallBackType& callback)
+{
+    auto& vm = state.vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto iterator = iteratorForIterable(state, iterable, iteratorMethod);
+    RETURN_IF_EXCEPTION(scope, void());
+    while (true) {
+        JSValue next = iteratorStep(&state, iterator);
+        if (UNLIKELY(scope.exception()) || next.isFalse())
+            return;
+
+        JSValue nextValue = iteratorValue(&state, next);
+        RETURN_IF_EXCEPTION(scope, void());
+
+        callback(vm, state, nextValue);
+        if (UNLIKELY(scope.exception())) {
+            scope.release();
+            iteratorClose(&state, iterator);
+            return;
+        }
+    }
+}
+
 } // namespace JSC

Modified: trunk/Source/WebCore/ChangeLog (217528 => 217529)


--- trunk/Source/WebCore/ChangeLog	2017-05-28 14:11:56 UTC (rev 217528)
+++ trunk/Source/WebCore/ChangeLog	2017-05-29 01:30:58 UTC (rev 217529)
@@ -1,3 +1,45 @@
+2017-05-28  Sam Weinig  <[email protected]>
+
+        [WebIDL] @@iterator should only be accessed once when disambiguating a union type
+        https://bugs.webkit.org/show_bug.cgi?id=172684
+
+        Reviewed by Yusuke Suzuki.
+
+        WebIDL specifies that when determining if the value you are converting to a union
+        is a sequence, you must get the @@iterator property and, should it exist, use it
+        to iterate the sequence. While we correctly accessing the property to make the 
+        determination, we were not passing it into the sequence conversion code, and thus
+        the sequence conversion code re-accessed it, which is observable and wrong.
+
+        This patch pipes the @@iterator method through the sequence conversion code to avoid
+        this.
+
+        Test: js/dom/sequence-in-union-iterator-access.html
+
+        * bindings/js/JSDOMConvertSequences.h:
+        (WebCore::Detail::GenericSequenceConverter::convert):
+        (WebCore::Detail::NumericSequenceConverter::convertArray):
+        (WebCore::Detail::NumericSequenceConverter::convert):
+        (WebCore::Detail::SequenceConverter::convertArray):
+        (WebCore::Detail::SequenceConverter::convert):
+        (WebCore::Detail::SequenceConverter<IDLLong>::convert):
+        (WebCore::Detail::SequenceConverter<IDLFloat>::convert):
+        (WebCore::Detail::SequenceConverter<IDLUnrestrictedFloat>::convert):
+        (WebCore::Detail::SequenceConverter<IDLDouble>::convert):
+        (WebCore::Detail::SequenceConverter<IDLUnrestrictedDouble>::convert):
+        (WebCore::Converter<IDLSequence<T>>::convert):
+        (WebCore::Converter<IDLFrozenArray<T>>::convert):
+        Add variants of convert that take a JSObject* (sequence) / JSValue (iterator method)
+        rather than just the JSValue (sequence). To avoid too much duplication, split some
+        parts of SequenceConverter and NumericSequenceConverter up so they could be reused.
+
+        * bindings/js/JSDOMConvertUnion.h:
+        - Fix incorrect step 3 (WebIDL got updated at some point and we didn't notice) to remove
+          records.
+        - Update sequence and FrozenArray checking/conversion to get the iterator method and pass
+          it along, using the new ConditionalSequenceConverter helper which forwards to the new
+          sequence converters that accept the iterator method.
+
 2017-05-27  Chris Dumez  <[email protected]>
 
         imported/w3c/web-platform-tests/html/semantics/forms/form-control-infrastructure/form_attribute.html is crashing

Modified: trunk/Source/WebCore/bindings/js/JSDOMConvertSequences.h (217528 => 217529)


--- trunk/Source/WebCore/bindings/js/JSDOMConvertSequences.h	2017-05-28 14:11:56 UTC (rev 217528)
+++ trunk/Source/WebCore/bindings/js/JSDOMConvertSequences.h	2017-05-29 01:30:58 UTC (rev 217529)
@@ -39,17 +39,17 @@
 struct GenericSequenceConverter {
     using ReturnType = Vector<typename IDLType::ImplementationType>;
 
-    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* jsObject)
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object)
     {
-        return convert(state, jsObject, ReturnType());
+        return convert(state, object, ReturnType());
     }
 
-    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* jsObject, ReturnType&& result)
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, ReturnType&& result)
     {
-        forEachInIterable(&state, jsObject, [&result](JSC::VM& vm, JSC::ExecState* state, JSC::JSValue jsValue) {
+        forEachInIterable(&state, object, [&result](JSC::VM& vm, JSC::ExecState* state, JSC::JSValue nextValue) {
             auto scope = DECLARE_THROW_SCOPE(vm);
 
-            auto convertedValue = Converter<IDLType>::convert(*state, jsValue);
+            auto convertedValue = Converter<IDLType>::convert(*state, nextValue);
             if (UNLIKELY(scope.exception()))
                 return;
             result.append(WTFMove(convertedValue));
@@ -56,6 +56,24 @@
         });
         return result;
     }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        return convert(state, object, method, ReturnType());
+    }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method, ReturnType&& result)
+    {
+        forEachInIterable(state, object, method, [&result](JSC::VM& vm, JSC::ExecState& state, JSC::JSValue nextValue) {
+            auto scope = DECLARE_THROW_SCOPE(vm);
+
+            auto convertedValue = Converter<IDLType>::convert(state, nextValue);
+            if (UNLIKELY(scope.exception()))
+                return;
+            result.append(WTFMove(convertedValue));
+        });
+        return result;
+    }
 };
 
 // Specialization for numeric types
@@ -67,6 +85,35 @@
     using GenericConverter = GenericSequenceConverter<IDLType>;
     using ReturnType = typename GenericConverter::ReturnType;
 
+    static ReturnType convertArray(JSC::ExecState& state, JSC::ThrowScope& scope, JSC::JSArray* array, unsigned length, JSC::IndexingType indexingType, ReturnType&& result)
+    {
+        if (indexingType == JSC::Int32Shape) {
+            for (unsigned i = 0; i < length; i++) {
+                auto indexValue = array->butterfly()->contiguousInt32()[i].get();
+                ASSERT(!indexValue || indexValue.isInt32());
+                if (!indexValue)
+                    result.uncheckedAppend(0);
+                else
+                    result.uncheckedAppend(indexValue.asInt32());
+            }
+            return result;
+        }
+
+        ASSERT(indexingType == JSC::DoubleShape);
+        for (unsigned i = 0; i < length; i++) {
+            auto doubleValue = array->butterfly()->contiguousDouble()[i];
+            if (std::isnan(doubleValue))
+                result.uncheckedAppend(0);
+            else {
+                auto convertedValue = Converter<IDLType>::convert(state, scope, doubleValue);
+                RETURN_IF_EXCEPTION(scope, { });
+
+                result.uncheckedAppend(convertedValue);
+            }
+        }
+        return result;
+    }
+
     static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
     {
         auto& vm = state.vm();
@@ -103,31 +150,40 @@
         if (indexingType != JSC::Int32Shape && indexingType != JSC::DoubleShape)
             return GenericConverter::convert(state, object, WTFMove(result));
 
-        if (indexingType == JSC::Int32Shape) {
-            for (unsigned i = 0; i < length; i++) {
-                auto indexValue = array->butterfly()->contiguousInt32()[i].get();
-                ASSERT(!indexValue || indexValue.isInt32());
-                if (!indexValue)
-                    result.uncheckedAppend(0);
-                else
-                    result.uncheckedAppend(indexValue.asInt32());
-            }
-            return result;
-        }
+        return convertArray(state, scope, array, length, indexingType, WTFMove(result));
+    }
 
-        ASSERT(indexingType == JSC::DoubleShape);
-        for (unsigned i = 0; i < length; i++) {
-            auto doubleValue = array->butterfly()->contiguousDouble()[i];
-            if (std::isnan(doubleValue))
-                result.uncheckedAppend(0);
-            else {
-                auto convertedValue = Converter<IDLType>::convert(state, scope, doubleValue);
-                RETURN_IF_EXCEPTION(scope, { });
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        auto& vm = state.vm();
+        auto scope = DECLARE_THROW_SCOPE(vm);
 
-                result.uncheckedAppend(convertedValue);
-            }
+        if (!JSC::isJSArray(object))
+            return GenericConverter::convert(state, object, method);
+
+        JSC::JSArray* array = JSC::asArray(object);
+        if (!array->isIteratorProtocolFastAndNonObservable())
+            return GenericConverter::convert(state, object, method);
+
+        unsigned length = array->length();
+        ReturnType result;
+        // If we're not an int32/double array, it's possible that converting a
+        // JSValue to a number could cause the iterator protocol to change, hence,
+        // we may need more capacity, or less. In such cases, we use the length
+        // as a proxy for the capacity we will most likely need (it's unlikely that 
+        // a program is written with a valueOf that will augment the iterator protocol).
+        // If we are an int32/double array, then length is precisely the capacity we need.
+        if (!result.tryReserveCapacity(length)) {
+            // FIXME: Is the right exception to throw?
+            throwTypeError(&state, scope);
+            return { };
         }
-        return result;
+        
+        JSC::IndexingType indexingType = array->indexingType() & JSC::IndexingShapeMask;
+        if (indexingType != JSC::Int32Shape && indexingType != JSC::DoubleShape)
+            return GenericConverter::convert(state, object, method, WTFMove(result));
+
+        return convertArray(state, scope, array, length, indexingType, WTFMove(result));
     }
 };
 
@@ -136,27 +192,8 @@
     using GenericConverter = GenericSequenceConverter<IDLType>;
     using ReturnType = typename GenericConverter::ReturnType;
 
-    static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
+    static ReturnType convertArray(JSC::ExecState& state, JSC::ThrowScope& scope, JSC::JSArray* array)
     {
-        auto& vm = state.vm();
-        auto scope = DECLARE_THROW_SCOPE(vm);
-
-        if (!value.isObject()) {
-            throwSequenceTypeError(state, scope);
-            return { };
-        }
-
-        JSC::JSObject* object = JSC::asObject(value);
-        if (Converter<IDLType>::conversionHasSideEffects)
-            return GenericConverter::convert(state, object);
-
-        if (!JSC::isJSArray(object))
-            return GenericConverter::convert(state, object);
-
-        JSC::JSArray* array = JSC::asArray(object);
-        if (!array->isIteratorProtocolFastAndNonObservable())
-            return GenericConverter::convert(state, object);
-
         unsigned length = array->length();
 
         ReturnType result;
@@ -196,6 +233,48 @@
         }
         return result;
     }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSValue value)
+    {
+        auto& vm = state.vm();
+        auto scope = DECLARE_THROW_SCOPE(vm);
+
+        if (!value.isObject()) {
+            throwSequenceTypeError(state, scope);
+            return { };
+        }
+
+        JSC::JSObject* object = JSC::asObject(value);
+        if (Converter<IDLType>::conversionHasSideEffects)
+            return GenericConverter::convert(state, object);
+
+        if (!JSC::isJSArray(object))
+            return GenericConverter::convert(state, object);
+
+        JSC::JSArray* array = JSC::asArray(object);
+        if (!array->isIteratorProtocolFastAndNonObservable())
+            return GenericConverter::convert(state, object);
+        
+        return convertArray(state, scope, array);
+    }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        auto& vm = state.vm();
+        auto scope = DECLARE_THROW_SCOPE(vm);
+
+        if (Converter<IDLType>::conversionHasSideEffects)
+            return GenericConverter::convert(state, object, method);
+
+        if (!JSC::isJSArray(object))
+            return GenericConverter::convert(state, object, method);
+
+        JSC::JSArray* array = JSC::asArray(object);
+        if (!array->isIteratorProtocolFastAndNonObservable())
+            return GenericConverter::convert(state, object, method);
+
+        return convertArray(state, scope, array);
+    }
 };
 
 template<>
@@ -206,6 +285,11 @@
     {
         return NumericSequenceConverter<IDLLong>::convert(state, value);
     }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        return NumericSequenceConverter<IDLLong>::convert(state, object, method);
+    }
 };
 
 template<>
@@ -216,6 +300,11 @@
     {
         return NumericSequenceConverter<IDLFloat>::convert(state, value);
     }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        return NumericSequenceConverter<IDLFloat>::convert(state, object, method);
+    }
 };
 
 template<>
@@ -226,6 +315,11 @@
     {
         return NumericSequenceConverter<IDLUnrestrictedFloat>::convert(state, value);
     }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        return NumericSequenceConverter<IDLUnrestrictedFloat>::convert(state, object, method);
+    }
 };
 
 template<>
@@ -236,6 +330,11 @@
     {
         return NumericSequenceConverter<IDLDouble>::convert(state, value);
     }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        return NumericSequenceConverter<IDLDouble>::convert(state, object, method);
+    }
 };
 
 template<>
@@ -246,6 +345,11 @@
     {
         return NumericSequenceConverter<IDLUnrestrictedDouble>::convert(state, value);
     }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        return NumericSequenceConverter<IDLUnrestrictedDouble>::convert(state, object, method);
+    }
 };
 
 }
@@ -257,6 +361,11 @@
     {
         return Detail::SequenceConverter<T>::convert(state, value);
     }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        return Detail::SequenceConverter<T>::convert(state, object, method);
+    }
 };
 
 template<typename T> struct JSConverter<IDLSequence<T>> {
@@ -280,6 +389,11 @@
     {
         return Detail::SequenceConverter<T>::convert(state, value);
     }
+
+    static ReturnType convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        return Detail::SequenceConverter<T>::convert(state, object, method);
+    }
 };
 
 template<typename T> struct JSConverter<IDLFrozenArray<T>> {

Modified: trunk/Source/WebCore/bindings/js/JSDOMConvertUnion.h (217528 => 217529)


--- trunk/Source/WebCore/bindings/js/JSDOMConvertUnion.h	2017-05-28 14:11:56 UTC (rev 217528)
+++ trunk/Source/WebCore/bindings/js/JSDOMConvertUnion.h	2017-05-29 01:30:58 UTC (rev 217529)
@@ -51,6 +51,26 @@
     }
 };
 
+
+template<typename ReturnType, typename T, bool enabled>
+struct ConditionalSequenceConverter;
+
+template<typename ReturnType, typename T>
+struct ConditionalSequenceConverter<ReturnType, T, true> {
+    static std::optional<ReturnType> convert(JSC::ExecState& state, JSC::JSObject* object, JSC::JSValue method)
+    {
+        return ReturnType(Converter<T>::convert(state, object, method));
+    }
+};
+
+template<typename ReturnType, typename T>
+struct ConditionalSequenceConverter<ReturnType, T, false> {
+    static std::optional<ReturnType> convert(JSC::ExecState&, JSC::JSObject*, JSC::JSValue)
+    {
+        return std::nullopt;
+    }
+};
+
 namespace Detail {
 
 template<typename List, bool condition>
@@ -128,15 +148,10 @@
         // NOTE: Union is expected to be pre-flattented.
         
         // 3. If V is null or undefined then:
-        if (hasDictionaryType || hasRecordType) {
+        if (hasDictionaryType) {
             if (value.isUndefinedOrNull()) {
                 //     1. If types includes a dictionary type, then return the result of converting V to that dictionary type.
-                if (hasDictionaryType)
-                    return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, DictionaryType, hasDictionaryType>::convert(state, value).value());
-                
-                //     2. If types includes a record type, then return the result of converting V to that record type.
-                if (hasRecordType)
-                    return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, RecordType, hasRecordType>::convert(state, value).value());
+                return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, DictionaryType, hasDictionaryType>::convert(state, value).value());
             }
         }
 
@@ -173,8 +188,7 @@
             if (value.isCell()) {
                 JSC::JSCell* cell = value.asCell();
                 if (cell->isObject()) {
-                    // FIXME: We should be able to optimize the following code by making use
-                    // of the fact that we have proved that the value is an object. 
+                    auto object = asObject(value);
                 
                     //     1. If types includes a sequence type, then:
                     //         1. Let method be the result of GetMethod(V, @@iterator).
@@ -183,10 +197,10 @@
                     //            sequence of that type from V and method.        
                     constexpr bool hasSequenceType = numberOfSequenceTypes != 0;
                     if (hasSequenceType) {
-                        bool hasIterator = JSC::hasIteratorMethod(state, value);
+                        auto method = JSC::iteratorMethod(state, object);
                         RETURN_IF_EXCEPTION(scope, ReturnType());
-                        if (hasIterator)
-                            return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, SequenceType, hasSequenceType>::convert(state, value).value());
+                        if (!method.isUndefined())
+                            return std::move<WTF::CheckMoveParameter>(ConditionalSequenceConverter<ReturnType, SequenceType, hasSequenceType>::convert(state, object, method).value());
                     }
 
                     //     2. If types includes a frozen array type, then:
@@ -196,10 +210,10 @@
                     //            frozen array of that type from V and method.
                     constexpr bool hasFrozenArrayType = numberOfFrozenArrayTypes != 0;
                     if (hasFrozenArrayType) {
-                        bool hasIterator = JSC::hasIteratorMethod(state, value);
+                        auto method = JSC::iteratorMethod(state, object);
                         RETURN_IF_EXCEPTION(scope, ReturnType());
-                        if (hasIterator)
-                            return std::move<WTF::CheckMoveParameter>(ConditionalConverter<ReturnType, FrozenArrayType, hasFrozenArrayType>::convert(state, value).value());
+                        if (!method.isUndefined())
+                            return std::move<WTF::CheckMoveParameter>(ConditionalSequenceConverter<ReturnType, FrozenArrayType, hasFrozenArrayType>::convert(state, object, method).value());
                     }
 
                     //     3. If types includes a dictionary type, then return the result of
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to