Title: [195878] trunk/Source/_javascript_Core
Revision
195878
Author
keith_mil...@apple.com
Date
2016-01-29 18:45:25 -0800 (Fri, 29 Jan 2016)

Log Message

Array.prototype native functions should use Symbol.species to construct the result
https://bugs.webkit.org/show_bug.cgi?id=153660

Reviewed by Saam Barati.

This patch adds support for Symbol.species in the Array.prototype native functions.
We make an optimization to avoid regressions on some benchmarks by using an
adaptive watchpoint to check if Array.prototype.constructor is ever changed.

* runtime/ArrayPrototype.cpp:
(JSC::putLength):
(JSC::setLength):
(JSC::speciesConstructArray):
(JSC::arrayProtoFuncConcat):
(JSC::arrayProtoFuncSlice):
(JSC::arrayProtoFuncSplice):
(JSC::ArrayPrototype::setConstructor):
(JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint):
(JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire):
* runtime/ArrayPrototype.h:
(JSC::ArrayPrototype::didChangeConstructorProperty):
* runtime/ConstructData.cpp:
(JSC::construct):
* runtime/ConstructData.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* tests/es6.yaml:
* tests/stress/array-species-functions.js: Added.
(Symbol.species):
(funcThrows):
(test.species):
(test):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (195877 => 195878)


--- trunk/Source/_javascript_Core/ChangeLog	2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-01-30 02:45:25 UTC (rev 195878)
@@ -1,3 +1,38 @@
+2016-01-29  Keith Miller  <keith_mil...@apple.com>
+
+        Array.prototype native functions should use Symbol.species to construct the result
+        https://bugs.webkit.org/show_bug.cgi?id=153660
+
+        Reviewed by Saam Barati.
+
+        This patch adds support for Symbol.species in the Array.prototype native functions.
+        We make an optimization to avoid regressions on some benchmarks by using an
+        adaptive watchpoint to check if Array.prototype.constructor is ever changed.
+
+        * runtime/ArrayPrototype.cpp:
+        (JSC::putLength):
+        (JSC::setLength):
+        (JSC::speciesConstructArray):
+        (JSC::arrayProtoFuncConcat):
+        (JSC::arrayProtoFuncSlice):
+        (JSC::arrayProtoFuncSplice):
+        (JSC::ArrayPrototype::setConstructor):
+        (JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint):
+        (JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire):
+        * runtime/ArrayPrototype.h:
+        (JSC::ArrayPrototype::didChangeConstructorProperty):
+        * runtime/ConstructData.cpp:
+        (JSC::construct):
+        * runtime/ConstructData.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * tests/es6.yaml:
+        * tests/stress/array-species-functions.js: Added.
+        (Symbol.species):
+        (funcThrows):
+        (test.species):
+        (test):
+
 2016-01-29  Filip Pizlo  <fpi...@apple.com>
 
         CallLinkStatus should trust BadCell exit sites whenever there is no stub

Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (195877 => 195878)


--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp	2016-01-30 02:45:25 UTC (rev 195878)
@@ -24,6 +24,8 @@
 #include "config.h"
 #include "ArrayPrototype.h"
 
+#include "AdaptiveInferredPropertyValueWatchpointBase.h"
+#include "ArrayConstructor.h"
 #include "BuiltinNames.h"
 #include "ButterflyInlines.h"
 #include "CachedCall.h"
@@ -155,12 +157,64 @@
     return obj->get(exec, exec->propertyNames().length).toUInt32(exec);
 }
 
-static void putLength(ExecState* exec, JSObject* obj, JSValue value)
+static ALWAYS_INLINE void putLength(ExecState* exec, JSObject* obj, JSValue value)
 {
     PutPropertySlot slot(obj);
     obj->methodTable()->put(obj, exec, exec->propertyNames().length, value, slot);
 }
 
