[ES6] Add support for Symbol.isConcatSpreadable.
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Apr 2016 00:50:44 +0000 (00:50 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Apr 2016 00:50:44 +0000 (00:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=155351

Reviewed by Saam Barati.

Source/JavaScriptCore:

This patch adds support for Symbol.isConcatSpreadable. In order to do so it was necessary to move the
Array.prototype.concat function to JS. A number of different optimizations were needed to make such the move to
a builtin performant. First, four new DFG intrinsics were added.

1) IsArrayObject (I would have called it IsArray but we use the same name for an IndexingType): an intrinsic of
   the Array.isArray function.
2) IsJSArray: checks the first child is a JSArray object.
3) IsArrayConstructor: checks the first child is an instance of ArrayConstructor.
4) CallObjectConstructor: an intrinsic of the Object constructor.

IsActualObject, IsJSArray, and CallObjectConstructor can all be converted into constants in the abstract interpreter if
we are able to prove that the first child is an Array or for ToObject an Object.

In order to further improve the perfomance we also now cover more indexing types in our fast path memcpy
code. Before we would only memcpy Arrays if they had the same indexing type and did not have Array storage and
were not undecided. Now the memcpy code covers the following additional two cases: One array is undecided and
the other is a non-array storage and the case where one array is Int32 and the other is contiguous (we map this
into a contiguous array).

This patch also adds a new fast path for concat with more than one array argument by using memcpy to append
values onto the result array. This works roughly the same as the two array fast path using the same methodology
to decide if we can memcpy the other butterfly into the result butterfly.

Two new debugging tools are also added to the jsc cli. One is a version of the print function with a private
name so it can be used for debugging builtins. The other is dumpDataLog, which takes a JSValue and runs our
dataLog function on it.

Finally, this patch add a new constructor to JSValueRegsTemporary that allows it to reuse the the registers of a
JSValueOperand if the operand's use count is one.

* JavaScriptCore.xcodeproj/project.pbxproj:
* builtins/ArrayPrototype.js:
(concatSlowPath):
(concat):
* bytecode/BytecodeIntrinsicRegistry.cpp:
(JSC::BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry):
* bytecode/BytecodeIntrinsicRegistry.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
(JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileCurrentBlock):
(JSC::DFG::SpeculativeJIT::compileIsJSArray):
(JSC::DFG::SpeculativeJIT::compileIsArrayObject):
(JSC::DFG::SpeculativeJIT::compileIsArrayConstructor):
(JSC::DFG::SpeculativeJIT::compileCallObjectConstructor):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCallObjectConstructor):
(JSC::FTL::DFG::LowerDFGToB3::compileIsArrayObject):
(JSC::FTL::DFG::LowerDFGToB3::compileIsJSArray):
(JSC::FTL::DFG::LowerDFGToB3::compileIsArrayConstructor):
(JSC::FTL::DFG::LowerDFGToB3::isArray):
* jit/JITOperations.h:
* jsc.cpp:
(WTF::RuntimeArray::createStructure):
(GlobalObject::finishCreation):
(functionDebug):
(functionDataLogValue):
* runtime/ArrayConstructor.cpp:
(JSC::ArrayConstructor::finishCreation):
(JSC::arrayConstructorPrivateFuncIsArrayConstructor):
* runtime/ArrayConstructor.h:
(JSC::isArrayConstructor):
* runtime/ArrayPrototype.cpp:
(JSC::ArrayPrototype::finishCreation):
(JSC::arrayProtoPrivateFuncIsJSArray):
(JSC::moveElements):
(JSC::arrayProtoPrivateFuncConcatMemcpy):
(JSC::arrayProtoPrivateFuncAppendMemcpy):
(JSC::arrayProtoFuncConcat): Deleted.
* runtime/ArrayPrototype.h:
(JSC::ArrayPrototype::createStructure):
* runtime/CommonIdentifiers.h:
* runtime/Intrinsic.h:
* runtime/JSArray.cpp:
(JSC::JSArray::appendMemcpy):
(JSC::JSArray::fastConcatWith): Deleted.
* runtime/JSArray.h:
(JSC::JSArray::createStructure):
(JSC::JSArray::fastConcatType): Deleted.
* runtime/JSArrayInlines.h: Added.
(JSC::JSArray::memCopyWithIndexingType):
(JSC::JSArray::canFastCopy):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSType.h:
* runtime/ObjectConstructor.h:
(JSC::constructObject):
* tests/es6.yaml:
* tests/stress/array-concat-spread-object.js: Added.
(arrayEq):
* tests/stress/array-concat-spread-proxy-exception-check.js: Added.
(arrayEq):
* tests/stress/array-concat-spread-proxy.js: Added.
(arrayEq):
* tests/stress/array-concat-with-slow-indexingtypes.js: Added.
(arrayEq):
* tests/stress/array-species-config-array-constructor.js:

Source/WebCore:

Makes runtime arrays have the new ArrayType

* bridge/runtime_array.h:
(JSC::RuntimeArray::createStructure):

LayoutTests:

Fix tests for Symbol.isConcatSpreadable on the Symbol object.

* js/Object-getOwnPropertyNames-expected.txt:
* js/dom/array-prototype-properties-expected.txt:
* js/script-tests/Object-getOwnPropertyNames.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@199128 268f45cc-cd09-0410-ab3c-d52691b4dbfc

47 files changed:
LayoutTests/ChangeLog
LayoutTests/js/Object-getOwnPropertyNames-expected.txt
LayoutTests/js/dom/array-prototype-properties-expected.txt
LayoutTests/js/script-tests/Object-getOwnPropertyNames.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/builtins/ArrayPrototype.js
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.cpp
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/ArrayConstructor.cpp
Source/JavaScriptCore/runtime/ArrayConstructor.h
Source/JavaScriptCore/runtime/ArrayPrototype.cpp
Source/JavaScriptCore/runtime/ArrayPrototype.h
Source/JavaScriptCore/runtime/CommonIdentifiers.h
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSArray.cpp
Source/JavaScriptCore/runtime/JSArray.h
Source/JavaScriptCore/runtime/JSArrayInlines.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSType.h
Source/JavaScriptCore/runtime/ObjectConstructor.h
Source/JavaScriptCore/tests/es6.yaml
Source/JavaScriptCore/tests/stress/array-concat-spread-object.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/array-concat-spread-proxy-exception-check.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/array-concat-spread-proxy.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/array-concat-with-slow-indexingtypes.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/array-species-config-array-constructor.js
Source/WebCore/ChangeLog
Source/WebCore/bridge/runtime_array.h

index bd33bb6..95e621e 100644 (file)
@@ -1,3 +1,16 @@
+2016-03-29  Keith Miller  <keith_miller@apple.com>
+
+        [ES6] Add support for Symbol.isConcatSpreadable.
+        https://bugs.webkit.org/show_bug.cgi?id=155351
+
+        Reviewed by Saam Barati.
+
+        Fix tests for Symbol.isConcatSpreadable on the Symbol object.
+
+        * js/Object-getOwnPropertyNames-expected.txt:
+        * js/dom/array-prototype-properties-expected.txt:
+        * js/script-tests/Object-getOwnPropertyNames.js:
+
 2016-04-06  Sam Weinig  <sam@webkit.org>
 
         window.CSS should be a constructor with static functions
index ece9097..5f73b0b 100644 (file)
@@ -61,7 +61,7 @@ PASS getSortedOwnPropertyNames(Error) is ['length', 'name', 'prototype']
 PASS getSortedOwnPropertyNames(Error.prototype) is ['constructor', 'message', 'name', 'toString']
 PASS getSortedOwnPropertyNames(Math) is ['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']
 PASS getSortedOwnPropertyNames(JSON) is ['parse', 'stringify']
-PASS getSortedOwnPropertyNames(Symbol) is ['for', 'hasInstance', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'search', 'species', 'toPrimitive', 'toStringTag', 'unscopables']
+PASS getSortedOwnPropertyNames(Symbol) is ['for', 'hasInstance', 'isConcatSpreadable', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'search', 'species', 'toPrimitive', 'toStringTag', 'unscopables']
 PASS getSortedOwnPropertyNames(Symbol.prototype) is ['constructor', 'toString', 'valueOf']
 PASS getSortedOwnPropertyNames(Map) is ['length', 'name', 'prototype']
 PASS getSortedOwnPropertyNames(Map.prototype) is ['clear', 'constructor', 'delete', 'entries', 'forEach', 'get', 'has', 'keys', 'set', 'size', 'values']
index c4e9afc..5228eb6 100644 (file)
@@ -5,7 +5,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 PASS Array.prototype.toString.call(undefined) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.toString.call(undefined)').
 PASS Array.prototype.toLocaleString.call(undefined) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.toLocaleString.call(undefined)').
-PASS Array.prototype.concat.call(undefined, []) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.concat.call(undefined, [])').
+PASS Array.prototype.concat.call(undefined, []) threw exception TypeError: Array.prototype.concat requires that |this| not be undefined.
 PASS Array.prototype.join.call(undefined, []) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.join.call(undefined, [])').
 PASS Array.prototype.pop.call(undefined) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.pop.call(undefined)').
 PASS Array.prototype.push.call(undefined, {}) threw exception TypeError: undefined is not an object (evaluating 'Array.prototype.push.call(undefined, {})').
