+2016-06-15 Keith Miller <keith_miller@apple.com>
+
+ Add support for Symbol.isConcatSpreadable (round 2)
+ https://bugs.webkit.org/show_bug.cgi?id=158769
+
+ Reviewed by Mark Lam.
+
+ Fix tests for Symbol.isConcatSpreadable. Also, add new test that
+ the array species construction does not use the callees' global
+ object's Array[Symbol.species] when given an array from another
+ global object.
+
+ * js/Object-getOwnPropertyNames-expected.txt:
+ * js/array-species-different-globalobjects.html:
+ * js/dom/array-prototype-properties-expected.txt:
+ * js/script-tests/Object-getOwnPropertyNames.js:
+
2016-06-15 Zalan Bujtas <zalan@apple.com>
Decouple the percent height and positioned descendants maps.
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', 'replace', 'search', 'species', 'split', 'toPrimitive', 'toStringTag', 'unscopables']
+PASS getSortedOwnPropertyNames(Symbol) is ['for', 'hasInstance', 'isConcatSpreadable', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'replace', 'search', 'species', 'split', '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']
PASS result instanceof Array is true
PASS result instanceof Array is true
PASS result instanceof Array is true
+PASS result instanceof Array is true
+PASS result instanceof Array is true
+PASS result instanceof Array is true
+PASS result instanceof Array is true
+PASS result instanceof Array is true
+PASS result instanceof Array is true
PASS successfullyParsed is true
TEST COMPLETE
testFunctions.forEach(testFunction);
+Array[Symbol.species] = false;
+
+testFunctions.forEach(testFunction);
+
</script>
<script src="../resources/js-test-post.js"></script>
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, {})').
"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', 'replace', 'search', 'species', 'split', 'toPrimitive', 'toStringTag', 'unscopables']",
+ "Symbol": "['for', 'hasInstance', 'isConcatSpreadable', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'replace', 'search', 'species', 'split', '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']",
+2016-06-15 Keith Miller <keith_miller@apple.com>
+
+ Add support for Symbol.isConcatSpreadable (round 2)
+ https://bugs.webkit.org/show_bug.cgi?id=158769
+
+ Reviewed by Mark Lam.
+
+ 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, this patch adds a
+ new Bytecode intrinsic, isJSArray, that checks if the value is a
+ JSArray object. Specifically, isJSArray checks that the array
+ object is a normal instance of JSArray and not a RuntimeArray or
+ Array.prototype. isJSArray can also be converted into a constant
+ by the DFG if we are able to prove that the incomming value is
+ already a JSArray.
+
+ 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 or were undecided. Now the memcpy code covers the
+ following additional three cases:
+
+ 1) One array is undecided and the other does not have array storage
+
+ 2) One array is Int32 and the other is contiguous (we map this
+ into a contiguous array).
+
+ 3) The this value is an array and first argument is a non-array
+ that does not have Symbol.isConcatSpreadable set.
+
+ 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.
+
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * builtins/ArrayPrototype.js:
+ (concatSlowPath):
+ (concat):
+ * bytecode/BytecodeIntrinsicRegistry.cpp:
+ (JSC::BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry):
+ * bytecode/BytecodeIntrinsicRegistry.h:
+ * bytecode/BytecodeList.json:
+ * bytecode/BytecodeUseDef.h:
+ (JSC::computeUsesForBytecodeOffset):
+ (JSC::computeDefsForBytecodeOffset):
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dumpBytecode):
+ * bytecompiler/BytecodeGenerator.h:
+ (JSC::BytecodeGenerator::emitIsJSArray):
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::BytecodeIntrinsicNode::emit_intrinsic_isJSArray):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCapabilities.cpp:
+ (JSC::DFG::capabilityLevel):
+ * 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:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileCurrentBlock):
+ (JSC::DFG::SpeculativeJIT::compileIsJSArray):
+ (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::compileIsJSArray):
+ (JSC::FTL::DFG::LowerDFGToB3::isArray):
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass):
+ * jit/JIT.h:
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_is_jsarray):
+ * jit/JITOpcodes32_64.cpp:
+ (JSC::JIT::emit_op_is_jsarray):
+ * jit/JITOperations.h:
+ * llint/LLIntData.cpp:
+ (JSC::LLInt::Data::performAssertions):
+ * llint/LowLevelInterpreter.asm:
+ * llint/LowLevelInterpreter32_64.asm:
+ * llint/LowLevelInterpreter64.asm:
+ * runtime/ArrayConstructor.h:
+ (JSC::isArrayConstructor):
+ * runtime/ArrayPrototype.cpp:
+ (JSC::ArrayPrototype::finishCreation):
+ (JSC::speciesWatchpointsValid):
+ (JSC::speciesConstructArray):
+ (JSC::moveElements):
+ (JSC::concatAppendOne):
+ (JSC::arrayProtoFuncConcat): Deleted.
+ * runtime/ArrayPrototype.h:
+ * runtime/CommonIdentifiers.h:
+ * runtime/CommonSlowPaths.cpp:
+ (JSC::SLOW_PATH_DECL):
+ * runtime/IndexingType.h:
+ (JSC::indexingTypeForValue):
+ * runtime/JSArray.cpp:
+ (JSC::JSArray::appendMemcpy):
+ (JSC::JSArray::fastConcatWith): Deleted.
+ * runtime/JSArray.h:
+ (JSC::JSArray::createStructure):
+ (JSC::isJSArray):
+ (JSC::JSArray::fastConcatType): Deleted.
+ * runtime/JSArrayInlines.h: Added.
+ (JSC::JSArray::mergeIndexingTypeForCopying):
+ (JSC::JSArray::canFastCopy):
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::init):
+ * runtime/JSObject.cpp:
+ (JSC::JSObject::convertUndecidedForValue):
+ * 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-06-15 Mark Lam <mark.lam@apple.com>
Assertion failure when returning incomplete property descriptor from proxy trap.
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, ); }; };
53FA2AE11CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 53FA2AE01CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
53FA2AE31CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53FA2AE21CF380390022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.cpp */; };
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>"; };
53FA2AE01CF37F3F0022711D /* LLIntPrototypeLoadAdaptiveStructureWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLIntPrototypeLoadAdaptiveStructureWatchpoint.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 */,
return array;
}
+function concatSlowPath()
+{
+ "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 currentElement = @Object(this);
+
+ var constructor;
+ if (@isArray(currentElement)) {
+ constructor = currentElement.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;
+ else if (@isObject(constructor)) {
+ constructor = constructor.@speciesSymbol;
+ if (constructor === null)
+ constructor = @Array;
+ }
+ }
+ if (constructor === @undefined)
+ constructor = @Array;
+
+ var argCount = arguments.length;
+ var result = new constructor(0);
+ var resultIsArray = @isJSArray(result);
+
+ var resultIndex = 0;
+ var argIndex = 0;
+
+ do {
+ let spreadable = @isObject(currentElement) && currentElement.@isConcatSpreadableSymbol;
+ if ((spreadable === @undefined && @isArray(currentElement)) || spreadable) {
+ let length = @toLength(currentElement.length);
+ if (resultIsArray && @isJSArray(currentElement)) {
+ @appendMemcpy(result, currentElement, resultIndex);
+ 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 (@argumentCount() === 1
+ && @isJSArray(this)
+ && this.@isConcatSpreadableSymbol === @undefined
+ && (!@isObject(first) || first.@isConcatSpreadableSymbol === @undefined)) {
+
+ let result = @concatMemcpy(this, first);
+ if (result !== null)
+ return result;
+ }
+
+ return @tailCallForwardArguments(@concatSlowPath, this);
+}
+
function copyWithin(target, start /*, end */)
{
"use strict";
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)));
macro(argumentCount) \
macro(assert) \
macro(isObject) \
+ macro(isJSArray) \
macro(tailCallForwardArguments) \
macro(tryGetById) \
macro(putByValDirect) \
macro(arrayIterationKindValue) \
macro(arrayIterationKindKeyValue) \
macro(MAX_STRING_LENGTH) \
+ macro(MAX_SAFE_INTEGER) \
macro(promiseStatePending) \
macro(promiseStateFulfilled) \
macro(promiseStateRejected) \
{ "name" : "op_is_boolean", "length" : 3 },
{ "name" : "op_is_number", "length" : 3 },
{ "name" : "op_is_string", "length" : 3 },
+ { "name" : "op_is_jsarray", "length" : 3 },
{ "name" : "op_is_object", "length" : 3 },
{ "name" : "op_is_object_or_null", "length" : 3 },
{ "name" : "op_is_function", "length" : 3 },
case op_is_boolean:
case op_is_number:
case op_is_string:
+ case op_is_jsarray:
case op_is_object:
case op_is_object_or_null:
case op_is_function:
case op_is_boolean:
case op_is_number:
case op_is_string:
+ case op_is_jsarray:
case op_is_object:
case op_is_object_or_null:
case op_is_function:
printUnaryOp(out, exec, location, it, "is_string");
break;
}
+ case op_is_jsarray: {
+ printUnaryOp(out, exec, location, it, "is_jsarray");
+ break;
+ }
case op_is_object: {
printUnaryOp(out, exec, location, it, "is_object");
break;
RegisterID* emitEnumeratorGenericPropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index);
RegisterID* emitToIndexString(RegisterID* dst, RegisterID* index);
+ RegisterID* emitIsJSArray(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_is_jsarray, dst, src); }
RegisterID* emitIsObject(RegisterID* dst, RegisterID* src);
RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src);
RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src);
return generator.moveToDestinationIfNeeded(dst, generator.emitToString(generator.tempDestination(dst), src.get()));
}
+
+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_isJSArray(JSC::BytecodeGenerator& generator, JSC::RegisterID* dst)
+{
+ ArgumentListNode* node = m_args->m_listNode;
+ RefPtr<RegisterID> src = generator.emitNode(node);
+ ASSERT(!node->m_next);
+
+ return generator.moveToDestinationIfNeeded(dst, generator.emitIsJSArray(generator.tempDestination(dst), src.get()));
+}
RegisterID* BytecodeIntrinsicNode::emit_intrinsic_isObject(BytecodeGenerator& generator, RegisterID* dst)
{
#if ENABLE(DFG_JIT)
+#include "ArrayConstructor.h"
#include "DFGAbstractInterpreter.h"
#include "GetByIdStatus.h"
#include "GetterSetter.h"
}
case IsEmpty:
+ case IsJSArray:
case IsUndefined:
case IsBoolean:
case IsNumber:
if (child.value()) {
bool constantWasSet = true;
switch (node->op()) {
+ case IsJSArray:
+ setConstant(node, jsBoolean(child.value().isObject() && child.value().getObject()->type() == ArrayType));
+ break;
case IsUndefined:
setConstant(node, jsBoolean(
child.value().isCell()
bool constantWasSet = false;
switch (node->op()) {
+ case IsJSArray:
+ // 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 IsEmpty: {
if (child.m_type && !(child.m_type & SpecEmpty)) {
setConstant(node, jsBoolean(false));
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:
#include "JSLexicalEnvironment.h"
#include "JSCInlines.h"
#include "JSModuleEnvironment.h"
+#include "ObjectConstructor.h"
#include "PreciseJumpTargets.h"
#include "PutByIdFlags.h"
#include "PutByIdStatus.h"
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,
NEXT_OPCODE(op_is_string);
}
+ case op_is_jsarray: {
+ Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
+ set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(IsJSArray, value));
+ NEXT_OPCODE(op_is_jsarray);
+ }
+
case op_is_object: {
Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(IsObject, value));
case op_is_boolean:
case op_is_number:
case op_is_string:
+ case op_is_jsarray:
case op_is_object:
case op_is_object_or_null:
case op_is_function:
case GetGlobalObject:
case StringCharCodeAt:
case CompareStrictEq:
+ case IsJSArray:
case IsEmpty:
case IsUndefined:
case IsBoolean:
read(MathDotRandomState);
write(MathDotRandomState);
return;
-
+
case HasGenericProperty:
case HasStructureProperty:
case GetEnumerableLength:
write(HeapObjectCount);
return;
+ case CallObjectConstructor:
case ToThis:
case CreateThis:
read(MiscFields);
case OverridesHasInstance:
case InstanceOf:
case InstanceOfCustom:
+ case IsJSArray:
case IsEmpty:
case IsUndefined:
case IsBoolean:
case CreateDirectArguments:
case CreateScopedArguments:
case CreateClonedArguments:
+ case CallObjectConstructor:
case ToThis:
case CreateThis:
case AllocatePropertyStorage:
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;
case NewRegexp:
case DeleteById:
case DeleteByVal:
+ case IsJSArray:
case IsEmpty:
case IsUndefined:
case IsBoolean:
macro(OverridesHasInstance, NodeMustGenerate | NodeResultBoolean) \
macro(InstanceOf, NodeResultBoolean) \
macro(InstanceOfCustom, NodeMustGenerate | NodeResultBoolean) \
+ \
+ macro(IsJSArray, NodeResultBoolean) \
macro(IsEmpty, NodeResultBoolean) \
macro(IsUndefined, NodeResultBoolean) \
macro(IsBoolean, NodeResultBoolean) \
macro(LogicalNot, NodeResultBoolean) \
macro(ToPrimitive, NodeResultJS | NodeMustGenerate) \
macro(ToString, NodeResultJS | NodeMustGenerate) \
+ macro(CallObjectConstructor, NodeResultJS) \
macro(CallStringConstructor, NodeResultJS | NodeMustGenerate) \
macro(NewStringObject, NodeResultJS) \
macro(MakeRope, NodeResultJS) \
#include "config.h"
#include "DFGOperations.h"
+#include "ArrayConstructor.h"
#include "ButterflyInlines.h"
#include "ClonedArguments.h"
#include "CodeBlock.h"
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();
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;
case OverridesHasInstance:
case InstanceOf:
case InstanceOfCustom:
+ case IsJSArray:
case IsEmpty:
case IsUndefined:
case IsBoolean:
break;
}
+ case CallObjectConstructor: {
+ setPrediction(SpecObject);
+ break;
+ }
case SkipScope:
case GetGlobalObject: {
setPrediction(SpecObjectOther);
case OverridesHasInstance:
case InstanceOf:
case InstanceOfCustom:
+ case IsJSArray:
case IsEmpty:
case IsUndefined:
case IsBoolean:
case IsRegExpObject:
case TypeOf:
case LogicalNot:
+ case CallObjectConstructor:
case ToPrimitive:
case ToString:
case SetFunctionName:
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);
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::compileIsRegExpObject(Node* node)
{
JSValueOperand value(this, node->child1());
blessedBooleanResult(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()) {
void compileInstanceOf(Node*);
void compileInstanceOfCustom(Node*);
+ void compileIsJSArray(Node*);
void compileIsRegExpObject(Node*);
void emitCall(Node*);
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);
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);
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);
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_EJI operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload, UniquedStringImpl* uid)
{
m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, TrustedImmPtr(uid));
void compileLazyJSConstant(Node*);
void compileMaterializeNewObject(Node*);
void compileRecordRegExpCachedResult(Node*);
+ void compileCallObjectConstructor(Node*);
void compileResolveScope(Node*);
void compileGetDynamicVar(Node*);
void compilePutDynamicVar(Node*);
cellResult(resultPayload.gpr(), node);
break;
}
+
+ case CallObjectConstructor: {
+ compileCallObjectConstructor(node);
+ break;
+ }
case ToThis: {
ASSERT(node->child1().useKind() == UntypedUse);
break;
}
+ case IsJSArray: {
+ compileIsJSArray(node);
+ break;
+ }
+
case IsObject: {
JSValueOperand value(this, node->child1());
GPRTemporary result(this, Reuse, value, TagWord);
cellResult(result.gpr(), node);
break;
}
-
+
+ case CallObjectConstructor: {
+ compileCallObjectConstructor(node);
+ break;
+ }
+
case ToThis: {
ASSERT(node->child1().useKind() == UntypedUse);
JSValueOperand thisValue(this, node->child1());
break;
}
+ case IsJSArray: {
+ compileIsJSArray(node);
+ break;
+ }
+
case IsObject: {
JSValueOperand value(this, node->child1());
GPRTemporary result(this, Reuse, value);
case GetScope:
case GetCallee:
case GetArgumentCountIncludingThis:
+ case CallObjectConstructor:
case ToString:
case CallStringConstructor:
case MakeRope:
case Throw:
case ThrowReferenceError:
case Unreachable:
+ case IsJSArray:
case IsEmpty:
case IsUndefined:
case IsBoolean:
case DFG::Check:
compileNoOp();
break;
+ case CallObjectConstructor:
+ compileCallObjectConstructor();
+ break;
case ToThis:
compileToThis();
break;
case IsString:
compileIsString();
break;
+ case IsJSArray:
+ compileIsJSArray();
+ break;
case IsObject:
compileIsObject();
break;
DFG_NODE_DO_TO_CHILDREN(m_graph, m_node, speculate);
}
+ void compileCallObjectConstructor()
+ {
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+ 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(operationObjectConstructor), m_callFrame, m_out.constIntPtr(globalObject), value));
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ setJSValue(m_out.phi(m_out.int64, fastResult, slowResult));
+ }
+
void compileToThis()
{
LValue value = lowJSValue(m_node->child1());
setBoolean(m_out.phi(m_out.boolean, notCellResult, cellResult));
}
+ 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 compileIsObject()
{
LValue value = lowJSValue(m_node->child1());
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)
{
DEFINE_OP(op_is_boolean)
DEFINE_OP(op_is_number)
DEFINE_OP(op_is_string)
+ DEFINE_OP(op_is_jsarray)
DEFINE_OP(op_is_object)
DEFINE_OP(op_jeq_null)
DEFINE_OP(op_jfalse)
void emit_op_is_boolean(Instruction*);
void emit_op_is_number(Instruction*);
void emit_op_is_string(Instruction*);
+ void emit_op_is_jsarray(Instruction*);
void emit_op_is_object(Instruction*);
void emit_op_jeq_null(Instruction*);
void emit_op_jfalse(Instruction*);
emitPutVirtualRegister(dst);
}
+void JIT::emit_op_is_jsarray(Instruction* currentInstruction)
+{
+ int dst = currentInstruction[1].u.operand;
+ int value = currentInstruction[2].u.operand;
+
+ emitGetVirtualRegister(value, regT0);
+ Jump isNotCell = emitJumpIfNotJSCell(regT0);
+
+ compare8(Equal, Address(regT0, JSCell::typeInfoTypeOffset()), TrustedImm32(ArrayType), regT0);
+ emitTagBool(regT0);
+ Jump done = jump();
+
+ isNotCell.link(this);
+ move(TrustedImm32(ValueFalse), regT0);
+
+ done.link(this);
+ emitPutVirtualRegister(dst);
+}
+
void JIT::emit_op_is_object(Instruction* currentInstruction)
{
int dst = currentInstruction[1].u.operand;
emitStoreBool(dst, regT0);
}
+void JIT::emit_op_is_jsarray(Instruction* currentInstruction)
+{
+ int dst = currentInstruction[1].u.operand;
+ int value = currentInstruction[2].u.operand;
+
+ emitLoad(value, regT1, regT0);
+ Jump isNotCell = branch32(NotEqual, regT1, TrustedImm32(JSValue::CellTag));
+
+ compare8(Equal, Address(regT0, JSCell::typeInfoTypeOffset()), TrustedImm32(ArrayType), regT0);
+ Jump done = jump();
+
+ isNotCell.link(this);
+ move(TrustedImm32(0), regT0);
+
+ done.link(this);
+ emitStoreBool(dst, regT0);
+}
+
void JIT::emit_op_is_object(Instruction* currentInstruction)
{
int dst = currentInstruction[1].u.operand;
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*);
STATIC_ASSERT(ObjectType == 20);
STATIC_ASSERT(FinalObjectType == 21);
STATIC_ASSERT(JSFunctionType == 23);
+ STATIC_ASSERT(ArrayType == 29);
STATIC_ASSERT(Int8ArrayType == 100);
STATIC_ASSERT(Int16ArrayType == 101);
STATIC_ASSERT(Int32ArrayType == 102);
const ObjectType = 20
const FinalObjectType = 21
const JSFunctionType = 23
+const ArrayType = 29
# The typed array types need to be numbered in a particular order because of the manually written
# switch statement in get_by_val and put_by_val.
dispatch(3)
+_llint_op_is_jsarray:
+ traceExecution()
+ loadi 8[PC], t1
+ loadi 4[PC], t2
+ loadConstantOrVariable(t1, t0, t3)
+ storei BooleanTag, TagOffset[cfr, t2, 8]
+ bineq t0, CellTag, .opIsJSArrayNotCell
+ cbeq JSCell::m_type[t3], ArrayType, t1
+ storei t1, PayloadOffset[cfr, t2, 8]
+ dispatch(3)
+.opIsJSArrayNotCell:
+ storep 0, PayloadOffset[cfr, t2, 8]
+ dispatch(3)
+
+
_llint_op_is_object:
traceExecution()
loadi 8[PC], t1
dispatch(3)
+_llint_op_is_jsarray:
+ traceExecution()
+ loadisFromInstruction(2, t1)
+ loadisFromInstruction(1, t2)
+ loadConstantOrVariable(t1, t0)
+ btqnz t0, tagMask, .opIsJSArrayNotCell
+ cbeq JSCell::m_type[t0], ArrayType, t1
+ orq ValueFalse, t1
+ storeq t1, [cfr, t2, 8]
+ dispatch(3)
+.opIsJSArrayNotCell:
+ storeq ValueFalse, [cfr, t2, 8]
+ dispatch(3)
+
+
_llint_op_is_object:
traceExecution()
loadisFromInstruction(2, t1)
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
#include "GetterSetter.h"
#include "Interpreter.h"
#include "JIT.h"
+#include "JSArrayInlines.h"
#include "JSArrayIterator.h"
#include "JSCBuiltins.h"
#include "JSCInlines.h"
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*);
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);
putLength(exec, obj, jsNumber(value));
}
+inline bool speciesWatchpointsValid(ExecState* exec, JSObject* thisObject)
+{
+ ArrayPrototype* arrayPrototype = thisObject->globalObject()->arrayPrototype();
+ ArrayPrototype::SpeciesWatchpointStatus status = arrayPrototype->speciesWatchpointStatus();
+ if (UNLIKELY(status == ArrayPrototype::SpeciesWatchpointStatus::Uninitialized))
+ status = arrayPrototype->attemptToInitializeSpeciesWatchpoint(exec);
+ ASSERT(status != ArrayPrototype::SpeciesWatchpointStatus::Uninitialized);
+ return !thisObject->hasCustomProperties()
+ && arrayPrototype == thisObject->getPrototypeDirect()
+ && status == ArrayPrototype::SpeciesWatchpointStatus::Initialized;
+}
+
enum class SpeciesConstructResult {
FastPath,
Exception,
// ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate
JSValue constructor = jsUndefined();
if (LIKELY(isArray(exec, thisObject))) {
- ArrayPrototype* arrayPrototype = thisObject->globalObject()->arrayPrototype();
- ArrayPrototype::SpeciesWatchpointStatus status = arrayPrototype->speciesWatchpointStatus();
- if (UNLIKELY(status == ArrayPrototype::SpeciesWatchpointStatus::Uninitialized))
- status = arrayPrototype->attemptToInitializeSpeciesWatchpoint(exec);
- ASSERT(status != ArrayPrototype::SpeciesWatchpointStatus::Uninitialized);
// Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
// We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
- if (LIKELY(!thisObject->hasCustomProperties()
- && arrayPrototype == thisObject->getPrototypeDirect()
- && status == ArrayPrototype::SpeciesWatchpointStatus::Initialized))
+ if (LIKELY(speciesWatchpointsValid(exec, thisObject)))
return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
constructor = thisObject->get(exec, exec->propertyNames().constructor);
return JSValue::encode(join(*exec, thisObject, separator->view(exec).get()));
}
-EncodedJSValue JSC_HOST_CALL arrayProtoFuncConcat(ExecState* exec)
-{
- VM& vm = exec->vm();
- 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 (UNLIKELY(vm.exception()))
- 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 (UNLIKELY(vm.exception()))
- return JSValue::encode(jsUndefined());
- }
-
- curArg = thisValue.toObject(exec);
- ASSERT(!vm.exception());
- 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 (UNLIKELY(vm.exception()))
- return JSValue::encode(jsUndefined());
- for (unsigned k = 0; k < length; ++k) {
- JSValue v = getProperty(exec, currentArray, k);
- if (UNLIKELY(vm.exception()))
- 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);
return JSValue::encode(jsNumber(-1));
}
+static bool moveElements(ExecState* exec, VM& vm, JSArray* target, unsigned targetOffset, JSArray* source, unsigned sourceLength)
+{
+ if (LIKELY(!hasAnyArrayStorage(source->indexingType()) && !source->structure()->holesMustForwardToPrototype(vm))) {
+ for (unsigned i = 0; i < sourceLength; ++i) {
+ JSValue value = source->tryGetIndexQuickly(i);
+ if (value) {
+ target->putDirectIndex(exec, targetOffset + i, value);
+ if (vm.exception())
+ return false;
+ }
+ }
+ } else {
+ 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;
+}
+
+static EncodedJSValue concatAppendOne(ExecState* exec, VM& vm, JSArray* first, JSValue second)
+{
+ ASSERT(!isJSArray(second));
+ ASSERT(!shouldUseSlowPut(first->indexingType()));
+ Butterfly* firstButterfly = first->butterfly();
+ unsigned firstArraySize = firstButterfly->publicLength();
+
+ IndexingType type = first->mergeIndexingTypeForCopying(indexingTypeForValue(second) | IsArray);
+ JSArray* result;
+ if (type == NonArray) {
+ result = constructEmptyArray(exec, nullptr, firstArraySize + 1);
+ if (vm.exception())
+ return JSValue::encode(JSValue());
+
+ if (!moveElements(exec, vm, result, 0, first, firstArraySize)) {
+ ASSERT(vm.exception());
+ return JSValue::encode(JSValue());
+ }
+
+ } else {
+ Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
+ result = JSArray::tryCreateUninitialized(vm, resultStructure, firstArraySize + 1);
+ if (!result)
+ return JSValue::encode(throwOutOfMemoryError(exec));
+
+ bool memcpyResult = result->appendMemcpy(exec, vm, 0, first);
+ RELEASE_ASSERT(memcpyResult);
+ }
+
+ result->putDirectIndex(exec, firstArraySize, second);
+ return JSValue::encode(result);
+
+}
+
+
+EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
+{
+ ASSERT(exec->argumentCount() == 2);
+ VM& vm = exec->vm();
+
+ JSArray* firstArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
+
+ // This code assumes that neither array has set Symbol.isConcatSpreadable. If the first array
+ // has indexed accessors then one of those accessors might change the value of Symbol.isConcatSpreadable
+ // on the second argument.
+ if (UNLIKELY(shouldUseSlowPut(firstArray->indexingType())))
+ return JSValue::encode(jsNull());
+
+ // We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
+ if (UNLIKELY(!speciesWatchpointsValid(exec, firstArray)))
+ return JSValue::encode(jsNull());
+
+ JSValue second = exec->uncheckedArgument(1);
+ if (!isJSArray(second))
+ return concatAppendOne(exec, vm, firstArray, second);
+
+ JSArray* secondArray = jsCast<JSArray*>(second);
+
+ Butterfly* firstButterfly = firstArray->butterfly();
+ Butterfly* secondButterfly = secondArray->butterfly();
+
+ unsigned firstArraySize = firstButterfly->publicLength();
+ unsigned secondArraySize = secondButterfly->publicLength();
+
+ IndexingType type = firstArray->mergeIndexingTypeForCopying(secondArray->indexingType());
+ if (type == NonArray || !firstArray->canFastCopy(vm, secondArray) || 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() == 3);
+
+ VM& vm = exec->vm();
+ JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
+ JSArray* otherArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
+ JSValue startValue = exec->uncheckedArgument(2);
+ ASSERT(startValue.isAnyInt() && startValue.asAnyInt() >= 0 && startValue.asAnyInt() <= std::numeric_limits<unsigned>::max());
+ unsigned startIndex = static_cast<unsigned>(startValue.asAnyInt());
+ if (!resultArray->appendMemcpy(exec, vm, startIndex, otherArray))
+ moveElements(exec, vm, resultArray, startIndex, otherArray, otherArray->length());
+
+ return JSValue::encode(jsUndefined());
+}
+
+
// -------------------- ArrayPrototype.constructor Watchpoint ------------------
static bool verbose = false;
EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
EncodedJSValue JSC_HOST_CALL arrayProtoFuncValues(ExecState*);
+EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState*);
+EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState*);
} // namespace JSC
macro(with) \
macro(yield)
-#define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL_NOT_IMPLEMENTED_YET(macro)\
- macro(isConcatSpreadable) \
-
#define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(macro) \
macro(hasInstance) \
+ macro(isConcatSpreadable) \
macro(iterator) \
macro(match) \
macro(replace) \
macro(isArrayConstructor) \
macro(isConstructor) \
macro(isRegExpObject) \
+ macro(concatMemcpy) \
+ macro(appendMemcpy) \
macro(predictFinalLengthFromArgumunts) \
macro(print) \
macro(isSet) \
SLOW_PATH_DECL(slow_path_assert)
{
BEGIN();
- ASSERT_WITH_MESSAGE(OP(1).jsValue().asBoolean(), "JS assertion failed at line %d in:\n%s\n", pc[2].u.operand, exec->codeBlock()->sourceCodeForTools().data());
+ RELEASE_ASSERT_WITH_MESSAGE(OP(1).jsValue().asBoolean(), "JS assertion failed at line %d in:\n%s\n", pc[2].u.operand, exec->codeBlock()->sourceCodeForTools().data());
END();
}
return (indexingType & IndexingShapeMask) == SlowPutArrayStorageShape;
}
+inline IndexingType indexingTypeForValue(JSValue value)
+{
+ if (value.isInt32())
+ return Int32Shape;
+
+ if (value.isNumber() && value.asNumber() == value.asNumber())
+ return DoubleShape;
+
+ return ContiguousShape;
+}
+
// Return an indexing type that can handle all of the elements of both indexing types.
IndexingType leastUpperBoundOfIndexingTypes(IndexingType, IndexingType);
return true;
}
+bool JSArray::appendMemcpy(ExecState* exec, VM& vm, unsigned startIndex, JSC::JSArray* otherArray)
+{
+ if (!canFastCopy(vm, otherArray))
+ return false;
+
+ IndexingType type = indexingType();
+ IndexingType copyType = mergeIndexingTypeForCopying(otherArray->indexingType());
+ if (type == ArrayWithUndecided && copyType != NonArray) {
+ if (copyType == ArrayWithInt32)
+ convertUndecidedToInt32(vm);
+ else if (copyType == ArrayWithDouble)
+ convertUndecidedToDouble(vm);
+ else if (copyType == ArrayWithContiguous)
+ convertUndecidedToContiguous(vm);
+ else {
+ ASSERT(copyType == ArrayWithUndecided);
+ return true;
+ }
+ } else if (type != copyType)
+ return false;
+
+ unsigned otherLength = otherArray->length();
+ unsigned newLength = startIndex + otherLength;
+ if (newLength >= MIN_SPARSE_ARRAY_INDEX)
+ return false;
+
+ if (!ensureLength(vm, newLength)) {
+ throwOutOfMemoryError(exec);
+ return false;
+ }
+ ASSERT(copyType == indexingType());
+
+ if (type == ArrayWithDouble)
+ memcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);
+ else
+ memcpy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength);
+
+ return true;
+}
+
bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
{
Butterfly* butterfly = m_butterfly.get();
}
}
-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();
#include "ArrayConventions.h"
#include "ButterflyInlines.h"
+#include "JSCellInlines.h"
#include "JSObject.h"
namespace JSC {
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 copying.
+ IndexingType mergeIndexingTypeForCopying(IndexingType other);
+ bool appendMemcpy(ExecState*, VM&, unsigned startIndex, JSArray* otherArray);
enum ShiftCountMode {
// This form of shift hints that we're doing queueing. With this assumption in hand,
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:
inline bool isJSArray(JSCell* cell)
{
- return cell->classInfo() == JSArray::info();
+ ASSERT((cell->classInfo() == JSArray::info()) == (cell->type() == ArrayType));
+ return cell->type() == ArrayType;
}
inline bool isJSArray(JSValue v) { return v.isCell() && isJSArray(v.asCell()); }
--- /dev/null
+/*
+ * 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::mergeIndexingTypeForCopying(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(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 (structure(vm)->holesMustForwardToPrototype(vm)
+ || otherArray->structure(vm)->holesMustForwardToPrototype(vm))
+ return false;
+ return true;
+}
+
+} // namespace JSC
+
+#endif /* JSArrayInlines_h */
JSFunction* privateFuncThisTimeValue = JSFunction::create(vm, this, 0, String(), dateProtoFuncGetTime);
JSFunction* privateFuncThisNumberValue = JSFunction::create(vm, this, 0, String(), numberProtoFuncValueOf);
JSFunction* privateFuncIsArrayConstructor = JSFunction::create(vm, this, 0, String(), arrayConstructorPrivateFuncIsArrayConstructor);
+ 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);
JSObject* arrayIteratorPrototype = ArrayIteratorPrototype::create(vm, this, ArrayIteratorPrototype::createStructure(vm, this, m_iteratorPrototype.get()));
JSFunction* privateFuncCreateArrayIterator = JSFunction::createBuiltinFunction(vm, arrayPrototypeCreateArrayIteratorConstructorCodeGenerator(vm), this);
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),
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),
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->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),
GlobalPropertyInfo(vm.propertyNames->builtinNames().arrayIteratorValueNextPrivateName(), privateFuncArrayIteratorValueNext, DontEnum | DontDelete | ReadOnly),
void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
{
- if (value.isInt32()) {
+ IndexingType type = indexingTypeForValue(value);
+ if (type == Int32Shape) {
convertUndecidedToInt32(vm);
return;
}
- if (value.isDouble() && value.asNumber() == value.asNumber()) {
+ if (type == DoubleShape) {
convertUndecidedToDouble(vm);
return;
}
-
+
+ ASSERT(type == ContiguousShape);
convertUndecidedToContiguous(vm);
}
PureForwardingProxyType,
ImpureProxyType,
WithScopeType,
+ ArrayType,
DirectArgumentsType,
ScopedArgumentsType,
return constructEmptyObject(exec, exec->lexicalGlobalObject()->objectStructureForObjectConstructor());
}
+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)
- 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
--- /dev/null
+// 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;
+ }
+}
--- /dev/null
+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"
+
+}
--- /dev/null
+// 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) { }
+
+}
--- /dev/null
+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"
+ }
+}
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";