+static ALWAYS_INLINE void setLength(ExecState* exec, JSObject* obj, unsigned value)
+{
+    if (isJSArray(obj))
+        jsCast<JSArray*>(obj)->setLength(exec, value);
+    putLength(exec, obj, jsNumber(value));
+}
+
+enum class SpeciesConstructResult {
+    FastPath,
+    Exception,
+    CreatedObject
+};
+
+static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstructArray(ExecState* exec, JSObject* thisObject, unsigned length)
+{
+    // ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate
+    JSValue constructor = jsUndefined();
+    if (LIKELY(isJSArray(thisObject))) {
+        // Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
+        // We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
+        if (LIKELY(!thisObject->hasCustomProperties()
+            && thisObject->globalObject()->arrayPrototype() == thisObject->prototype()
+            && !thisObject->globalObject()->arrayPrototype()->didChangeConstructorProperty()))
+            return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
+
+        constructor = thisObject->get(exec, exec->propertyNames().constructor);
+        if (exec->hadException())
+            return std::make_pair(SpeciesConstructResult::Exception, nullptr);
+        if (constructor.isConstructor()) {
+            JSObject* constructorObject = jsCast<JSObject*>(constructor);
+            if (exec->lexicalGlobalObject() != constructorObject->globalObject())
+                return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
+        }
+        if (constructor.isObject()) {
+            constructor = constructor.get(exec, exec->propertyNames().speciesSymbol);
+            if (exec->hadException())
+                return std::make_pair(SpeciesConstructResult::Exception, nullptr);
+            if (constructor.isNull())
+                return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
+        }
+    }
+    if (constructor.isUndefined())
+        return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
+
+    MarkedArgumentBuffer args;
+    args.append(jsNumber(length));
+    JSObject* newObject = construct(exec, constructor, args, "Species construction did not get a valid constructor");
+    if (exec->hadException())
+        return std::make_pair(SpeciesConstructResult::Exception, nullptr);
+    return std::make_pair(SpeciesConstructResult::CreatedObject, newObject);
+}
+
 static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
 {
     JSValue value = exec->argument(argument);
@@ -532,6 +586,11 @@
     JSValue curArg = thisValue.toObject(exec);
     Checked<unsigned, RecordOverflow> finalArraySize = 0;
 
+    // We need to do species construction before geting the rest of the elements.
+    std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, curArg.getObject(), 0);
+    if (speciesResult.first == SpeciesConstructResult::Exception)
+        return JSValue::encode(jsUndefined());
+
     JSArray* currentArray = nullptr;
     JSArray* previousArray = nullptr;
     for (unsigned i = 0; ; ++i) {
@@ -552,16 +611,22 @@
     if (finalArraySize.hasOverflowed())
         return JSValue::encode(throwOutOfMemoryError(exec));
 
-    if (argCount == 1 && previousArray && currentArray && finalArraySize.unsafeGet() < MIN_SPARSE_ARRAY_INDEX) {
+    if (speciesResult.first == SpeciesConstructResult::FastPath && argCount == 1 && previousArray && currentArray && finalArraySize.unsafeGet() < MIN_SPARSE_ARRAY_INDEX) {
         IndexingType type = JSArray::fastConcatType(exec->vm(), *previousArray, *currentArray);
         if (type != NonArray)
             return previousArray->fastConcatWith(*exec, *currentArray);
     }
 
-    JSArray* arr = constructEmptyArray(exec, nullptr, finalArraySize.unsafeGet());
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    ASSERT(speciesResult.first != SpeciesConstructResult::Exception);
 
+    JSObject* result;
+    if (speciesResult.first == SpeciesConstructResult::CreatedObject)
+        result = speciesResult.second;
+    else {
+        // We add the newTarget because the compiler gets confused between 0 being a number and a pointer.
+        result = constructEmptyArray(exec, nullptr, 0, JSValue());
+    }
+
     curArg = thisValue.toObject(exec);
     unsigned n = 0;
     for (unsigned i = 0; ; ++i) {
@@ -575,19 +640,19 @@
                 if (exec->hadException())
                     return JSValue::encode(jsUndefined());
                 if (v)
-                    arr->putDirectIndex(exec, n, v);
+                    result->putDirectIndex(exec, n, v);
                 n++;
             }
         } else {
-            arr->putDirectIndex(exec, n, curArg);
+            result->putDirectIndex(exec, n, curArg);
             n++;
         }
         if (i == argCount)
             break;
         curArg = exec->uncheckedArgument(i);
     }
-    arr->setLength(exec, n);
-    return JSValue::encode(arr);
+    setLength(exec, result, n);
+    return JSValue::encode(result);
 }
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
@@ -757,12 +822,21 @@
     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
     unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
 
-    if (isJSArray(thisObj)) {
+    std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
+    // We can only get an exception if we call some user function.
+    if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
+        return JSValue::encode(jsUndefined());
+
+    if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj))) {
         if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
             return JSValue::encode(result);
     }
 
-    JSArray* result = constructEmptyArray(exec, nullptr, end - begin);
+    JSObject* result;
+    if (speciesResult.first == SpeciesConstructResult::CreatedObject)
+        result = speciesResult.second;
+    else
+        result = constructEmptyArray(exec, nullptr, end - begin);
 
     unsigned n = 0;
     for (unsigned k = begin; k < end; k++, n++) {
@@ -772,7 +846,7 @@
         if (v)
             result->putDirectIndex(exec, n, v);
     }