index 2345df6..6f5316b 100644 (file)
@@ -70,7 +70,7 @@ var expectedPropertyNamesSet = {
     "Error.prototype": "['constructor', 'message', 'name', 'toString']",
     "Math": "['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']",
     "JSON": "['parse', 'stringify']",
-    "Symbol": "['for', 'hasInstance', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'search', 'species', 'toPrimitive', 'toStringTag', 'unscopables']",
+    "Symbol": "['for', 'hasInstance', 'isConcatSpreadable', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'search', 'species', 'toPrimitive', 'toStringTag', 'unscopables']",
     "Symbol.prototype": "['constructor', 'toString', 'valueOf']",
     "Map": "['length', 'name', 'prototype']",
     "Map.prototype": "['clear', 'constructor', 'delete', 'entries', 'forEach', 'get', 'has', 'keys', 'set', 'size', 'values']",
index 6a3dccb..fea0a5a 100644 (file)
@@ -1,3 +1,133 @@
+2016-03-29  Keith Miller  <keith_miller@apple.com>
+
+        [ES6] Add support for Symbol.isConcatSpreadable.
+        https://bugs.webkit.org/show_bug.cgi?id=155351
+
+        Reviewed by Saam Barati.
+
+        This patch adds support for Symbol.isConcatSpreadable. In order to do so it was necessary to move the
+        Array.prototype.concat function to JS. A number of different optimizations were needed to make such the move to
+        a builtin performant. First, four new DFG intrinsics were added.
+
+        1) IsArrayObject (I would have called it IsArray but we use the same name for an IndexingType): an intrinsic of
+           the Array.isArray function.
+        2) IsJSArray: checks the first child is a JSArray object.
+        3) IsArrayConstructor: checks the first child is an instance of ArrayConstructor.
+        4) CallObjectConstructor: an intrinsic of the Object constructor.
+
+        IsActualObject, IsJSArray, and CallObjectConstructor can all be converted into constants in the abstract interpreter if
+        we are able to prove that the first child is an Array or for ToObject an Object.
+
+        In order to further improve the perfomance we also now cover more indexing types in our fast path memcpy
+        code. Before we would only memcpy Arrays if they had the same indexing type and did not have Array storage and
+        were not undecided. Now the memcpy code covers the following additional two cases: One array is undecided and
+        the other is a non-array storage and the case where one array is Int32 and the other is contiguous (we map this
+        into a contiguous array).
+
+        This patch also adds a new fast path for concat with more than one array argument by using memcpy to append
+        values onto the result array. This works roughly the same as the two array fast path using the same methodology
+        to decide if we can memcpy the other butterfly into the result butterfly.
+
+        Two new debugging tools are also added to the jsc cli. One is a version of the print function with a private
+        name so it can be used for debugging builtins. The other is dumpDataLog, which takes a JSValue and runs our
+        dataLog function on it.
+
+        Finally, this patch add a new constructor to JSValueRegsTemporary that allows it to reuse the the registers of a
+        JSValueOperand if the operand's use count is one.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * builtins/ArrayPrototype.js:
+        (concatSlowPath):
+        (concat):
+        * bytecode/BytecodeIntrinsicRegistry.cpp:
+        (JSC::BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry):
+        * bytecode/BytecodeIntrinsicRegistry.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        (JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileCurrentBlock):
+        (JSC::DFG::SpeculativeJIT::compileIsJSArray):
+        (JSC::DFG::SpeculativeJIT::compileIsArrayObject):
+        (JSC::DFG::SpeculativeJIT::compileIsArrayConstructor):
+        (JSC::DFG::SpeculativeJIT::compileCallObjectConstructor):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallObjectConstructor):
+        (JSC::FTL::DFG::LowerDFGToB3::compileIsArrayObject):
+        (JSC::FTL::DFG::LowerDFGToB3::compileIsJSArray):
+        (JSC::FTL::DFG::LowerDFGToB3::compileIsArrayConstructor):
+        (JSC::FTL::DFG::LowerDFGToB3::isArray):
+        * jit/JITOperations.h:
+        * jsc.cpp:
+        (WTF::RuntimeArray::createStructure):
+        (GlobalObject::finishCreation):
+        (functionDebug):
+        (functionDataLogValue):
+        * runtime/ArrayConstructor.cpp:
+        (JSC::ArrayConstructor::finishCreation):
+        (JSC::arrayConstructorPrivateFuncIsArrayConstructor):
+        * runtime/ArrayConstructor.h:
+        (JSC::isArrayConstructor):
+        * runtime/ArrayPrototype.cpp:
+        (JSC::ArrayPrototype::finishCreation):
+        (JSC::arrayProtoPrivateFuncIsJSArray):
+        (JSC::moveElements):
+        (JSC::arrayProtoPrivateFuncConcatMemcpy):
+        (JSC::arrayProtoPrivateFuncAppendMemcpy):
+        (JSC::arrayProtoFuncConcat): Deleted.
+        * runtime/ArrayPrototype.h:
+        (JSC::ArrayPrototype::createStructure):
+        * runtime/CommonIdentifiers.h:
+        * runtime/Intrinsic.h:
+        * runtime/JSArray.cpp:
+        (JSC::JSArray::appendMemcpy):
+        (JSC::JSArray::fastConcatWith): Deleted.
+        * runtime/JSArray.h:
+        (JSC::JSArray::createStructure):
+        (JSC::JSArray::fastConcatType): Deleted.
+        * runtime/JSArrayInlines.h: Added.
+        (JSC::JSArray::memCopyWithIndexingType):
+        (JSC::JSArray::canFastCopy):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * runtime/JSType.h:
+        * runtime/ObjectConstructor.h:
+        (JSC::constructObject):
+        * tests/es6.yaml:
+        * tests/stress/array-concat-spread-object.js: Added.
+        (arrayEq):
+        * tests/stress/array-concat-spread-proxy-exception-check.js: Added.
+        (arrayEq):
+        * tests/stress/array-concat-spread-proxy.js: Added.
+        (arrayEq):
+        * tests/stress/array-concat-with-slow-indexingtypes.js: Added.
+        (arrayEq):
+        * tests/stress/array-species-config-array-constructor.js:
+
 2016-04-06  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r199070.
index bdfd887..0f83e68 100644 (file)
                5370B4F51BF26202005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5370B4F31BF25EA2005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.cpp */; };
                5370B4F61BF26205005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 5370B4F41BF25EA2005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.h */; };
                53917E7B1B7906FA000EBD33 /* JSGenericTypedArrayViewPrototypeFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 53917E7A1B7906E4000EBD33 /* JSGenericTypedArrayViewPrototypeFunctions.h */; };
+               539FB8BA1C99DA7C00940FA1 /* JSArrayInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 539FB8B91C99DA7C00940FA1 /* JSArrayInlines.h */; };
                53F6BF6D1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F6BF6C1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
                5D53726F0E1C54880021E549 /* Tracing.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D53726E0E1C54880021E549 /* Tracing.h */; };
                5D5D8AD10E0D0EBE00F9C692 /* libedit.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D5D8AD00E0D0EBE00F9C692 /* libedit.dylib */; };
                53917E7A1B7906E4000EBD33 /* JSGenericTypedArrayViewPrototypeFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGenericTypedArrayViewPrototypeFunctions.h; sourceTree = "<group>"; };
                53917E7C1B791106000EBD33 /* JSTypedArrayViewPrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSTypedArrayViewPrototype.h; sourceTree = "<group>"; };
                53917E831B791CB8000EBD33 /* TypedArrayPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = TypedArrayPrototype.js; path = builtins/TypedArrayPrototype.js; sourceTree = SOURCE_ROOT; };
+               539FB8B91C99DA7C00940FA1 /* JSArrayInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSArrayInlines.h; sourceTree = "<group>"; };
                53F256E11B87E28000B4B768 /* JSTypedArrayViewPrototype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSTypedArrayViewPrototype.cpp; sourceTree = "<group>"; };
                53F6BF6C1C3F060A00F41E5D /* InternalFunctionAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InternalFunctionAllocationProfile.h; sourceTree = "<group>"; };
                593D43CCA0BBE06D89C59707 /* MapDataInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapDataInlines.h; sourceTree = "<group>"; };
                                70DC3E081B2DF2C700054299 /* IteratorPrototype.h */,
                                93ADFCE60CCBD7AC00D30B08 /* JSArray.cpp */,
                                938772E5038BFE19008635CE /* JSArray.h */,
+                               539FB8B91C99DA7C00940FA1 /* JSArrayInlines.h */,
                                0F2B66B417B6B5AB00A7AE3F /* JSArrayBuffer.cpp */,
                                0F2B66B517B6B5AB00A7AE3F /* JSArrayBuffer.h */,
                                0F2B66B617B6B5AB00A7AE3F /* JSArrayBufferConstructor.cpp */,
                                0FB7F39B15ED8E4600F167B2 /* IndexingType.h in Headers */,
                                0F0A75231B94BFA900110660 /* InferredType.h in Headers */,
                                0FFC92121B94D4DF0071DD66 /* InferredTypeTable.h in Headers */,
+                               539FB8BA1C99DA7C00940FA1 /* JSArrayInlines.h in Headers */,
                                0FF8BDEB1AD4CF7100DFE884 /* InferredValue.h in Headers */,
                                BC18C4100E16F5CD00B34460 /* InitializeThreading.h in Headers */,
                                A513E5B8185B8BD3007E95AD /* InjectedScript.h in Headers */,
index 1e8e732..7e61e68 100644 (file)
@@ -646,6 +646,94 @@ function sort(comparator)
     return array;
 }
 
+function concatSlowPath()
+{
+    "use strict";
+
+    var argCount = arguments.length;
+    var result = new this.species(0);
+    var resultIsArray = @isJSArray(result);
+
+    var currentElement = this.array;
+    var resultIndex = 0;
+    var argIndex = 0;
+
+    do {
+        var spreadable = @isObject(currentElement) && currentElement[@symbolIsConcatSpreadable];
+        if ((spreadable == @undefined && @isArray(currentElement)) || spreadable) {
+            var length = @toLength(currentElement.length);
+            if (resultIsArray && @isJSArray(currentElement)
+                && @appendMemcpy(result, currentElement)) {
+
+                resultIndex += length;
+            } else {
+                if (length + resultIndex > @MAX_SAFE_INTEGER)
+                    throw @TypeError("length exceeded the maximum safe integer");
+                for (var i = 0; i < length; i++) {
+                    if (i in currentElement)
+                        @putByValDirect(result, resultIndex, currentElement[i]);
+                    resultIndex++;
+                }
+            }
+        } else {
+            if (resultIndex >= @MAX_SAFE_INTEGER)
+                throw @TypeError("length exceeded the maximum safe integer");
+            @putByValDirect(result, resultIndex++, currentElement);
+        }
+        currentElement = arguments[argIndex];
+    } while (argIndex++ < argCount);
+
+    result.length = resultIndex;
+    return result;
+}
+
+function concat(first)
+{
+    "use strict";
+
+    if (this == null) {
+        if (this === null)
+            throw new @TypeError("Array.prototype.concat requires that |this| not be null");
+        throw new @TypeError("Array.prototype.concat requires that |this| not be undefined");
+    }
+
+    var array = @Object(this);
+
+    var constructor;
+    if (@isArray(array)) {
+        constructor = array.constructor;
+        // We have this check so that if some array from a different global object
+        // calls this map they don't get an array with the Array.prototype of the
+        // other global object.
+        if (@isArrayConstructor(constructor) && @Array !== constructor)
+            constructor = @undefined;
+        if (@isObject(constructor)) {
+            constructor = constructor[@symbolSpecies];
+            if (constructor === null)
+                constructor = @Array;
+        }
+    }
+    if (constructor === @undefined)
+        constructor = @Array;
+
+    var result;
+    if (arguments.length === 1
+        && constructor === @Array
+        && @isJSArray(array)
+        && @isJSArray(first)
+        // FIXME: these get_by_ids should be "in"s but using "in" here is a 10% regression.
+        // https://bugs.webkit.org/show_bug.cgi?id=155590
+        && array[@symbolIsConcatSpreadable] == @undefined
+        && first[@symbolIsConcatSpreadable] == @undefined) {
+
+        result = @concatMemcpy(array, first);
+        if (result !== null)
+            return result;
+    }
+
+    return @concatSlowPath.@apply({ array: array, species: constructor }, arguments);
+}
+
 function copyWithin(target, start /*, end */)
 {
     "use strict";
index bbbc3ff..d5854b0 100644 (file)
@@ -49,9 +49,11 @@ BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry(VM& vm)
     m_arrayIterationKindValue.set(m_vm, jsNumber(ArrayIterateValue));
     m_arrayIterationKindKeyValue.set(m_vm, jsNumber(ArrayIterateKeyValue));
     m_MAX_STRING_LENGTH.set(m_vm, jsNumber(JSString::MaxLength));
+    m_MAX_SAFE_INTEGER.set(m_vm, jsDoubleNumber(9007199254740991.0)); // 2 ^ 53 - 1
     m_promiseStatePending.set(m_vm, jsNumber(static_cast<unsigned>(JSPromise::Status::Pending)));
     m_promiseStateFulfilled.set(m_vm, jsNumber(static_cast<unsigned>(JSPromise::Status::Fulfilled)));
     m_promiseStateRejected.set(m_vm, jsNumber(static_cast<unsigned>(JSPromise::Status::Rejected)));
+    m_symbolIsConcatSpreadable.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->isConcatSpreadableSymbol.impl())));
     m_symbolIterator.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->iteratorSymbol.impl())));
     m_symbolMatch.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->matchSymbol.impl())));
     m_symbolSearch.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->searchSymbol.impl())));
