<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[195878] trunk/Source/JavaScriptCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/195878">195878</a></dd>
<dt>Author</dt> <dd>keith_miller@apple.com</dd>
<dt>Date</dt> <dd>2016-01-29 18:45:25 -0800 (Fri, 29 Jan 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>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):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeArrayPrototypecpp">trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeArrayPrototypeh">trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeConstructDatacpp">trunk/Source/JavaScriptCore/runtime/ConstructData.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeConstructDatah">trunk/Source/JavaScriptCore/runtime/ConstructData.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSCJSValueh">trunk/Source/JavaScriptCore/runtime/JSCJSValue.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSCJSValueInlinesh">trunk/Source/JavaScriptCore/runtime/JSCJSValueInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSGlobalObjectcpp">trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoretestses6yaml">trunk/Source/JavaScriptCore/tests/es6.yaml</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstressarrayspeciesfunctionsjs">trunk/Source/JavaScriptCore/tests/stress/array-species-functions.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (195877 => 195878)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-01-30 02:45:25 UTC (rev 195878)
</span><span class="lines">@@ -1,3 +1,38 @@
</span><ins>+2016-01-29  Keith Miller  &lt;keith_miller@apple.com&gt;
+
+        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):
+
</ins><span class="cx"> 2016-01-29  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         CallLinkStatus should trust BadCell exit sites whenever there is no stub
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeArrayPrototypecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp (195877 => 195878)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp        2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp        2016-01-30 02:45:25 UTC (rev 195878)
</span><span class="lines">@@ -24,6 +24,8 @@
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> #include &quot;ArrayPrototype.h&quot;
</span><span class="cx"> 
</span><ins>+#include &quot;AdaptiveInferredPropertyValueWatchpointBase.h&quot;
+#include &quot;ArrayConstructor.h&quot;
</ins><span class="cx"> #include &quot;BuiltinNames.h&quot;
</span><span class="cx"> #include &quot;ButterflyInlines.h&quot;
</span><span class="cx"> #include &quot;CachedCall.h&quot;
</span><span class="lines">@@ -155,12 +157,64 @@
</span><span class="cx">     return obj-&gt;get(exec, exec-&gt;propertyNames().length).toUInt32(exec);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void putLength(ExecState* exec, JSObject* obj, JSValue value)
</del><ins>+static ALWAYS_INLINE void putLength(ExecState* exec, JSObject* obj, JSValue value)
</ins><span class="cx"> {
</span><span class="cx">     PutPropertySlot slot(obj);
</span><span class="cx">     obj-&gt;methodTable()-&gt;put(obj, exec, exec-&gt;propertyNames().length, value, slot);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static ALWAYS_INLINE void setLength(ExecState* exec, JSObject* obj, unsigned value)
+{
+    if (isJSArray(obj))
+        jsCast&lt;JSArray*&gt;(obj)-&gt;setLength(exec, value);
+    putLength(exec, obj, jsNumber(value));
+}
+
+enum class SpeciesConstructResult {
+    FastPath,
+    Exception,
+    CreatedObject
+};
+
+static ALWAYS_INLINE std::pair&lt;SpeciesConstructResult, JSObject*&gt; 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-&gt;hasCustomProperties()
+            &amp;&amp; thisObject-&gt;globalObject()-&gt;arrayPrototype() == thisObject-&gt;prototype()
+            &amp;&amp; !thisObject-&gt;globalObject()-&gt;arrayPrototype()-&gt;didChangeConstructorProperty()))
+            return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
+
+        constructor = thisObject-&gt;get(exec, exec-&gt;propertyNames().constructor);
+        if (exec-&gt;hadException())
+            return std::make_pair(SpeciesConstructResult::Exception, nullptr);
+        if (constructor.isConstructor()) {
+            JSObject* constructorObject = jsCast&lt;JSObject*&gt;(constructor);
+            if (exec-&gt;lexicalGlobalObject() != constructorObject-&gt;globalObject())
+                return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
+        }
+        if (constructor.isObject()) {
+            constructor = constructor.get(exec, exec-&gt;propertyNames().speciesSymbol);
+            if (exec-&gt;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, &quot;Species construction did not get a valid constructor&quot;);
+    if (exec-&gt;hadException())
+        return std::make_pair(SpeciesConstructResult::Exception, nullptr);
+    return std::make_pair(SpeciesConstructResult::CreatedObject, newObject);
+}
+
</ins><span class="cx"> static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
</span><span class="cx"> {
</span><span class="cx">     JSValue value = exec-&gt;argument(argument);
</span><span class="lines">@@ -532,6 +586,11 @@
</span><span class="cx">     JSValue curArg = thisValue.toObject(exec);
</span><span class="cx">     Checked&lt;unsigned, RecordOverflow&gt; finalArraySize = 0;
</span><span class="cx"> 
</span><ins>+    // We need to do species construction before geting the rest of the elements.
+    std::pair&lt;SpeciesConstructResult, JSObject*&gt; speciesResult = speciesConstructArray(exec, curArg.getObject(), 0);
+    if (speciesResult.first == SpeciesConstructResult::Exception)
+        return JSValue::encode(jsUndefined());
+
</ins><span class="cx">     JSArray* currentArray = nullptr;
</span><span class="cx">     JSArray* previousArray = nullptr;
</span><span class="cx">     for (unsigned i = 0; ; ++i) {
</span><span class="lines">@@ -552,16 +611,22 @@
</span><span class="cx">     if (finalArraySize.hasOverflowed())
</span><span class="cx">         return JSValue::encode(throwOutOfMemoryError(exec));
</span><span class="cx"> 
</span><del>-    if (argCount == 1 &amp;&amp; previousArray &amp;&amp; currentArray &amp;&amp; finalArraySize.unsafeGet() &lt; MIN_SPARSE_ARRAY_INDEX) {
</del><ins>+    if (speciesResult.first == SpeciesConstructResult::FastPath &amp;&amp; argCount == 1 &amp;&amp; previousArray &amp;&amp; currentArray &amp;&amp; finalArraySize.unsafeGet() &lt; MIN_SPARSE_ARRAY_INDEX) {
</ins><span class="cx">         IndexingType type = JSArray::fastConcatType(exec-&gt;vm(), *previousArray, *currentArray);
</span><span class="cx">         if (type != NonArray)
</span><span class="cx">             return previousArray-&gt;fastConcatWith(*exec, *currentArray);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    JSArray* arr = constructEmptyArray(exec, nullptr, finalArraySize.unsafeGet());
-    if (exec-&gt;hadException())
-        return JSValue::encode(jsUndefined());
</del><ins>+    ASSERT(speciesResult.first != SpeciesConstructResult::Exception);
</ins><span class="cx"> 
</span><ins>+    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());
+    }
+
</ins><span class="cx">     curArg = thisValue.toObject(exec);
</span><span class="cx">     unsigned n = 0;
</span><span class="cx">     for (unsigned i = 0; ; ++i) {
</span><span class="lines">@@ -575,19 +640,19 @@
</span><span class="cx">                 if (exec-&gt;hadException())
</span><span class="cx">                     return JSValue::encode(jsUndefined());
</span><span class="cx">                 if (v)
</span><del>-                    arr-&gt;putDirectIndex(exec, n, v);
</del><ins>+                    result-&gt;putDirectIndex(exec, n, v);
</ins><span class="cx">                 n++;
</span><span class="cx">             }
</span><span class="cx">         } else {
</span><del>-            arr-&gt;putDirectIndex(exec, n, curArg);
</del><ins>+            result-&gt;putDirectIndex(exec, n, curArg);
</ins><span class="cx">             n++;
</span><span class="cx">         }
</span><span class="cx">         if (i == argCount)
</span><span class="cx">             break;
</span><span class="cx">         curArg = exec-&gt;uncheckedArgument(i);
</span><span class="cx">     }
</span><del>-    arr-&gt;setLength(exec, n);
-    return JSValue::encode(arr);
</del><ins>+    setLength(exec, result, n);
+    return JSValue::encode(result);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
</span><span class="lines">@@ -757,12 +822,21 @@
</span><span class="cx">     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
</span><span class="cx">     unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
</span><span class="cx"> 
</span><del>-    if (isJSArray(thisObj)) {
</del><ins>+    std::pair&lt;SpeciesConstructResult, JSObject*&gt; 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 &amp;&amp; isJSArray(thisObj))) {
</ins><span class="cx">         if (JSArray* result = asArray(thisObj)-&gt;fastSlice(*exec, begin, end - begin))
</span><span class="cx">             return JSValue::encode(result);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    JSArray* result = constructEmptyArray(exec, nullptr, end - begin);
</del><ins>+    JSObject* result;
+    if (speciesResult.first == SpeciesConstructResult::CreatedObject)
+        result = speciesResult.second;
+    else
+        result = constructEmptyArray(exec, nullptr, end - begin);
</ins><span class="cx"> 
</span><span class="cx">     unsigned n = 0;
</span><span class="cx">     for (unsigned k = begin; k &lt; end; k++, n++) {
</span><span class="lines">@@ -772,7 +846,7 @@
</span><span class="cx">         if (v)
</span><span class="cx">             result-&gt;putDirectIndex(exec, n, v);
</span><span class="cx">     }
</span><del>-    result-&gt;setLength(exec, n);
</del><ins>+    setLength(exec, result, n);
</ins><span class="cx">     return JSValue::encode(result);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -786,10 +860,22 @@
</span><span class="cx">     unsigned length = getLength(exec, thisObj);
</span><span class="cx">     if (exec-&gt;hadException())
</span><span class="cx">         return JSValue::encode(jsUndefined());
</span><del>-    
-    if (!exec-&gt;argumentCount())
-        return JSValue::encode(constructEmptyArray(exec, nullptr));
</del><span class="cx"> 
</span><ins>+    if (!exec-&gt;argumentCount()) {
+        std::pair&lt;SpeciesConstructResult, JSObject*&gt; 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);
+    }
+
</ins><span class="cx">     unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
</span><span class="cx"> 
</span><span class="cx">     unsigned deleteCount = length - begin;
</span><span class="lines">@@ -803,15 +889,22 @@
</span><span class="cx">             deleteCount = static_cast&lt;unsigned&gt;(deleteDouble);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    JSArray* result = nullptr;
</del><ins>+    std::pair&lt;SpeciesConstructResult, JSObject*&gt; speciesResult = speciesConstructArray(exec, thisObj, deleteCount);
+    if (speciesResult.first == SpeciesConstructResult::Exception)
+        return JSValue::encode(jsUndefined());
</ins><span class="cx"> 
</span><del>-    if (isJSArray(thisObj))
</del><ins>+    JSObject* result = nullptr;
+    if (speciesResult.first == SpeciesConstructResult::FastPath &amp;&amp; isJSArray(thisObj))
</ins><span class="cx">         result = asArray(thisObj)-&gt;fastSlice(*exec, begin, deleteCount);
</span><span class="cx"> 
</span><span class="cx">     if (!result) {
</span><del>-        result = JSArray::tryCreateUninitialized(vm, exec-&gt;lexicalGlobalObject()-&gt;arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount);
-        if (!result)
-            return JSValue::encode(throwOutOfMemoryError(exec));
</del><ins>+        if (speciesResult.first == SpeciesConstructResult::CreatedObject)
+            result = speciesResult.second;
+        else {
+            result = JSArray::tryCreateUninitialized(vm, exec-&gt;lexicalGlobalObject()-&gt;arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount);
+            if (!result)
+                return JSValue::encode(throwOutOfMemoryError(exec));
+        }
</ins><span class="cx"> 
</span><span class="cx">         for (unsigned k = 0; k &lt; deleteCount; ++k) {
</span><span class="cx">             JSValue v = getProperty(exec, thisObj, k + begin);
</span><span class="lines">@@ -837,7 +930,7 @@
</span><span class="cx">             return JSValue::encode(jsUndefined());
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    putLength(exec, thisObj, jsNumber(length - deleteCount + additionalArgs));
</del><ins>+    setLength(exec, thisObj, length - deleteCount + additionalArgs);
</ins><span class="cx">     return JSValue::encode(result);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -943,4 +1036,48 @@
</span><span class="cx">     return JSValue::encode(JSArrayIterator::create(exec, exec-&gt;callee()-&gt;globalObject()-&gt;arrayIteratorStructure(), ArrayIterateKey, thisObj));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// -------------------- ArrayPrototype.constructor Watchpoint ------------------
+
+class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
+public:
+    typedef AdaptiveInferredPropertyValueWatchpointBase Base;
+    ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&amp;, ArrayPrototype*);
+
+private:
+    virtual void handleFire(const FireDetail&amp;) override;
+
+    ArrayPrototype* m_arrayPrototype;
+};
+
+void ArrayPrototype::setConstructor(VM&amp; vm, JSObject* constructorProperty, unsigned attributes)
+{
+    putDirectWithoutTransition(vm, vm.propertyNames-&gt;constructor, constructorProperty, attributes);
+
+    PropertyOffset offset = this-&gt;structure()-&gt;get(vm, vm.propertyNames-&gt;constructor);
+    ASSERT(isValidOffset(offset));
+    this-&gt;structure()-&gt;startWatchingPropertyForReplacements(vm, offset);
+
+    ObjectPropertyCondition condition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames-&gt;constructor.impl(), constructorProperty);
+    ASSERT(condition.isWatchable());
+
+    m_constructorWatchpoint = std::make_unique&lt;ArrayPrototypeAdaptiveInferredPropertyWatchpoint&gt;(condition, this);
+    m_constructorWatchpoint-&gt;install();
+}
+
+ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&amp; key, ArrayPrototype* prototype)
+    : Base(key)
+    , m_arrayPrototype(prototype)
+{
+}
+
+void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail&amp; detail)
+{
+    StringPrintStream out;
+    out.print(&quot;ArrayPrototype adaption of &quot;, key(), &quot; failed: &quot;, detail);
+
+    StringFireDetail stringDetail(out.toCString().data());
+
+    m_arrayPrototype-&gt;m_didChangeConstructorProperty = true;
+}
+
</ins><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeArrayPrototypeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h (195877 => 195878)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h        2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h        2016-01-30 02:45:25 UTC (rev 195878)
</span><span class="lines">@@ -26,6 +26,8 @@
</span><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><ins>+class ArrayPrototypeAdaptiveInferredPropertyWatchpoint;
+
</ins><span class="cx"> class ArrayPrototype : public JSArray {
</span><span class="cx"> private:
</span><span class="cx">     ArrayPrototype(VM&amp;, Structure*);
</span><span class="lines">@@ -42,8 +44,18 @@
</span><span class="cx">         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), ArrayClass);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    void setConstructor(VM&amp;, JSObject* constructorProperty, unsigned attributes);
+
+    bool didChangeConstructorProperty() const { return m_didChangeConstructorProperty; }
+
</ins><span class="cx"> protected:
</span><span class="cx">     void finishCreation(VM&amp;, JSGlobalObject*);
</span><ins>+
+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&lt;ArrayPrototypeAdaptiveInferredPropertyWatchpoint&gt; m_constructorWatchpoint;
+    bool m_didChangeConstructorProperty = false;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeConstructDatacpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ConstructData.cpp (195877 => 195878)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ConstructData.cpp        2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/JavaScriptCore/runtime/ConstructData.cpp        2016-01-30 02:45:25 UTC (rev 195878)
</span><span class="lines">@@ -35,6 +35,17 @@
</span><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><ins>+JSObject* construct(ExecState* exec, JSValue constructorObject, const ArgList&amp; args, const String&amp; errorMessage)
+{
+    ConstructData constructData;
+    ConstructType constructType = getConstructData(constructorObject, constructData);
+    if (constructType == ConstructTypeNone)
+        return throwTypeError(exec, errorMessage);
+
+    return construct(exec, constructorObject, constructType, constructData, args, constructorObject);
+}
+
+
</ins><span class="cx"> JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType constructType, const ConstructData&amp; constructData, const ArgList&amp; args, JSValue newTarget)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(constructType == ConstructTypeJS || constructType == ConstructTypeHost);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeConstructDatah"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ConstructData.h (195877 => 195878)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ConstructData.h        2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/JavaScriptCore/runtime/ConstructData.h        2016-01-30 02:45:25 UTC (rev 195878)
</span><span class="lines">@@ -56,6 +56,8 @@
</span><span class="cx">     } js;
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+// 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&amp;, const String&amp; errorMessage);
</ins><span class="cx"> JS_EXPORT_PRIVATE JSObject* construct(ExecState*, JSValue constructor, ConstructType, const ConstructData&amp;, const ArgList&amp;, JSValue newTarget);
</span><span class="cx"> 
</span><span class="cx"> ALWAYS_INLINE JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType constructType, const ConstructData&amp; constructData, const ArgList&amp; args)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSCJSValueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSCJSValue.h (195877 => 195878)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSCJSValue.h        2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/JavaScriptCore/runtime/JSCJSValue.h        2016-01-30 02:45:25 UTC (rev 195878)
</span><span class="lines">@@ -219,6 +219,7 @@
</span><span class="cx">     // Querying the type.
</span><span class="cx">     bool isEmpty() const;
</span><span class="cx">     bool isFunction() const;
</span><ins>+    bool isConstructor() const;
</ins><span class="cx">     bool isUndefined() const;
</span><span class="cx">     bool isNull() const;
</span><span class="cx">     bool isUndefinedOrNull() const;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSCJSValueInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSCJSValueInlines.h (195877 => 195878)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSCJSValueInlines.h        2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/JavaScriptCore/runtime/JSCJSValueInlines.h        2016-01-30 02:45:25 UTC (rev 195878)
</span><span class="lines">@@ -682,6 +682,16 @@
</span><span class="cx">     return isCell() &amp;&amp; (asCell()-&gt;inherits(JSFunction::info()) || asCell()-&gt;inherits(InternalFunction::info()));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// 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;
+}
+
</ins><span class="cx"> // this method is here to be after the inline declaration of JSCell::inherits
</span><span class="cx"> inline bool JSValue::inherits(const ClassInfo* classInfo) const
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSGlobalObjectcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp (195877 => 195878)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp        2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp        2016-01-30 02:45:25 UTC (rev 195878)
</span><span class="lines">@@ -404,7 +404,7 @@
</span><span class="cx">     m_definePropertyFunction.set(vm, this, definePropertyFunction);
</span><span class="cx"> 
</span><span class="cx">     JSCell* functionConstructor = FunctionConstructor::create(vm, FunctionConstructor::createStructure(vm, this, m_functionPrototype.get()), m_functionPrototype.get());
</span><del>-    JSCell* arrayConstructor = ArrayConstructor::create(vm, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get(), speciesGetterSetter);
</del><ins>+    JSObject* arrayConstructor = ArrayConstructor::create(vm, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get(), speciesGetterSetter);
</ins><span class="cx">     
</span><span class="cx">     m_regExpConstructor.set(vm, this, RegExpConstructor::create(vm, RegExpConstructor::createStructure(vm, this, m_functionPrototype.get()), m_regExpPrototype.get(), speciesGetterSetter));
</span><span class="cx">     
</span><span class="lines">@@ -439,7 +439,7 @@
</span><span class="cx">     
</span><span class="cx">     m_objectPrototype-&gt;putDirectWithoutTransition(vm, vm.propertyNames-&gt;constructor, objectConstructor, DontEnum);
</span><span class="cx">     m_functionPrototype-&gt;putDirectWithoutTransition(vm, vm.propertyNames-&gt;constructor, functionConstructor, DontEnum);
</span><del>-    m_arrayPrototype-&gt;putDirectWithoutTransition(vm, vm.propertyNames-&gt;constructor, arrayConstructor, DontEnum);
</del><ins>+    m_arrayPrototype-&gt;setConstructor(vm, arrayConstructor, DontEnum);
</ins><span class="cx">     m_regExpPrototype-&gt;putDirectWithoutTransition(vm, vm.propertyNames-&gt;constructor, m_regExpConstructor.get(), DontEnum);
</span><span class="cx">     
</span><span class="cx">     putDirectWithoutTransition(vm, vm.propertyNames-&gt;Object, objectConstructor, DontEnum);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestses6yaml"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tests/es6.yaml (195877 => 195878)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/es6.yaml        2016-01-30 02:36:14 UTC (rev 195877)
+++ trunk/Source/JavaScriptCore/tests/es6.yaml        2016-01-30 02:45:25 UTC (rev 195878)
</span><span class="lines">@@ -717,15 +717,15 @@
</span><span class="cx"> - path: es6/Array_is_subclassable_Array.of.js
</span><span class="cx">   cmd: runES6 :normal
</span><span class="cx"> - path: es6/Array_is_subclassable_Array.prototype.concat.js
</span><del>-  cmd: runES6 :fail
</del><ins>+  cmd: runES6 :normal
</ins><span class="cx"> - path: es6/Array_is_subclassable_Array.prototype.filter.js
</span><span class="cx">   cmd: runES6 :fail
</span><span class="cx"> - path: es6/Array_is_subclassable_Array.prototype.map.js
</span><span class="cx">   cmd: runES6 :fail
</span><span class="cx"> - path: es6/Array_is_subclassable_Array.prototype.slice.js
</span><del>-  cmd: runES6 :fail
</del><ins>+  cmd: runES6 :normal
</ins><span class="cx"> - path: es6/Array_is_subclassable_Array.prototype.splice.js
</span><del>-  cmd: runES6 :fail
</del><ins>+  cmd: runES6 :normal
</ins><span class="cx"> - path: es6/Array_is_subclassable_correct_prototype_chain.js
</span><span class="cx">   cmd: runES6 :normal
</span><span class="cx"> - path: es6/Array_static_methods_Array.from_generator_instances.js
</span><span class="lines">@@ -1195,15 +1195,15 @@
</span><span class="cx"> - path: es6/well-known_symbols_Symbol.search.js
</span><span class="cx">   cmd: runES6 :fail
</span><span class="cx"> - path: es6/well-known_symbols_Symbol.species_Array.prototype.concat.js
</span><del>-  cmd: runES6 :fail
</del><ins>+  cmd: runES6 :normal
</ins><span class="cx"> - path: es6/well-known_symbols_Symbol.species_Array.prototype.filter.js
</span><span class="cx">   cmd: runES6 :fail
</span><span class="cx"> - path: es6/well-known_symbols_Symbol.species_Array.prototype.map.js
</span><span class="cx">   cmd: runES6 :fail
</span><span class="cx"> - path: es6/well-known_symbols_Symbol.species_Array.prototype.slice.js
</span><del>-  cmd: runES6 :fail
</del><ins>+  cmd: runES6 :normal
</ins><span class="cx"> - path: es6/well-known_symbols_Symbol.species_Array.prototype.splice.js
</span><del>-  cmd: runES6 :fail
</del><ins>+  cmd: runES6 :normal
</ins><span class="cx"> - path: es6/well-known_symbols_Symbol.species_existence.js
</span><span class="cx">   cmd: runES6 :normal
</span><span class="cx"> - path: es6/well-known_symbols_Symbol.species_RegExp.prototype[Symbol.split].js
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressarrayspeciesfunctionsjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/array-species-functions.js (0 => 195878)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/array-species-functions.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/array-species-functions.js        2016-01-30 02:45:25 UTC (rev 195878)
</span><span class="lines">@@ -0,0 +1,79 @@
</span><ins>+C = class extends Array { }
+N = class { }
+N[Symbol.species] = function() { throw &quot;this should never be called&quot;; }
+
+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) {
+    &quot;use strict&quot;;
+    let [protoFunction, args] = testData;
+    let foo = new C(10);
+    let n = new N();
+
+    // Test non-array ignores constructor.
+    objProp(n, &quot;constructor&quot;, { value: C });
+    let bar = protoFunction.call(...[n, ...args]);
+    if (!(bar instanceof Array) || bar instanceof N || bar instanceof C)
+        throw Error();
+
+    objProp(foo, &quot;constructor&quot;, { value: null });
+    if (!funcThrows(protoFunction, [foo, ...args]))
+        throw &quot;didn't throw&quot;;
+
+    // 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(&quot;failed on &quot; + protoFunction);
+
+    function speciesThrows() {
+        throw Error();
+    }
+
+    C[Symbol.species] = speciesThrows;
+    if (!funcThrows(protoFunction, [foo, ...args]))
+        throw &quot;didn't throw&quot;;
+
+    C[Symbol.species] = true;
+    if (!funcThrows(protoFunction, [foo, ...args]))
+        throw &quot;didn't throw&quot;;
+
+}
+
+testFunctions.forEach(test);
</ins></span></pre>
</div>
</div>

</body>
</html>