-    result->setLength(exec, n);
+    setLength(exec, result, n);
     return JSValue::encode(result);
 }
 
@@ -786,10 +860,22 @@
     unsigned length = getLength(exec, thisObj);
     if (exec->hadException())
         return JSValue::encode(jsUndefined());
-    
-    if (!exec->argumentCount())
-        return JSValue::encode(constructEmptyArray(exec, nullptr));
 
+    if (!exec->argumentCount()) {
+        std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0);
+        if (speciesResult.first == SpeciesConstructResult::Exception)
+            return JSValue::encode(jsUndefined());
+
+        JSObject* result;
+        if (speciesResult.first == SpeciesConstructResult::CreatedObject)
+            result = speciesResult.second;
+        else
+            result = constructEmptyArray(exec, nullptr);
+
+        setLength(exec, result, 0);
+        return JSValue::encode(result);
+    }
+
     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
 
     unsigned deleteCount = length - begin;
@@ -803,15 +889,22 @@
             deleteCount = static_cast<unsigned>(deleteDouble);
     }
 
-    JSArray* result = nullptr;
+    std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, deleteCount);
+    if (speciesResult.first == SpeciesConstructResult::Exception)
+        return JSValue::encode(jsUndefined());
 
-    if (isJSArray(thisObj))
+    JSObject* result = nullptr;
+    if (speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj))
         result = asArray(thisObj)->fastSlice(*exec, begin, deleteCount);
 
     if (!result) {
-        result = JSArray::tryCreateUninitialized(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount);
-        if (!result)
-            return JSValue::encode(throwOutOfMemoryError(exec));
+        if (speciesResult.first == SpeciesConstructResult::CreatedObject)
+            result = speciesResult.second;
+        else {
+            result = JSArray::tryCreateUninitialized(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount);
+            if (!result)
+                return JSValue::encode(throwOutOfMemoryError(exec));
+        }
 
         for (unsigned k = 0; k < deleteCount; ++k) {
             JSValue v = getProperty(exec, thisObj, k + begin);
@@ -837,7 +930,7 @@
             return JSValue::encode(jsUndefined());
     }
 
-    putLength(exec, thisObj, jsNumber(length - deleteCount + additionalArgs));
+    setLength(exec, thisObj, length - deleteCount + additionalArgs);
     return JSValue::encode(result);
 }
 
@@ -943,4 +1036,48 @@
     return JSValue::encode(JSArrayIterator::create(exec, exec->callee()->globalObject()->arrayIteratorStructure(), ArrayIterateKey, thisObj));
 }
 
+// -------------------- ArrayPrototype.constructor Watchpoint ------------------
+
+class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
+public:
+    typedef AdaptiveInferredPropertyValueWatchpointBase Base;
+    ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
+
+private:
+    virtual void handleFire(const FireDetail&) override;
+
+    ArrayPrototype* m_arrayPrototype;
+};
+
+void ArrayPrototype::setConstructor(VM& vm, JSObject* constructorProperty, unsigned attributes)
+{
+    putDirectWithoutTransition(vm, vm.propertyNames->constructor, constructorProperty, attributes);
+
+    PropertyOffset offset = this->structure()->get(vm, vm.propertyNames->constructor);
+    ASSERT(isValidOffset(offset));
+    this->structure()->startWatchingPropertyForReplacements(vm, offset);
+
+    ObjectPropertyCondition condition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), constructorProperty);
+    ASSERT(condition.isWatchable());
+
+    m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this);
+    m_constructorWatchpoint->install();
+}
+
+ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
+    : Base(key)
+    , m_arrayPrototype(prototype)
+{
+}
+
+void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail& detail)
+{
+    StringPrintStream out;
+    out.print("ArrayPrototype adaption of ", key(), " failed: ", detail);
+
+    StringFireDetail stringDetail(out.toCString().data());
+
+    m_arrayPrototype->m_didChangeConstructorProperty = true;
+}
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.h (195877 => 195878)


--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.h	2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.h	2016-01-30 02:45:25 UTC (rev 195878)
@@ -26,6 +26,8 @@
 
 namespace JSC {
 
+class ArrayPrototypeAdaptiveInferredPropertyWatchpoint;
+
 class ArrayPrototype : public JSArray {
 private:
     ArrayPrototype(VM&, Structure*);
@@ -42,8 +44,18 @@
         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), ArrayClass);
     }
 