index bf42a31..0204af5 100644 (file)
@@ -51,9 +51,11 @@ class Identifier;
     macro(arrayIterationKindValue) \
     macro(arrayIterationKindKeyValue) \
     macro(MAX_STRING_LENGTH) \
+    macro(MAX_SAFE_INTEGER) \
     macro(promiseStatePending) \
     macro(promiseStateFulfilled) \
     macro(promiseStateRejected) \
+    macro(symbolIsConcatSpreadable) \
     macro(symbolIterator) \
     macro(symbolMatch) \
     macro(symbolSearch) \
index 7ca6bbc..7d2ee77 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(DFG_JIT)
 
+#include "ArrayConstructor.h"
 #include "DFGAbstractInterpreter.h"
 #include "GetByIdStatus.h"
 #include "GetterSetter.h"
@@ -955,7 +956,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         }
         break;
     }
-        
+
+    case IsArrayObject:
+    case IsJSArray:
+    case IsArrayConstructor:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
@@ -967,6 +971,28 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         if (child.value()) {
             bool constantWasSet = true;
             switch (node->op()) {
+            case IsArrayObject:
+                if (child.value().isObject()) {
+                    if (child.value().getObject()->type() == ArrayType) {
+                        setConstant(node, jsBoolean(true));
+                        break;
+                    }
+
+                    if (child.value().getObject()->type() == ProxyObjectType) {
+                        // We have no way of knowing what type we are proxing yet.
+                        constantWasSet = false;
+                        break;
+                    }
+                }
+
+                setConstant(node, jsBoolean(false));
+                break;
+            case IsJSArray:
+                setConstant(node, jsBoolean(child.value().isObject() && child.value().getObject()->type() == ArrayType));
+                break;
+            case IsArrayConstructor:
+                setConstant(node, jsBoolean(child.value().isObject() && child.value().getObject()->classInfo() == ArrayConstructor::info()));
+                break;
             case IsUndefined:
                 setConstant(node, jsBoolean(
                     child.value().isCell()
@@ -1029,6 +1055,22 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         
         bool constantWasSet = false;
         switch (node->op()) {
+        case IsJSArray:
+        case IsArrayObject:
+            // We don't have a SpeculatedType for Proxies yet so we can't do better at proving false.
+            if (!(child.m_type & ~SpecArray)) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+
+            if (!(child.m_type & SpecObject)) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+
+            break;
         case IsUndefined:
             // FIXME: Use the masquerades-as-undefined watchpoint thingy.
             // https://bugs.webkit.org/show_bug.cgi?id=144456
@@ -1782,7 +1824,21 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         ASSERT(node->structure());
         forNode(node).set(m_graph, node->structure());
         break;
-        
+
+    case CallObjectConstructor: {
+        AbstractValue& source = forNode(node->child1());
+        AbstractValue& destination = forNode(node);
+
+        if (!(source.m_type & ~SpecObject)) {
+            m_state.setFoundConstants(true);
+            destination = source;
+            break;
+        }
+
+        forNode(node).setType(m_graph, SpecObject);
+        break;
+    }
+
     case PhantomNewObject:
     case PhantomNewFunction:
     case PhantomNewGeneratorFunction:
index 0018f6f..3558d6a 100644 (file)
@@ -45,6 +45,7 @@
 #include "JSLexicalEnvironment.h"
 #include "JSCInlines.h"
 #include "JSModuleEnvironment.h"
+#include "ObjectConstructor.h"
 #include "PreciseJumpTargets.h"
 #include "PutByIdFlags.h"
 #include "PutByIdStatus.h"
@@ -2141,6 +2142,34 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         }
     }
 
+    case IsArrayIntrinsic: {
+        if (argumentCountIncludingThis != 2)
+            return false;
+
+        insertChecks();
+        Node* isArray = addToGraph(IsArrayObject, OpInfo(prediction), get(virtualRegisterForArgument(1, registerOffset)));
+        set(VirtualRegister(resultOperand), isArray);
+        return true;
+    }
+
+    case IsJSArrayIntrinsic: {
+        ASSERT(argumentCountIncludingThis == 2);
+
+        insertChecks();
+        Node* isArray = addToGraph(IsJSArray, OpInfo(prediction), get(virtualRegisterForArgument(1, registerOffset)));
+        set(VirtualRegister(resultOperand), isArray);
+        return true;
+    }
+
+    case IsArrayConstructorIntrinsic: {
+        ASSERT(argumentCountIncludingThis == 2);
+
+        insertChecks();
+        Node* isArray = addToGraph(IsArrayConstructor, OpInfo(prediction), get(virtualRegisterForArgument(1, registerOffset)));
+        set(VirtualRegister(resultOperand), isArray);
+        return true;
+    }
+
     case CharCodeAtIntrinsic: {
         if (argumentCountIncludingThis != 2)
             return false;
@@ -2516,7 +2545,16 @@ bool ByteCodeParser::handleConstantInternalFunction(
         set(VirtualRegister(resultOperand), result);
         return true;
     }
-    
+
+    // FIXME: This should handle construction as well. https://bugs.webkit.org/show_bug.cgi?id=155591
+    if (function->classInfo() == ObjectConstructor::info() && kind == CodeForCall) {
+        insertChecks();
+
+        Node* result = addToGraph(CallObjectConstructor, get(virtualRegisterForArgument(1, registerOffset)));
+        set(VirtualRegister(resultOperand), result);
+        return true;
+    }
+
     for (unsigned typeIndex = 0; typeIndex < NUMBER_OF_TYPED_ARRAY_TYPES; ++typeIndex) {
         bool result = handleTypedArrayConstructor(
             resultOperand, function, registerOffset, argumentCountIncludingThis,
index 89b248a..bad425d 100644 (file)
@@ -142,6 +142,8 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case GetGlobalObject:
     case StringCharCodeAt:
     case CompareStrictEq:
+    case IsJSArray:
+    case IsArrayConstructor:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
@@ -181,7 +183,8 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         read(MathDotRandomState);
         write(MathDotRandomState);
         return;
-        
+
+    case IsArrayObject:
     case HasGenericProperty:
     case HasStructureProperty:
     case GetEnumerableLength:
@@ -398,6 +401,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         write(HeapObjectCount);
         return;
 
+    case CallObjectConstructor:
     case ToThis:
     case CreateThis:
         read(MiscFields);
index 00cb27d..0ea9333 100644 (file)
@@ -151,6 +151,9 @@ bool doesGC(Graph& graph, Node* node)
     case OverridesHasInstance:
     case InstanceOf:
     case InstanceOfCustom:
+    case IsArrayObject:
+    case IsJSArray:
+    case IsArrayConstructor:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
@@ -242,6 +245,7 @@ bool doesGC(Graph& graph, Node* node)
     case CreateDirectArguments:
     case CreateScopedArguments:
     case CreateClonedArguments:
+    case CallObjectConstructor:
     case ToThis:
     case CreateThis:
     case AllocatePropertyStorage:
index 21f6235..11e10ed 100644 (file)
@@ -1029,7 +1029,18 @@ private:
             fixEdge<Int32Use>(node->child1());
             break;
         }
-            
+
+        case CallObjectConstructor: {
+            if (node->child1()->shouldSpeculateObject()) {
+                fixEdge<ObjectUse>(node->child1());
+                node->convertToIdentity();
+                break;
+            }
+
+            fixEdge<UntypedUse>(node->child1());
+            break;
+        }
+
         case ToThis: {
             fixupToThis(node);
             break;
@@ -1490,6 +1501,9 @@ private:
         case NewRegexp:
         case ProfileWillCall:
         case ProfileDidCall:
+        case IsArrayObject:
+        case IsJSArray:
+        case IsArrayConstructor:
         case IsUndefined:
         case IsBoolean:
         case IsNumber:
index 8a2b7a9..2e10d24 100644 (file)
@@ -296,6 +296,11 @@ namespace JSC { namespace DFG {
     macro(OverridesHasInstance, NodeMustGenerate | NodeResultBoolean) \
     macro(InstanceOf, NodeResultBoolean) \
     macro(InstanceOfCustom, NodeMustGenerate | NodeResultBoolean) \
+    \
+    /* I'd like to call this IsArray but then we get namespace problems with the indexing type name. Also, it is marked must generate because it can throw. */ \
+    macro(IsArrayObject, NodeMustGenerate | NodeResultBoolean) \
+    macro(IsJSArray, NodeResultBoolean) \
+    macro(IsArrayConstructor, NodeResultBoolean) \
     macro(IsUndefined, NodeResultBoolean) \
     macro(IsBoolean, NodeResultBoolean) \
     macro(IsNumber, NodeResultBoolean) \
@@ -307,6 +312,7 @@ namespace JSC { namespace DFG {
     macro(LogicalNot, NodeResultBoolean) \
     macro(ToPrimitive, NodeResultJS | NodeMustGenerate) \
     macro(ToString, NodeResultJS | NodeMustGenerate) \
+    macro(CallObjectConstructor, NodeResultJS) \
     macro(CallStringConstructor, NodeResultJS | NodeMustGenerate) \
     macro(NewStringObject, NodeResultJS) \
     macro(MakeRope, NodeResultJS) \
index 0ee4c45..eabfd70 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "DFGOperations.h"
 
+#include "ArrayConstructor.h"
 #include "ButterflyInlines.h"
 #include "ClonedArguments.h"
 #include "CodeBlock.h"
@@ -176,6 +177,19 @@ JSCell* JIT_OPERATION operationCreateThis(ExecState* exec, JSObject* constructor
     return constructEmptyObject(exec);
 }
 
+JSCell* JIT_OPERATION operationObjectConstructor(ExecState* exec, JSGlobalObject* globalObject, EncodedJSValue encodedTarget)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+
+    JSValue value = JSValue::decode(encodedTarget);
+    ASSERT(!value.isObject());
+
+    if (value.isUndefinedOrNull())
+        return constructEmptyObject(exec, globalObject->objectPrototype());
+    return value.toObject(exec, globalObject);
+}
+
 EncodedJSValue JIT_OPERATION operationValueBitAnd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
     VM* vm = &exec->vm();
@@ -710,6 +724,22 @@ size_t JIT_OPERATION operationRegExpTestGeneric(ExecState* exec, JSGlobalObject*
     return asRegExpObject(base)->test(exec, globalObject, input);
 }
 
+size_t JIT_OPERATION operationIsArrayConstructor(ExecState* exec, EncodedJSValue encodedTarget)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    return isArrayConstructor(JSValue::decode(encodedTarget));
+}
+
+size_t JIT_OPERATION operationIsArrayObject(ExecState* exec, EncodedJSValue encodedTarget)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    return isArray(exec, JSValue::decode(encodedTarget));
+}
+
 size_t JIT_OPERATION operationCompareStrictEqCell(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
     VM* vm = &exec->vm();
index 6e40caf..443985c 100644 (file)
@@ -41,6 +41,7 @@ JSCell* JIT_OPERATION operationStringFromCharCode(ExecState*, int32_t)  WTF_INTE
 EncodedJSValue JIT_OPERATION operationStringFromCharCodeUntyped(ExecState*, EncodedJSValue)  WTF_INTERNAL;
 
 // These routines are provide callbacks out to C++ implementations of operations too complex to JIT.
+JSCell* JIT_OPERATION operationObjectConstructor(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationCreateThis(ExecState*, JSObject* constructor, int32_t inlineCapacity) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToThis(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationToThisStrict(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
@@ -110,6 +111,8 @@ size_t JIT_OPERATION operationRegExpTest(ExecState*, JSGlobalObject*, RegExpObje
 size_t JIT_OPERATION operationRegExpTestGeneric(ExecState*, JSGlobalObject*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 size_t JIT_OPERATION operationCompareStrictEqCell(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 size_t JIT_OPERATION operationCompareStrictEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
+size_t JIT_OPERATION operationIsArrayConstructor(ExecState*, EncodedJSValue);
+size_t JIT_OPERATION operationIsArrayObject(ExecState*, EncodedJSValue);
 JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState*, Structure*, JSScope*, SymbolTable*, EncodedJSValue);
 JSCell* JIT_OPERATION operationCreateDirectArguments(ExecState*, Structure*, int32_t length, int32_t minCapacity);
 JSCell* JIT_OPERATION operationCreateDirectArgumentsDuringExit(ExecState*, InlineCallFrame*, JSFunction*, int32_t argumentCount);
index b498837..8b80849 100644 (file)
@@ -418,6 +418,9 @@ private:
         case OverridesHasInstance:
         case InstanceOf:
         case InstanceOfCustom:
+        case IsArrayObject:
+        case IsJSArray:
+        case IsArrayConstructor:
         case IsUndefined:
         case IsBoolean:
         case IsNumber:
@@ -491,6 +494,11 @@ private:
             break;
         }
 
+        case CallObjectConstructor: {
+            changed |= setPrediction(SpecObject);
+            break;
+        }
+
         case ToThis: {
             // ToThis in methods for primitive types should speculate primitive types in strict mode.
             ECMAMode ecmaMode = m_graph.executableFor(node->origin.semantic)->isStrictMode() ? StrictMode : NotStrictMode;
index 52bdcfe..efc398e 100644 (file)
@@ -251,6 +251,9 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case OverridesHasInstance:
     case InstanceOf:
     case InstanceOfCustom:
+    case IsArrayObject:
+    case IsJSArray:
+    case IsArrayConstructor:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
@@ -260,6 +263,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case IsFunction:
     case TypeOf:
     case LogicalNot:
+    case CallObjectConstructor:
     case ToPrimitive:
     case ToString:
     case SetFunctionName:
index 7b297f5..2c4a9e5 100644 (file)
@@ -1527,7 +1527,7 @@ void SpeculativeJIT::compileCurrentBlock()
             dataLog("\n");
         }
 
-        if (Options::validateDFGExceptionHandling() && mayExit(m_jit.graph(), m_currentNode) != DoesNotExit)
+        if (Options::validateDFGExceptionHandling() && (mayExit(m_jit.graph(), m_currentNode) != DoesNotExit || m_currentNode->isTerminal()))
             m_jit.jitReleaseAssertNoException();
 
         m_jit.pcToCodeOriginMapBuilder().appendItem(m_jit.label(), m_origin.semantic);
@@ -3272,6 +3272,98 @@ void SpeculativeJIT::compileInstanceOfCustom(Node* node)
     unblessedBooleanResult(resultGPR, node);
 }
 
+void SpeculativeJIT::compileIsJSArray(Node* node)
+{
+    JSValueOperand value(this, node->child1());
+    GPRFlushedCallResult result(this);
+
+    JSValueRegs valueRegs = value.jsValueRegs();
+    GPRReg resultGPR = result.gpr();
+
+    JITCompiler::Jump isNotCell = m_jit.branchIfNotCell(valueRegs);
+
+    m_jit.compare8(JITCompiler::Equal,
+        JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoTypeOffset()),
+        TrustedImm32(ArrayType),
+        resultGPR);
+    blessBoolean(resultGPR);
+    JITCompiler::Jump done = m_jit.jump();
+
+    isNotCell.link(&m_jit);
+    moveFalseTo(resultGPR);
+
+    done.link(&m_jit);
+    blessedBooleanResult(resultGPR, node);
+}
+
+void SpeculativeJIT::compileIsArrayObject(Node* node)
+{
+    JSValueOperand value(this, node->child1());
+    GPRFlushedCallResult result(this);
+
+    JSValueRegs valueRegs = value.jsValueRegs();
+    GPRReg resultGPR = result.gpr();
+
+    JITCompiler::JumpList done;
+
+    JITCompiler::Jump isNotCell = m_jit.branchIfNotCell(valueRegs);
+
+    JITCompiler::Jump notJSArray = m_jit.branch8(JITCompiler::NotEqual,
+        JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoTypeOffset()),
+        TrustedImm32(ArrayType));
+    m_jit.move(TrustedImm32(true), resultGPR);
+    done.append(m_jit.jump());
+
+    notJSArray.link(&m_jit);
+    silentSpillAllRegisters(resultGPR);
+    callOperation(operationIsArrayObject, resultGPR, valueRegs);
+    silentFillAllRegisters(resultGPR);
+    m_jit.exceptionCheck();
+    done.append(m_jit.jump());
+
+    isNotCell.link(&m_jit);
+    m_jit.move(TrustedImm32(false), resultGPR);
+
+    done.link(&m_jit);
+    unblessedBooleanResult(resultGPR, node);
+}
+
+// FIXME: This function should just get the ClassInfo and check if it's == ArrayConstructor::info(). https://bugs.webkit.org/show_bug.cgi?id=155667
+void SpeculativeJIT::compileIsArrayConstructor(Node* node)
+{
+    JSValueOperand value(this, node->child1());
+    GPRFlushedCallResult result(this);
+
+    JSValueRegs valueRegs = value.jsValueRegs();
+    GPRReg resultGPR = result.gpr();
+
+    flushRegisters();
+    callOperation(operationIsArrayConstructor, resultGPR, valueRegs);
+    unblessedBooleanResult(resultGPR, node);
+}
+
+void SpeculativeJIT::compileCallObjectConstructor(Node* node)
+{
+    RELEASE_ASSERT(node->child1().useKind() == UntypedUse);
+    JSValueOperand value(this, node->child1());
+#if USE(JSVALUE64)
+    GPRTemporary result(this, Reuse, value);
+#else
+    GPRTemporary result(this, Reuse, value, PayloadWord);
+#endif
+
+    JSValueRegs valueRegs = value.jsValueRegs();
+    GPRReg resultGPR = result.gpr();
+
+    MacroAssembler::JumpList slowCases;
+    slowCases.append(m_jit.branchIfNotCell(valueRegs));
+    slowCases.append(m_jit.branchIfNotObject(valueRegs.payloadGPR()));
+    m_jit.move(valueRegs.payloadGPR(), resultGPR);
+
+    addSlowPathGenerator(slowPathCall(slowCases, this, operationObjectConstructor, resultGPR, m_jit.globalObjectFor(node->origin.semantic), valueRegs));
+    cellResult(resultGPR, node);
+}
+
 void SpeculativeJIT::compileArithAdd(Node* node)
 {
     switch (node->binaryUseKind()) {
index 8409f65..4995bd9 100644 (file)
@@ -729,6 +729,10 @@ public:
     void compileInstanceOfForObject(Node*, GPRReg valueReg, GPRReg prototypeReg, GPRReg scratchAndResultReg, GPRReg scratch2Reg);
     void compileInstanceOf(Node*);
     void compileInstanceOfCustom(Node*);
+
+    void compileIsJSArray(Node*);
+    void compileIsArrayConstructor(Node*);
+    void compileIsArrayObject(Node*);
     
     void emitCall(Node*);
     
@@ -1392,6 +1396,17 @@ public:
         return appendCallSetResult(operation, result);
     }
 
+    JITCompiler::Call callOperation(C_JITOperation_EGJ operation, GPRReg result, JSGlobalObject* globalObject, GPRReg arg1)
+    {
+        m_jit.setupArgumentsWithExecState(TrustedImmPtr(globalObject), arg1);
+        return appendCallSetResult(operation, result);
+    }
+
+    JITCompiler::Call callOperation(C_JITOperation_EGJ operation, GPRReg result, JSGlobalObject* globalObject, JSValueRegs arg1)
+    {
+        return callOperation(operation, result, globalObject, arg1.gpr());
+    }
+
     JITCompiler::Call callOperation(C_JITOperation_EJ operation, GPRReg result, GPRReg arg1)
     {
         m_jit.setupArgumentsWithExecState(arg1);
@@ -1432,10 +1447,18 @@ public:
         m_jit.setupArgumentsWithExecState(arg1);
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(S_JITOperation_EJ operation, GPRReg result, JSValueRegs arg1)
+    {
+        return callOperation(operation, result, arg1.gpr());
+    }
     JITCompiler::Call callOperation(J_JITOperation_EJ operation, JSValueRegs result, JSValueRegs arg1)
     {
         return callOperation(operation, result.payloadGPR(), arg1.payloadGPR());
     }
+    JITCompiler::Call callOperation(J_JITOperation_EJ operation, GPRReg result, JSValueRegs arg1)
+    {
+        return callOperation(operation, result, arg1.payloadGPR());
+    }
     JITCompiler::Call callOperation(J_JITOperation_EJ operation, GPRReg result, GPRReg arg1)
     {
         m_jit.setupArgumentsWithExecState(arg1);
@@ -1815,6 +1838,17 @@ public:
         return appendCallSetResult(operation, result);
     }
 
+    JITCompiler::Call callOperation(C_JITOperation_EGJ operation, GPRReg result, JSGlobalObject* globalObject, GPRReg arg1Tag, GPRReg arg1Payload)
+    {
+        m_jit.setupArgumentsWithExecState(TrustedImmPtr(globalObject), arg1Payload, arg1Tag);
+        return appendCallSetResult(operation, result);
+    }
+
+    JITCompiler::Call callOperation(C_JITOperation_EGJ operation, GPRReg result, JSGlobalObject* globalObject, JSValueRegs arg1)
+    {
+        return callOperation(operation, result, globalObject, arg1.tagGPR(), arg1.payloadGPR());
+    }
+
     JITCompiler::Call callOperation(C_JITOperation_EJ operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload)
     {
         m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag);
@@ -1839,6 +1873,11 @@ public:
         return appendCallSetResult(operation, result);
     }
 
+    JITCompiler::Call callOperation(S_JITOperation_EJ operation, GPRReg result, JSValueRegs arg1)
+    {
+        return callOperation(operation, result, arg1.tagGPR(), arg1.payloadGPR());
+    }
+
     JITCompiler::Call callOperation(S_JITOperation_EJJ operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2Tag, GPRReg arg2Payload)
     {
         m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, SH4_32BIT_DUMMY_ARG arg2Payload, arg2Tag);
@@ -2412,7 +2451,8 @@ public:
     void compileLazyJSConstant(Node*);
     void compileMaterializeNewObject(Node*);
     void compileRecordRegExpCachedResult(Node*);
-    
+    void compileCallObjectConstructor(Node*);
+
     void moveTrueTo(GPRReg);
     void moveFalseTo(GPRReg);
     void blessBoolean(GPRReg);
index 295c6ad..b707bf3 100644 (file)
@@ -3813,6 +3813,11 @@ void SpeculativeJIT::compile(Node* node)
         cellResult(resultPayload.gpr(), node);
         break;
     }
+
+    case CallObjectConstructor: {
+        compileCallObjectConstructor(node);
+        break;
+    }
         
     case ToThis: {
         ASSERT(node->child1().useKind() == UntypedUse);
@@ -4464,6 +4469,21 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case IsJSArray: {
+        compileIsJSArray(node);
+        break;
+    }
+
+    case IsArrayObject: {
+        compileIsArrayObject(node);
+        break;
+    }
+
+    case IsArrayConstructor: {
+        compileIsArrayConstructor(node);
+        break;
+    }
+
     case IsObject: {
         JSValueOperand value(this, node->child1());
         GPRTemporary result(this, Reuse, value, TagWord);
index a62768d..4dd82ef 100644 (file)
@@ -3877,7 +3877,12 @@ void SpeculativeJIT::compile(Node* node)
         cellResult(result.gpr(), node);
         break;
     }
-        
+
+    case CallObjectConstructor: {
+        compileCallObjectConstructor(node);
+        break;
+    }
+
     case ToThis: {
         ASSERT(node->child1().useKind() == UntypedUse);
         JSValueOperand thisValue(this, node->child1());
@@ -4480,6 +4485,21 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case IsJSArray: {
+        compileIsJSArray(node);
+        break;
+    }
+
+    case IsArrayObject: {
+        compileIsArrayObject(node);
+        break;
+    }
+
+    case IsArrayConstructor: {
+        compileIsArrayConstructor(node);
+        break;
+    }
+
     case IsObject: {
         JSValueOperand value(this, node->child1());
         GPRTemporary result(this, Reuse, value);
index 31edfb6..7661457 100644 (file)
@@ -162,6 +162,7 @@ inline CapabilityLevel canCompile(Node* node)
     case GetScope:
     case GetCallee:
     case GetArgumentCount:
+    case CallObjectConstructor:
     case ToString:
     case CallStringConstructor:
     case MakeRope:
@@ -175,6 +176,9 @@ inline CapabilityLevel canCompile(Node* node)
     case Throw:
     case ThrowReferenceError:
     case Unreachable:
+    case IsArrayObject:
+    case IsJSArray:
+    case IsArrayConstructor:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
index 584020b..4b515fe 100644 (file)
@@ -480,6 +480,9 @@ private:
         case DFG::Check:
             compileNoOp();
             break;
+        case CallObjectConstructor:
+            compileCallObjectConstructor();
+            break;
         case ToThis:
             compileToThis();
             break;
@@ -846,6 +849,15 @@ private:
         case IsString:
             compileIsString();
             break;
+        case IsArrayObject:
+            compileIsArrayObject();
+            break;
+        case IsJSArray:
+            compileIsJSArray();
+            break;
+        case IsArrayConstructor:
+            compileIsArrayConstructor();
+            break;
         case IsObject:
             compileIsObject();
             break;
@@ -1410,6 +1422,28 @@ private:
     {
         DFG_NODE_DO_TO_CHILDREN(m_graph, m_node, speculate);
     }
+
+    void compileCallObjectConstructor()
+    {
+        LValue value = lowJSValue(m_node->child1());
+
+        LBasicBlock isCellCase = m_out.newBlock();
+        LBasicBlock slowCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        m_out.branch(isCell(value, provenType(m_node->child1())), usually(isCellCase), rarely(slowCase));
+
+        LBasicBlock lastNext = m_out.appendTo(isCellCase, slowCase);
+        ValueFromBlock fastResult = m_out.anchor(value);
+        m_out.branch(isObject(value), usually(continuation), rarely(slowCase));
+
+        m_out.appendTo(slowCase, continuation);
+        ValueFromBlock slowResult = m_out.anchor(vmCall(m_out.int64, m_out.operation(operationToObject), m_callFrame, value));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setJSValue(m_out.phi(m_out.int64, fastResult, slowResult));
+    }
     
     void compileToThis()
     {
@@ -5691,6 +5725,55 @@ private:
         setBoolean(m_out.phi(m_out.boolean, notCellResult, cellResult));
     }
 
+    void compileIsArrayObject()
+    {
+        LValue value = lowJSValue(m_node->child1());
+
+        LBasicBlock cellCase = m_out.newBlock();
+        LBasicBlock notArrayCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        ValueFromBlock notCellResult = m_out.anchor(m_out.booleanFalse);
+        m_out.branch(isCell(value, provenType(m_node->child1())), unsure(cellCase), unsure(continuation));
+
+        LBasicBlock lastNext = m_out.appendTo(cellCase, notArrayCase);
+        ValueFromBlock arrayResult = m_out.anchor(m_out.booleanTrue);
+        m_out.branch(isArray(value, provenType(m_node->child1())), unsure(continuation), unsure(notArrayCase));
+
+        m_out.appendTo(notArrayCase, continuation);
+        ValueFromBlock notArrayResult = m_out.anchor(vmCall(m_out.boolean, m_out.operation(operationIsArrayObject), m_callFrame, value));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setBoolean(m_out.phi(m_out.boolean, notCellResult, arrayResult, notArrayResult));
+    }
+
+    void compileIsJSArray()
+    {
+        LValue value = lowJSValue(m_node->child1());
+
+        LBasicBlock isCellCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        ValueFromBlock notCellResult = m_out.anchor(m_out.booleanFalse);
+        m_out.branch(
+            isCell(value, provenType(m_node->child1())), unsure(isCellCase), unsure(continuation));
+
+        LBasicBlock lastNext = m_out.appendTo(isCellCase, continuation);
+        ValueFromBlock cellResult = m_out.anchor(isArray(value, provenType(m_node->child1())));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setBoolean(m_out.phi(m_out.boolean, notCellResult, cellResult));
+    }
+
+    void compileIsArrayConstructor()
+    {
+        LValue value = lowJSValue(m_node->child1());
+
+        setBoolean(vmCall(m_out.boolean, m_out.operation(operationIsArrayConstructor), m_callFrame, value));
+    }
+
     void compileIsObject()
     {
         LValue value = lowJSValue(m_node->child1());
@@ -9725,6 +9808,15 @@ private:
         
         jsValueToStrictInt52(edge, lowJSValue(edge, ManualOperandSpeculation));
     }
+
+    LValue isArray(LValue cell, SpeculatedType type = SpecFullTop)
+    {
+        if (LValue proven = isProvenValue(type & SpecCell, SpecArray))
+            return proven;
+        return m_out.equal(
+            m_out.load8ZeroExt32(cell, m_heaps.JSCell_typeInfoType),
+            m_out.constInt32(ArrayType));
+    }
     
     LValue isObject(LValue cell, SpeculatedType type = SpecFullTop)
     {
index 941a993..15b9fa2 100644 (file)
@@ -163,6 +163,7 @@ typedef JSCell* JIT_OPERATION (*C_JITOperation_ECZ)(ExecState*, JSCell*, int32_t
 typedef JSCell* JIT_OPERATION (*C_JITOperation_ECZC)(ExecState*, JSCell*, int32_t, JSCell*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_ECC)(ExecState*, JSCell*, JSCell*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EGC)(ExecState*, JSGlobalObject*, JSCell*);
+typedef JSCell* JIT_OPERATION (*C_JITOperation_EGJ)(ExecState*, JSGlobalObject*, EncodedJSValue);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EIcf)(ExecState*, InlineCallFrame*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EJ)(ExecState*, EncodedJSValue);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EJsc)(ExecState*, JSScope*);
index 0f46e2d..225ad34 100644 (file)
@@ -442,7 +442,7 @@ public:
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
-        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), ArrayClass);
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ArrayType, StructureFlags), info(), ArrayClass);
     }
 
 protected:
@@ -563,6 +563,7 @@ static EncodedJSValue JSC_HOST_CALL functionGetHiddenValue(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionSetHiddenValue(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionDataLogValue(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionDescribeArray(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
@@ -730,7 +731,8 @@ protected:
     void finishCreation(VM& vm, const Vector<String>& arguments)
     {
         Base::finishCreation(vm);
-        
+
+        addFunction(vm, "dataLogValue", functionDataLogValue, 1);
         addFunction(vm, "debug", functionDebug, 1);
         addFunction(vm, "describe", functionDescribe, 1);
         addFunction(vm, "describeArray", functionDescribeArray, 1);
@@ -829,6 +831,7 @@ protected:
         }
 
         putDirect(vm, Identifier::fromString(globalExec(), "console"), jsUndefined());
+        putDirect(vm, vm.propertyNames->printPrivateName, JSFunction::create(vm, this, 1, vm.propertyNames->printPrivateName.string(), functionPrint));
     }
 
     void addFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
@@ -1128,6 +1131,12 @@ EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }
 
+EncodedJSValue JSC_HOST_CALL functionDataLogValue(ExecState* exec)
+{
+    dataLog("value is: ", exec->argument(0), "\n");
+    return JSValue::encode(jsUndefined());
+}
+
 EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState* exec)
 {
     if (exec->argumentCount() < 1)
index ca28bcc..6115c1a 100644 (file)
@@ -68,7 +68,7 @@ void ArrayConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, Arra
     putDirectWithoutTransition(vm, vm.propertyNames->prototype, arrayPrototype, DontEnum | DontDelete | ReadOnly);
     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete);
     putDirectNonIndexAccessor(vm, vm.propertyNames->speciesSymbol, speciesSymbol, Accessor | ReadOnly | DontEnum);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->isArray, arrayConstructorIsArray, DontEnum, 1);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->isArray, arrayConstructorIsArray, DontEnum, 1, IsArrayIntrinsic);
 }
 
 bool ArrayConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot)
@@ -135,7 +135,7 @@ EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState* exec)
 
 EncodedJSValue JSC_HOST_CALL arrayConstructorPrivateFuncIsArrayConstructor(ExecState* exec)
 {
-    return JSValue::encode(jsBoolean(jsDynamicCast<ArrayConstructor*>(exec->uncheckedArgument(0))));
+    return JSValue::encode(jsBoolean(isArrayConstructor(exec->uncheckedArgument(0))));
 }
 
 } // namespace JSC
index 664703a..bc3accc 100644 (file)
@@ -91,6 +91,14 @@ inline bool isArray(ExecState* exec, JSValue argumentValue)
     ASSERT_NOT_REACHED();
 }
 
+inline bool isArrayConstructor(JSValue argumentValue)
+{
+    if (!argumentValue.isObject())
+        return false;
+
+    return jsCast<JSObject*>(argumentValue)->classInfo() == ArrayConstructor::info();
+}
+
 } // namespace JSC
 
 #endif // ArrayConstructor_h