+    void setConstructor(VM&, JSObject* constructorProperty, unsigned attributes);
+
+    bool didChangeConstructorProperty() const { return m_didChangeConstructorProperty; }
+
 protected:
     void finishCreation(VM&, JSGlobalObject*);
+
+private:
+    // This bit is set if any user modifies the constructor property Array.prototype. This is used to optimize species creation for JSArrays.
+    friend ArrayPrototypeAdaptiveInferredPropertyWatchpoint;
+    std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorWatchpoint;
+    bool m_didChangeConstructorProperty = false;
 };
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);

Modified: trunk/Source/_javascript_Core/runtime/ConstructData.cpp (195877 => 195878)


--- trunk/Source/_javascript_Core/runtime/ConstructData.cpp	2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/_javascript_Core/runtime/ConstructData.cpp	2016-01-30 02:45:25 UTC (rev 195878)
@@ -35,6 +35,17 @@
 
 namespace JSC {
 
+JSObject* construct(ExecState* exec, JSValue constructorObject, const ArgList& args, const String& errorMessage)
+{
+    ConstructData constructData;
+    ConstructType constructType = getConstructData(constructorObject, constructData);
+    if (constructType == ConstructTypeNone)
+        return throwTypeError(exec, errorMessage);
+
+    return construct(exec, constructorObject, constructType, constructData, args, constructorObject);
+}
+
+
 JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget)
 {
     ASSERT(constructType == ConstructTypeJS || constructType == ConstructTypeHost);

Modified: trunk/Source/_javascript_Core/runtime/ConstructData.h (195877 => 195878)


--- trunk/Source/_javascript_Core/runtime/ConstructData.h	2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/_javascript_Core/runtime/ConstructData.h	2016-01-30 02:45:25 UTC (rev 195878)
@@ -56,6 +56,8 @@
     } js;
 };
 
+// Convenience wrapper so you don't need to deal with CallData and CallType unless you are going to use them.
+JSObject* construct(ExecState*, JSValue functionObject, const ArgList&, const String& errorMessage);
 JS_EXPORT_PRIVATE JSObject* construct(ExecState*, JSValue constructor, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget);
 
 ALWAYS_INLINE JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType constructType, const ConstructData& constructData, const ArgList& args)

Modified: trunk/Source/_javascript_Core/runtime/JSCJSValue.h (195877 => 195878)


--- trunk/Source/_javascript_Core/runtime/JSCJSValue.h	2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/_javascript_Core/runtime/JSCJSValue.h	2016-01-30 02:45:25 UTC (rev 195878)
@@ -219,6 +219,7 @@
     // Querying the type.
     bool isEmpty() const;
     bool isFunction() const;
+    bool isConstructor() const;
     bool isUndefined() const;
     bool isNull() const;
     bool isUndefinedOrNull() const;

Modified: trunk/Source/_javascript_Core/runtime/JSCJSValueInlines.h (195877 => 195878)


--- trunk/Source/_javascript_Core/runtime/JSCJSValueInlines.h	2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/_javascript_Core/runtime/JSCJSValueInlines.h	2016-01-30 02:45:25 UTC (rev 195878)
@@ -682,6 +682,16 @@
     return isCell() && (asCell()->inherits(JSFunction::info()) || asCell()->inherits(InternalFunction::info()));
 }
 
+// FIXME: We could do this in a smarter way. See: https://bugs.webkit.org/show_bug.cgi?id=153670
+inline bool JSValue::isConstructor() const
+{
+    if (isFunction()) {
+        ConstructData data;
+        return getConstructData(*this, data) != ConstructTypeNone;
+    }
+    return false;
+}
+
 // this method is here to be after the inline declaration of JSCell::inherits
 inline bool JSValue::inherits(const ClassInfo* classInfo) const
 {

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (195877 => 195878)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-01-30 02:45:25 UTC (rev 195878)
@@ -404,7 +404,7 @@
     m_definePropertyFunction.set(vm, this, definePropertyFunction);
 
     JSCell* functionConstructor = FunctionConstructor::create(vm, FunctionConstructor::createStructure(vm, this, m_functionPrototype.get()), m_functionPrototype.get());
-    JSCell* arrayConstructor = ArrayConstructor::create(vm, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get(), speciesGetterSetter);
+    JSObject* arrayConstructor = ArrayConstructor::create(vm, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get(), speciesGetterSetter);
     
     m_regExpConstructor.set(vm, this, RegExpConstructor::create(vm, RegExpConstructor::createStructure(vm, this, m_functionPrototype.get()), m_regExpPrototype.get(), speciesGetterSetter));
     
@@ -439,7 +439,7 @@
     
     m_objectPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, objectConstructor, DontEnum);
     m_functionPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, functionConstructor, DontEnum);