index cfdd7fc..c7a4244 100644 (file)
@@ -34,6 +34,7 @@
 #include "Error.h"
 #include "Interpreter.h"
 #include "JIT.h"
+#include "JSArrayInlines.h"
 #include "JSArrayIterator.h"
 #include "JSCBuiltins.h"
 #include "JSCInlines.h"
@@ -50,7 +51,6 @@
 namespace JSC {
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState*);
-EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState*);
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState*);
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState*);
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState*);
@@ -93,7 +93,7 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, arrayProtoFuncToString, DontEnum, 0);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("concat", arrayProtoFuncConcat, DontEnum, 1);
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("concat", arrayPrototypeConcatCodeGenerator, DontEnum);
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("fill", arrayPrototypeFillCodeGenerator, DontEnum);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->join, arrayProtoFuncJoin, DontEnum, 1);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("pop", arrayProtoFuncPop, DontEnum, 0, ArrayPopIntrinsic);
@@ -590,87 +590,6 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
     return JSValue::encode(join(*exec, thisObject, separator->view(exec).get()));
 }
 
-EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
-    unsigned argCount = exec->argumentCount();
-    JSValue curArg = thisValue.toObject(exec);
-    if (!curArg)
-        return JSValue::encode(JSValue());
-    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) {
-        previousArray = currentArray;
-        currentArray = jsDynamicCast<JSArray*>(curArg);
-        if (currentArray) {
-            // Can't use JSArray::length here because this might be a RuntimeArray!
-            finalArraySize += getLength(exec, currentArray);
-            if (exec->hadException())
-                return JSValue::encode(jsUndefined());
-        } else
-            ++finalArraySize;
-        if (i == argCount)
-            break;
-        curArg = exec->uncheckedArgument(i);
-    }
-
-    if (finalArraySize.hasOverflowed())
-        return JSValue::encode(throwOutOfMemoryError(exec));
-
-    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);
-    }
-
-    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());
-        if (exec->hadException())
-            return JSValue::encode(jsUndefined());
-    }
-
-    curArg = thisValue.toObject(exec);
-    ASSERT(!exec->hadException());
-    unsigned n = 0;
-    for (unsigned i = 0; ; ++i) {
-        if (JSArray* currentArray = jsDynamicCast<JSArray*>(curArg)) {
-            // Can't use JSArray::length here because this might be a RuntimeArray!
-            unsigned length = getLength(exec, currentArray);
-            if (exec->hadException())
-                return JSValue::encode(jsUndefined());
-            for (unsigned k = 0; k < length; ++k) {
-                JSValue v = getProperty(exec, currentArray, k);
-                if (exec->hadException())
-                    return JSValue::encode(jsUndefined());
-                if (v)
-                    result->putDirectIndex(exec, n, v);
-                n++;
-            }
-        } else {
-            result->putDirectIndex(exec, n, curArg);
-            n++;
-        }
-        if (i == argCount)
-            break;
-        curArg = exec->uncheckedArgument(i);
-    }
-    setLength(exec, result, n);
-    return JSValue::encode(result);
-}
-
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
 {
     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
@@ -1095,6 +1014,92 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncKeys(ExecState* exec)
     return JSValue::encode(JSArrayIterator::create(exec, exec->callee()->globalObject()->arrayIteratorStructure(), ArrayIterateKey, thisObj));
 }
 
+EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncIsJSArray(ExecState* exec)
+{
+    JSValue value = exec->uncheckedArgument(0);
+    if (value.isObject())
+        return JSValue::encode(jsBoolean(value.getObject()->type() == ArrayType));
+    return JSValue::encode(jsBoolean(false));
+}
+
+inline bool moveElements(ExecState* exec, VM& vm, JSArray* target, unsigned targetOffset, JSArray* source, unsigned sourceLength)
+{
+    ASSERT(!hasAnyArrayStorage(source->indexingType()));
+    for (unsigned i = 0; i < sourceLength; ++i) {
+        JSValue value = getProperty(exec, source, i);
+        if (vm.exception())
+            return false;
+        if (value) {
+            target->putDirectIndex(exec, targetOffset + i, value);
+            if (vm.exception())
+                return false;
+        }
+    }
+    return true;
+}
+
+EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
+{
+    ASSERT(exec->argumentCount() == 2);
+    VM& vm = exec->vm();
+
+    JSArray* firstArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
+    JSArray* secondArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
+
+    if (!firstArray->canFastCopy(vm, secondArray))
+        return JSValue::encode(jsNull());
+
+    Butterfly* firstButterfly = firstArray->butterfly();
+    Butterfly* secondButterfly = secondArray->butterfly();
+
+    unsigned firstArraySize = firstButterfly->publicLength();
+    unsigned secondArraySize = secondButterfly->publicLength();
+
+    IndexingType type = firstArray->memCopyWithIndexingType(secondArray->indexingType());
+    if (type == NonArray || firstArraySize + secondArraySize >= MIN_SPARSE_ARRAY_INDEX) {
+        JSArray* result = constructEmptyArray(exec, nullptr, firstArraySize + secondArraySize);
+        if (vm.exception())
+            return JSValue::encode(JSValue());
+
+        if (!moveElements(exec, vm, result, 0, firstArray, firstArraySize)
+            || !moveElements(exec, vm, result, firstArraySize, secondArray, secondArraySize)) {
+            ASSERT(vm.exception());
+            return JSValue::encode(JSValue());
+        }
+
+        return JSValue::encode(result);
+    }
+
+    Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
+    JSArray* result = JSArray::tryCreateUninitialized(vm, resultStructure, firstArraySize + secondArraySize);
+    if (!result)
+        return JSValue::encode(throwOutOfMemoryError(exec));
+
+    if (type == ArrayWithDouble) {
+        double* buffer = result->butterfly()->contiguousDouble().data();
+        memcpy(buffer, firstButterfly->contiguousDouble().data(), sizeof(JSValue) * firstArraySize);
+        memcpy(buffer + firstArraySize, secondButterfly->contiguousDouble().data(), sizeof(JSValue) * secondArraySize);
+    } else if (type != ArrayWithUndecided) {
+        WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
+        memcpy(buffer, firstButterfly->contiguous().data(), sizeof(JSValue) * firstArraySize);
+        memcpy(buffer + firstArraySize, secondButterfly->contiguous().data(), sizeof(JSValue) * secondArraySize);
+    }
+
+    result->butterfly()->setPublicLength(firstArraySize + secondArraySize);
+    return JSValue::encode(result);
+}
+
+EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState* exec)
+{
+    ASSERT(exec->argumentCount() == 2);
+
+    JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
+    JSArray* otherArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
+
+    return JSValue::encode(jsBoolean(resultArray->appendMemcpy(exec, exec->vm(), otherArray)));
+}
+
+
 // -------------------- ArrayPrototype.constructor Watchpoint ------------------
 
 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