-    m_arrayPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, arrayConstructor, DontEnum);
+    m_arrayPrototype->setConstructor(vm, arrayConstructor, DontEnum);
     m_regExpPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, m_regExpConstructor.get(), DontEnum);
     
     putDirectWithoutTransition(vm, vm.propertyNames->Object, objectConstructor, DontEnum);

Modified: trunk/Source/_javascript_Core/tests/es6.yaml (195877 => 195878)


--- trunk/Source/_javascript_Core/tests/es6.yaml	2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/_javascript_Core/tests/es6.yaml	2016-01-30 02:45:25 UTC (rev 195878)
@@ -717,15 +717,15 @@
 - path: es6/Array_is_subclassable_Array.of.js
   cmd: runES6 :normal
 - path: es6/Array_is_subclassable_Array.prototype.concat.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Array_is_subclassable_Array.prototype.filter.js
   cmd: runES6 :fail
 - path: es6/Array_is_subclassable_Array.prototype.map.js
   cmd: runES6 :fail
 - path: es6/Array_is_subclassable_Array.prototype.slice.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Array_is_subclassable_Array.prototype.splice.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Array_is_subclassable_correct_prototype_chain.js
   cmd: runES6 :normal
 - path: es6/Array_static_methods_Array.from_generator_instances.js
@@ -1195,15 +1195,15 @@
 - path: es6/well-known_symbols_Symbol.search.js
   cmd: runES6 :fail
 - path: es6/well-known_symbols_Symbol.species_Array.prototype.concat.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.species_Array.prototype.filter.js
   cmd: runES6 :fail
 - path: es6/well-known_symbols_Symbol.species_Array.prototype.map.js
   cmd: runES6 :fail
 - path: es6/well-known_symbols_Symbol.species_Array.prototype.slice.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.species_Array.prototype.splice.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.species_existence.js
   cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.species_RegExp.prototype[Symbol.split].js

Added: trunk/Source/_javascript_Core/tests/stress/array-species-functions.js (0 => 195878)


--- trunk/Source/_javascript_Core/tests/stress/array-species-functions.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/array-species-functions.js	2016-01-30 02:45:25 UTC (rev 195878)
@@ -0,0 +1,79 @@
+C = class extends Array { }
+N = class { }
+N[Symbol.species] = function() { throw "this should never be called"; }
+
+testFunctions = [
+    [Array.prototype.concat, []],
+    [Array.prototype.slice, [1,2]],
+    [Array.prototype.splice, []],
+    [Array.prototype.splice, [0,1]]
+];
+
+objProp = Object.defineProperty;
+
+function funcThrows(func, args) {
+    try {
+        func.call(...args)
+        return false;
+    } catch (e) {
+        return true;
+    }
+}
+
+function test(testData) {
+    "use strict";
+    let [protoFunction, args] = testData;
+    let foo = new C(10);
+    let n = new N();
+
+    // Test non-array ignores constructor.
+    objProp(n, "constructor", { value: C });
+    let bar = protoFunction.call(...[n, ...args]);
+    if (!(bar instanceof Array) || bar instanceof N || bar instanceof C)
+        throw Error();
+
+    objProp(foo, "constructor", { value: null });
+    if (!funcThrows(protoFunction, [foo, ...args]))
+        throw "didn't throw";
+
+    // Test array defaults cases.
+    foo = new C(10);
+
+    objProp(C, Symbol.species, { value: undefined, writable: true});
+    bar = protoFunction.call(...[foo, ...args]);
+    if (!(bar instanceof Array) || bar instanceof C)
+        throw Error();
+
+    C[Symbol.species] = null;
+    bar = protoFunction.call(...[foo, ...args]);
+    if (!(bar instanceof Array) || bar instanceof C)
+        throw Error();
+
+    // Test species is custom constructor.
+    let called = false;
+    function species(...args) {
+        called = true;
+        return new C(...args);
+    }
+
+    C[Symbol.species] = species;
+    bar = protoFunction.call(...[foo, ...args]);
+
+    if (!(bar instanceof Array) || !(bar instanceof C) || !called)
+        throw Error("failed on " + protoFunction);
+
+    function speciesThrows() {
+        throw Error();
+    }
+
+    C[Symbol.species] = speciesThrows;
+    if (!funcThrows(protoFunction, [foo, ...args]))
+        throw "didn't throw";
+
+    C[Symbol.species] = true;
+    if (!funcThrows(protoFunction, [foo, ...args]))
+        throw "didn't throw";
+
+}
+
+testFunctions.forEach(test);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to