index 64d2163..1928048 100644 (file)
@@ -41,7 +41,7 @@ public:
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
-        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), ArrayClass);
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ArrayType, StructureFlags), info(), ArrayClass);
     }
 
     void setConstructor(VM&, JSObject* constructorProperty, unsigned attributes);
@@ -65,6 +65,9 @@ private:
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncValues(ExecState*);
+EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncIsJSArray(ExecState*);
+EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState*);
+EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState*);
 
 } // namespace JSC
 
index 6c90e9c..6ab1c9c 100644 (file)
     macro(yield)
 
 #define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL_NOT_IMPLEMENTED_YET(macro)\
-    macro(isConcatSpreadable) \
     macro(replace) \
     macro(split)
 
 #define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(macro) \
     macro(hasInstance) \
+    macro(isConcatSpreadable) \
     macro(iterator) \
     macro(match) \
     macro(search) \
     macro(hasInstanceBoundFunction) \
     macro(instanceOf) \
     macro(isArray) \
+    macro(isJSArray) \
     macro(isArrayConstructor) \
+    macro(concatMemcpy) \
+    macro(appendMemcpy) \
+    macro(predictFinalLengthFromArgumunts) \
+    macro(print) \
     macro(isSet) \
     macro(isMap) \
     macro(SetIterator) \
index 08fdeb4..b8e6623 100644 (file)
@@ -57,6 +57,9 @@ enum JS_EXPORT_PRIVATE Intrinsic {
     RandomIntrinsic,
     FRoundIntrinsic,
     TruncIntrinsic,
+    IsArrayIntrinsic,
+    IsArrayConstructorIntrinsic,
+    IsJSArrayIntrinsic,
 
     // Getter intrinsics.
     TypedArrayLengthIntrinsic,
index afa062f..4d2450e 100644 (file)
@@ -392,6 +392,37 @@ bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, boo
     return true;
 }
 
+bool JSArray::appendMemcpy(ExecState* exec, VM& vm, JSC::JSArray* otherArray)
+{
+    if (!canFastCopy(vm, otherArray))
+        return false;
+
+    IndexingType type = indexingType();
+    if (type != memCopyWithIndexingType(otherArray->indexingType()))
+        return false;
+
+    unsigned oldLength = length();
+    unsigned otherLength = otherArray->length();
+    unsigned newLength = oldLength + otherLength;
+    if (newLength >= MIN_SPARSE_ARRAY_INDEX)
+        return false;
+
+    if (!ensureLength(vm, newLength))
+        return false;
+    ASSERT(type == indexingType());
+    if (length() != newLength) {
+        throwOutOfMemoryError(exec);
+        return false;
+    }
+
+    if (type == ArrayWithDouble)
+        memcpy(butterfly()->contiguousDouble().data() + oldLength, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);
+    else
+        memcpy(butterfly()->contiguous().data() + oldLength, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength);
+
+    return true;
+}
+
 bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
 {
     Butterfly* butterfly = m_butterfly.get();
@@ -719,38 +750,6 @@ JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count
     }
 }
 
-EncodedJSValue JSArray::fastConcatWith(ExecState& exec, JSArray& otherArray)
-{
-    auto newArrayType = indexingType();
-
-    VM& vm = exec.vm();
-    ASSERT(newArrayType == fastConcatType(vm, *this, otherArray));
-
-    unsigned thisArraySize = m_butterfly.get()->publicLength();
-    unsigned otherArraySize = otherArray.m_butterfly.get()->publicLength();
-    ASSERT(thisArraySize + otherArraySize < MIN_SPARSE_ARRAY_INDEX);
-
-    Structure* resultStructure = exec.lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(newArrayType);
-    JSArray* resultArray = JSArray::tryCreateUninitialized(vm, resultStructure, thisArraySize + otherArraySize);
-    if (!resultArray)
-        return JSValue::encode(throwOutOfMemoryError(&exec));
-
-    auto& resultButterfly = *resultArray->butterfly();
-    auto& otherButterfly = *otherArray.butterfly();
-    if (newArrayType == ArrayWithDouble) {
-        auto buffer = resultButterfly.contiguousDouble().data();
-        memcpy(buffer, m_butterfly.get()->contiguousDouble().data(), sizeof(JSValue) * thisArraySize);
-        memcpy(buffer + thisArraySize, otherButterfly.contiguousDouble().data(), sizeof(JSValue) * otherArraySize);
-    } else {
-        auto buffer = resultButterfly.contiguous().data();
-        memcpy(buffer, m_butterfly.get()->contiguous().data(), sizeof(JSValue) * thisArraySize);
-        memcpy(buffer + thisArraySize, otherButterfly.contiguous().data(), sizeof(JSValue) * otherArraySize);
-    }
-
-    resultButterfly.setPublicLength(thisArraySize + otherArraySize);
-    return JSValue::encode(resultArray);
-}
-
 bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
 {
     unsigned oldLength = storage->length();
index 3c95419..2eb651b 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "ArrayConventions.h"
 #include "ButterflyInlines.h"
+#include "JSCellInlines.h"
 #include "JSObject.h"
 
 namespace JSC {
@@ -78,19 +79,10 @@ public:
 
     JSArray* fastSlice(ExecState&, unsigned startIndex, unsigned count);
 
-    static IndexingType fastConcatType(VM& vm, JSArray& firstArray, JSArray& secondArray)
-    {
-        IndexingType type = firstArray.indexingType();
-        if (type != secondArray.indexingType())
-            return NonArray;
-        if (type != ArrayWithDouble && type != ArrayWithInt32 && type != ArrayWithContiguous)
-            return NonArray;
-        if (firstArray.structure(vm)->holesMustForwardToPrototype(vm)
-            || secondArray.structure(vm)->holesMustForwardToPrototype(vm))
-            return NonArray;
-        return type;
-    }
-    EncodedJSValue fastConcatWith(ExecState&, JSArray&);
+    bool canFastCopy(VM&, JSArray* otherArray);
+    // This function returns NonArray if the indexing types are not compatable for memcpying.
+    IndexingType memCopyWithIndexingType(IndexingType other);
+    bool appendMemcpy(ExecState*, VM&, JSArray* otherArray);
 
     enum ShiftCountMode {
         // This form of shift hints that we're doing queueing. With this assumption in hand,
@@ -152,7 +144,7 @@ public:
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, IndexingType indexingType)
     {
-        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), indexingType);
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ArrayType, StructureFlags), info(), indexingType);
     }
         
 protected:
diff --git a/Source/JavaScriptCore/runtime/JSArrayInlines.h b/Source/JavaScriptCore/runtime/JSArrayInlines.h
new file mode 100644 (file)
index 0000000..5c85315
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef JSArrayInlines_h
+#define JSArrayInlines_h
+
+#include "JSArray.h"
+#include "JSCellInlines.h"
+#include "Structure.h"
+
+namespace JSC {
+
+IndexingType JSArray::memCopyWithIndexingType(IndexingType other)
+{
+    IndexingType type = indexingType();
+    if (!(type & IsArray && other & IsArray))
+        return NonArray;
+
+    if (hasAnyArrayStorage(type) || hasAnyArrayStorage(other))
+        return NonArray;
+
+    if (type == ArrayWithUndecided)
+        return other;
+
+    if (other == ArrayWithUndecided)
+        return type;
+
+    // We can memcpy an Int32 and a Contiguous into a Contiguous array since
+    // both share the same memory layout for Int32 numbers.
+    if ((type == ArrayWithInt32 || type == ArrayWithContiguous)
+        && (other == ArrayWithInt32 || other == ArrayWithContiguous)) {
+        if (other == ArrayWithContiguous)
+            return other;
+        return type;
+    }
+
+    if (type != other)
+        return NonArray;
+
+    return type;
+}
+
+bool JSArray::canFastCopy(VM& vm, JSArray* otherArray)
+{
+    if (hasAnyArrayStorage(this->indexingType()) || hasAnyArrayStorage(otherArray->indexingType()))
+        return false;
+    // FIXME: We should have a watchpoint for indexed properties on Array.prototype and Object.prototype
+    // instead of walking the prototype chain. https://bugs.webkit.org/show_bug.cgi?id=155592
+    if (otherArray->structure(vm)->holesMustForwardToPrototype(vm)
+        || otherArray->structure(vm)->holesMustForwardToPrototype(vm))
+        return false;
+    return true;
+}
+
+} // namespace JSC
+
+#endif /* JSArrayInlines_h */
index fccac95..56f2799 100644 (file)
@@ -515,6 +515,8 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     putDirectWithoutTransition(vm, vm.propertyNames->Uint32ArrayPrivateName, m_typedArrays[toIndex(TypeUint32)].constructor.get(), DontEnum);
     putDirectWithoutTransition(vm, vm.propertyNames->Float32ArrayPrivateName, m_typedArrays[toIndex(TypeFloat32)].constructor.get(), DontEnum);
     putDirectWithoutTransition(vm, vm.propertyNames->Float64ArrayPrivateName, m_typedArrays[toIndex(TypeFloat64)].constructor.get(), DontEnum);
+    putDirectWithoutTransition(vm, vm.propertyNames->ObjectPrivateName, objectConstructor, DontEnum | DontDelete | ReadOnly);
+    putDirectWithoutTransition(vm, vm.propertyNames->ArrayPrivateName, arrayConstructor, DontEnum | DontDelete | ReadOnly);
 
     m_moduleLoader.set(vm, this, ModuleLoaderObject::create(vm, this, ModuleLoaderObject::createStructure(vm, this, m_objectPrototype.get())));
     if (Options::exposeInternalModuleLoader())
@@ -538,13 +540,16 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     JSFunction* privateFuncHasInstanceBoundFunction = JSFunction::create(vm, this, 0, String(), hasInstanceBoundFunction);
     JSFunction* privateFuncInstanceOf = JSFunction::create(vm, this, 0, String(), objectPrivateFuncInstanceOf);
     JSFunction* privateFuncThisTimeValue = JSFunction::create(vm, this, 0, String(), dateProtoFuncGetTime);
-    JSFunction* privateFuncIsArrayConstructor = JSFunction::create(vm, this, 0, String(), arrayConstructorPrivateFuncIsArrayConstructor);
+    JSFunction* privateFuncIsArrayConstructor = JSFunction::create(vm, this, 0, String(), arrayConstructorPrivateFuncIsArrayConstructor, IsArrayConstructorIntrinsic);
+    JSFunction* privateFuncIsJSArray = JSFunction::create(vm, this, 0, String(), arrayProtoPrivateFuncIsJSArray, IsJSArrayIntrinsic);
+    JSFunction* privateFuncConcatMemcpy = JSFunction::create(vm, this, 0, String(), arrayProtoPrivateFuncConcatMemcpy);
+    JSFunction* privateFuncAppendMemcpy = JSFunction::create(vm, this, 0, String(), arrayProtoPrivateFuncAppendMemcpy);
+    JSFunction* privateFuncConcatSlowPath = JSFunction::createBuiltinFunction(vm, arrayPrototypeConcatSlowPathCodeGenerator(vm), this);
 
     GlobalPropertyInfo staticGlobals[] = {
         GlobalPropertyInfo(vm.propertyNames->NaN, jsNaN(), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->Infinity, jsNumber(std::numeric_limits<double>::infinity()), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->undefinedKeyword, jsUndefined(), DontEnum | DontDelete | ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->ObjectPrivateName, objectConstructor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->ownEnumerablePropertyKeysPrivateName, JSFunction::create(vm, this, 0, String(), ownEnumerablePropertyKeys), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->getTemplateObjectPrivateName, privateFuncGetTemplateObject, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->enqueueJobPrivateName, JSFunction::create(vm, this, 0, String(), enqueueJob), DontEnum | DontDelete | ReadOnly),
@@ -558,7 +563,6 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->hasInstanceBoundFunctionPrivateName, privateFuncHasInstanceBoundFunction, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->instanceOfPrivateName, privateFuncInstanceOf, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->BuiltinLogPrivateName, builtinLog, DontEnum | DontDelete | ReadOnly),
-        GlobalPropertyInfo(vm.propertyNames->ArrayPrivateName, arrayConstructor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->NumberPrivateName, numberConstructor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->RegExpPrivateName, m_regExpConstructor.get(), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->StringPrivateName, stringConstructor, DontEnum | DontDelete | ReadOnly),
@@ -580,6 +584,10 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->isMapPrivateName, JSFunction::create(vm, this, 1, String(), privateFuncIsMap), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->isArrayPrivateName, arrayConstructor->getDirect(vm, vm.propertyNames->isArray), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->isArrayConstructorPrivateName, privateFuncIsArrayConstructor, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->isJSArrayPrivateName, privateFuncIsJSArray, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->concatMemcpyPrivateName, privateFuncConcatMemcpy, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->appendMemcpyPrivateName, privateFuncAppendMemcpy, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().concatSlowPathPrivateName(), privateFuncConcatSlowPath, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->MapIteratorPrivateName, JSFunction::create(vm, this, 1, String(), privateFuncMapIterator), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->mapIteratorNextPrivateName, JSFunction::create(vm, this, 0, String(), privateFuncMapIteratorNext), DontEnum | DontDelete | ReadOnly),
 
index fff4042..b6876dc 100644 (file)
@@ -61,6 +61,7 @@ enum JSType : uint8_t {
     PureForwardingProxyType,
     ImpureProxyType,
     WithScopeType,
+    ArrayType,
     DirectArgumentsType,
     ScopedArgumentsType,
 
index 1b22ab6..47a7f33 100644 (file)
@@ -91,6 +91,13 @@ inline JSObject* constructEmptyObject(ExecState* exec)
     return constructEmptyObject(exec, exec->lexicalGlobalObject()->objectPrototype());
 }
 
+inline JSObject* constructObject(ExecState* exec, JSGlobalObject* globalObject, JSValue arg)
+{
+    if (arg.isUndefinedOrNull())
+        return constructEmptyObject(exec, globalObject->objectPrototype());
+    return arg.toObject(exec, globalObject);
+}
+
 // Section 6.2.4.4 of the ES6 specification.
 // https://tc39.github.io/ecma262/#sec-frompropertydescriptor
 inline JSObject* constructObjectFromPropertyDescriptor(ExecState* exec, const PropertyDescriptor& descriptor)
index 78435cc..75587d0 100644 (file)
 - path: es6/Proxy_internal_get_calls_Array.from.js
   cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_Array.prototype.concat.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_Array.prototype.pop.js
   cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_Array.prototype.reverse.js
 - path: es6/well-known_symbols_Symbol.hasInstance.js
   cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.isConcatSpreadable.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.match.js
   cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.replace.js
diff --git a/Source/JavaScriptCore/tests/stress/array-concat-spread-object.js b/Source/JavaScriptCore/tests/stress/array-concat-spread-object.js
new file mode 100644 (file)
index 0000000..027eba9
--- /dev/null
@@ -0,0 +1,48 @@
+// This file tests is concat spreadable.
+
+function arrayEq(a, b) {
+    if (a.length !== b.length)
+        return false;
+    for (let i = 0; i < a.length; i++) {
+        if (a[i] !== b[i])
+            return false;
+    }
+    return true;
+}
+
+
+{
+    // let o = {0:1, 1:2, 2:3, length:3};
+
+    // Test it works with proxies by default
+    for (let i = 0; i < 100000; i++) {
+        if (!arrayEq(Array.prototype.concat.call(o,o), [o,o]))
+            throw "failed normally with an object"
+    }
+
+    // Test it works with spreadable true
+    o[Symbol.isConcatSpreadable] = true;
+    for (let i = 0; i < 100000; i++) {
+        let result = Array.prototype.concat.call(o,o)
+        if (!arrayEq(result, [1,2,3,1,2,3]))
+            throw "failed with spread got: " + result;
+    }
+
+    // Test it works with many things
+    o[Symbol.isConcatSpreadable] = true;
+    let other = {}
+    for (let i = 0; i < 100000; i++) {
+        let result = Array.prototype.concat.call(o,o,true,[1,2],other)
+        if (!arrayEq(result, [1,2,3,1,2,3,true,1,2,other]))
+            throw "failed with spread got: " + result;
+    }
+
+    // Test it works with strings
+    String.prototype[Symbol.isConcatSpreadable] = true;
+    for (let i = 0; i < 100000; i++) {
+        let result = Array.prototype.concat.call("hi","hi")
+        // This is what the spec says is the correct answer... D:
+        if (!arrayEq(result, ["h", "i", "hi"]))
+            throw "failed with string got: " + result + " on iteration " + i;
+    }
+}
diff --git a/Source/JavaScriptCore/tests/stress/array-concat-spread-proxy-exception-check.js b/Source/JavaScriptCore/tests/stress/array-concat-spread-proxy-exception-check.js
new file mode 100644 (file)
index 0000000..81b06cc
--- /dev/null
@@ -0,0 +1,34 @@
+function arrayEq(a, b) {
+    if (a.length !== b.length)
+        return false;
+    for (let i = 0; i < a.length; i++) {
+        if (a[i] !== b[i])
+            return false;
+    }
+    return true;
+}
+
+{
+    let concat = Array.prototype.concat;
+    noInline(concat);
+    let array = [1, 2, 3];
+    let {proxy:p, revoke} = Proxy.revocable(array, { get : function(o, k) { return o[k]; } });
+
+    concat.call(p,p);
+
+    for (let i = 0; i < 100000; i++) {
+        if (!arrayEq(concat.call(p,p), [1,2,3,1,2,3]))
+            throw "bad";
+    }
+    revoke();
+    failed = true;
+    try {
+        concat.call(p,p);
+    } catch (e) {
+        failed = false;
+    }
+
+    if (failed)
+        throw "bad"
+
+}
diff --git a/Source/JavaScriptCore/tests/stress/array-concat-spread-proxy.js b/Source/JavaScriptCore/tests/stress/array-concat-spread-proxy.js
new file mode 100644 (file)
index 0000000..e2cbd48
--- /dev/null
@@ -0,0 +1,39 @@
+// This file tests is concat spreadable.
+
+function arrayEq(a, b) {
+    if (a.length !== b.length)
+        return false;
+    for (let i = 0; i < a.length; i++) {
+        if (a[i] !== b[i])
+            return false;
+    }
+    return true;
+}
+
+
+{
+    let array = [1,2,3];
+    let {proxy:p, revoke} = Proxy.revocable(array, { get : function(o, k) { return o[k]; } });
+
+    // Test it works with proxies by default
+    for (let i = 0; i < 100000; i++) {
+        if (!arrayEq(Array.prototype.concat.call(p,p), [1,2,3,1,2,3]))
+            throw "failed normally with a proxy"
+    }
+
+    // Test it works with spreadable false.
+    p[Symbol.isConcatSpreadable] = false;
+    for (let i = 0; i < 100000; i++) {
+        if (!arrayEq(Array.prototype.concat.call(p,p), [p,p]))
+            throw "failed with no spread"
+    }
+
+    p[Symbol.isConcatSpreadable] = undefined;
+    revoke();
+    passed = true;
+    try {
+        Array.prototype.concat.call(p,[]);
+        passed = false;
+    } catch (e) { }
+
+}
diff --git a/Source/JavaScriptCore/tests/stress/array-concat-with-slow-indexingtypes.js b/Source/JavaScriptCore/tests/stress/array-concat-with-slow-indexingtypes.js
new file mode 100644 (file)
index 0000000..492efb1
--- /dev/null
@@ -0,0 +1,35 @@
+function arrayEq(a, b) {
+    if (a.length !== b.length)
+        return false;
+    for (let i = 0; i < a.length; i++) {
+        if (a[i] !== b[i])
+            return false;
+    }
+    return true;
+}
+
+
+{
+
+    array = [1,2];
+    Object.defineProperty(array, 2, { get: () => { return 1; } });
+
+    for (let i = 0; i < 100000; i++) {
+        if (!arrayEq(Array.prototype.concat.call(array,array), [1,2,1,1,2,1]))
+            throw "failed normally with a getter"
+        if (!arrayEq(Array.prototype.concat.call([],array), [1,2,1]))
+            throw "failed with undecided and a getter"
+    }
+
+    // Test with indexed types on prototype.
+    array = [1,2];
+    array.length = 3;
+    Array.prototype[2] = 1;
+
+    for (let i = 0; i < 100000; i++) {
+        if (!arrayEq(Array.prototype.concat.call(array,array), [1,2,1,1,2,1]))
+            throw "failed normally with an indexed prototype"
+        if (!arrayEq(Array.prototype.concat.call([],array), [1,2,1]))
+            throw "failed with undecided and an indexed prototype"
+    }
+}
index cd0ef21..8477d5b 100644 (file)
@@ -16,6 +16,9 @@ if (!(result instanceof A))
 
 Object.defineProperty(Array, Symbol.species, { value: Int32Array, configurable: true });
 
+// We can't write to the length property on a typed array by default.
+Object.defineProperty(Int32Array.prototype, "length", { value: 0, writable: true });
+
 result = foo.concat([1]);
 if (!(result instanceof Int32Array))
     throw "concat failed";
index d06363e..da81df6 100644 (file)
@@ -1,3 +1,15 @@
+2016-03-29  Keith Miller  <keith_miller@apple.com>
+
+        [ES6] Add support for Symbol.isConcatSpreadable.
+        https://bugs.webkit.org/show_bug.cgi?id=155351
+
+        Reviewed by Saam Barati.
+
+        Makes runtime arrays have the new ArrayType
+
+        * bridge/runtime_array.h:
+        (JSC::RuntimeArray::createStructure):
+
 2016-04-06  Eric Carlson  <eric.carlson@apple.com>
 
         [iOS Simulator WK1] Crash in MediaPlayer::setPrivateBrowsingMode()
index 018c132..d470a4e 100644 (file)
@@ -75,7 +75,7 @@ public:
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
-        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), ArrayClass);
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ArrayType, StructureFlags), info(), ArrayClass);
     }
 
 protected: