diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 18f4b7ec6221..b3e26c53989c 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -869,3 +869,89 @@ function ArrayToString() { return callFunction(std_Object_toString, array); return callContentFunction(func, array); } + +// ES 2016 draft Mar 25, 2016 22.1.3.1. +// Note: Array.prototype.concat.length is 1. +function ArrayConcat(arg1) { + // Step 1. + var O = ToObject(this); + + // Step 2. + var A = std_Array(0); + + // Step 3. + var n = 0; + + // Step 4 (implicit in |arguments|). + + // Step 5. + var i = 0, argsLen = arguments.length; + + // Step 5.a (first element). + var E = O; + + var k, len; + while (true) { + // Steps 5.b-c. + // IsArray should be replaced with IsConcatSpreadable (bug 1041586). + if (IsArray(E)) { + // Step 5.c.ii. + len = ToLength(E.length); + + // Step 5.c.iii. + if (n + len > MAX_NUMERIC_INDEX) + ThrowTypeError(JSMSG_TOO_LONG_ARRAY); + + if (IsPackedArray(E)) { + // Step 5.c.i, 5.c.iv, and 5.c.iv.5. + for (k = 0; k < len; k++) { + // Steps 5.c.iv.1-3. + // IsPackedArray(E) ensures that |k in E| is always true. + _DefineDataProperty(A, n, E[k]); + + // Step 5.c.iv.4. + n++; + } + } else { + // Step 5.c.i, 5.c.iv, and 5.c.iv.5. + for (k = 0; k < len; k++) { + // Steps 5.c.iv.1-3. + if (k in E) + _DefineDataProperty(A, n, E[k]); + + // Step 5.c.iv.4. + n++; + } + } + } else { + // Step 5.d.i. + if (n >= MAX_NUMERIC_INDEX) + ThrowTypeError(JSMSG_TOO_LONG_ARRAY); + + // Step 5.d.ii. + _DefineDataProperty(A, n, E); + + // Step 5.d.iii. + n++; + } + + if (i >= argsLen) + break; + // Step 5.a (subsequent elements). + E = arguments[i]; + i++; + } + + // Step 6. + A.length = n; + + // Step 7. + return A; +} + +function ArrayStaticConcat(arr, arg1) { + if (arguments.length < 1) + ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.concat'); + var args = callFunction(std_Array_slice, arguments, 1); + return callFunction(std_Function_apply, ArrayConcat, arr, args); +} diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 31d24a678978..d7b45ed40486 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -5646,7 +5646,7 @@ GetTemplateObjectForNative(JSContext* cx, JSFunction* target, const CallArgs& ar } } - if (native == js::array_concat || native == js::array_slice) { + if (native == js::array_slice) { if (args.thisv().isObject()) { JSObject* obj = &args.thisv().toObject(); if (!obj->isSingleton()) { diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 649665ca0bb0..1a66dd125e2c 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -7976,55 +7976,6 @@ CodeGenerator::visitArrayPushT(LArrayPushT* lir) emitArrayPush(lir, lir->mir(), obj, value, elementsTemp, length); } -typedef JSObject* (*ArrayConcatDenseFn)(JSContext*, HandleObject, HandleObject, HandleObject); -static const VMFunction ArrayConcatDenseInfo = FunctionInfo(ArrayConcatDense); - -void -CodeGenerator::visitArrayConcat(LArrayConcat* lir) -{ - Register lhs = ToRegister(lir->lhs()); - Register rhs = ToRegister(lir->rhs()); - Register temp1 = ToRegister(lir->temp1()); - Register temp2 = ToRegister(lir->temp2()); - - // If 'length == initializedLength' for both arrays we try to allocate an object - // inline and pass it to the stub. Else, we just pass nullptr and the stub falls - // back to a slow path. - Label fail, call; - if (lir->mir()->unboxedThis()) { - masm.load32(Address(lhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1); - masm.branch32(Assembler::NotEqual, Address(lhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail); - } else { - masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1); - masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2); - masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail); - } - if (lir->mir()->unboxedArg()) { - masm.load32(Address(rhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1); - masm.branch32(Assembler::NotEqual, Address(rhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail); - } else { - masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1); - masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2); - masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail); - } - - // Try to allocate an object. - masm.createGCObject(temp1, temp2, lir->mir()->templateObj(), lir->mir()->initialHeap(), &fail); - masm.jump(&call); - { - masm.bind(&fail); - masm.movePtr(ImmPtr(nullptr), temp1); - } - masm.bind(&call); - - pushArg(temp1); - pushArg(ToRegister(lir->rhs())); - pushArg(ToRegister(lir->lhs())); - callVM(ArrayConcatDenseInfo, lir); -} - typedef JSObject* (*ArraySliceDenseFn)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); static const VMFunction ArraySliceDenseInfo = FunctionInfo(array_slice_dense); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 971cbab8d67b..eb4f751dc935 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -297,7 +297,6 @@ class CodeGenerator : public CodeGeneratorSpecific ConstantOrRegister value, Register elementsTemp, Register length); void visitArrayPushV(LArrayPushV* lir); void visitArrayPushT(LArrayPushT* lir); - void visitArrayConcat(LArrayConcat* lir); void visitArraySlice(LArraySlice* lir); void visitArrayJoin(LArrayJoin* lir); void visitLoadUnboxedScalar(LLoadUnboxedScalar* lir); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 42d8932a9648..b58765b793bd 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -14,7 +14,6 @@ _(ArrayPop) \ _(ArrayShift) \ _(ArrayPush) \ - _(ArrayConcat) \ _(ArraySlice) \ _(ArraySplice) \ \ diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 3bdc0d43b81e..77c04df475e4 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -788,7 +788,6 @@ class IonBuilder InliningStatus inlineArrayIsArray(CallInfo& callInfo); InliningStatus inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode); InliningStatus inlineArrayPush(CallInfo& callInfo); - InliningStatus inlineArrayConcat(CallInfo& callInfo); InliningStatus inlineArraySlice(CallInfo& callInfo); InliningStatus inlineArrayJoin(CallInfo& callInfo); InliningStatus inlineArraySplice(CallInfo& callInfo); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 260ea470bd67..d3440dfaca6a 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3175,21 +3175,6 @@ LIRGenerator::visitArrayPush(MArrayPush* ins) } } -void -LIRGenerator::visitArrayConcat(MArrayConcat* ins) -{ - MOZ_ASSERT(ins->type() == MIRType_Object); - MOZ_ASSERT(ins->lhs()->type() == MIRType_Object); - MOZ_ASSERT(ins->rhs()->type() == MIRType_Object); - - LArrayConcat* lir = new(alloc()) LArrayConcat(useFixed(ins->lhs(), CallTempReg1), - useFixed(ins->rhs(), CallTempReg2), - tempFixed(CallTempReg3), - tempFixed(CallTempReg4)); - defineReturn(lir, ins); - assignSafepoint(lir, ins); -} - void LIRGenerator::visitArraySlice(MArraySlice* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index d2fb5c77ae1b..67546875718e 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -220,7 +220,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitEffectiveAddress(MEffectiveAddress* ins); void visitArrayPopShift(MArrayPopShift* ins); void visitArrayPush(MArrayPush* ins); - void visitArrayConcat(MArrayConcat* ins); void visitArraySlice(MArraySlice* ins); void visitArrayJoin(MArrayJoin* ins); void visitLoadUnboxedScalar(MLoadUnboxedScalar* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 65030b8fb966..554727390250 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -77,8 +77,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineArrayPopShift(callInfo, MArrayPopShift::Shift); case InlinableNative::ArrayPush: return inlineArrayPush(callInfo); - case InlinableNative::ArrayConcat: - return inlineArrayConcat(callInfo); case InlinableNative::ArraySlice: return inlineArraySlice(callInfo); case InlinableNative::ArraySplice: @@ -767,128 +765,6 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) return InliningStatus_Inlined; } -IonBuilder::InliningStatus -IonBuilder::inlineArrayConcat(CallInfo& callInfo) -{ - if (callInfo.argc() != 1 || callInfo.constructing()) { - trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); - return InliningStatus_NotInlined; - } - - MDefinition* thisArg = convertUnboxedObjects(callInfo.thisArg()); - MDefinition* objArg = convertUnboxedObjects(callInfo.getArg(0)); - - // Ensure |this|, argument and result are objects. - if (getInlineReturnType() != MIRType_Object) - return InliningStatus_NotInlined; - if (thisArg->type() != MIRType_Object) - return InliningStatus_NotInlined; - if (objArg->type() != MIRType_Object) - return InliningStatus_NotInlined; - - // |this| and the argument must be dense arrays. - TemporaryTypeSet* thisTypes = thisArg->resultTypeSet(); - TemporaryTypeSet* argTypes = objArg->resultTypeSet(); - if (!thisTypes || !argTypes) - return InliningStatus_NotInlined; - - const Class* thisClasp = thisTypes->getKnownClass(constraints()); - if (thisClasp != &ArrayObject::class_ && thisClasp != &UnboxedArrayObject::class_) - return InliningStatus_NotInlined; - bool unboxedThis = (thisClasp == &UnboxedArrayObject::class_); - if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | - OBJECT_FLAG_LENGTH_OVERFLOW)) - { - trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); - return InliningStatus_NotInlined; - } - - const Class* argClasp = argTypes->getKnownClass(constraints()); - if (argClasp != &ArrayObject::class_ && argClasp != &UnboxedArrayObject::class_) - return InliningStatus_NotInlined; - bool unboxedArg = (argClasp == &UnboxedArrayObject::class_); - if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | - OBJECT_FLAG_LENGTH_OVERFLOW)) - { - trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); - return InliningStatus_NotInlined; - } - - // Watch out for indexed properties on the prototype. - if (ArrayPrototypeHasIndexedProperty(this, script())) { - trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); - return InliningStatus_NotInlined; - } - - // Require the 'this' types to have a specific type matching the current - // global, so we can create the result object inline. - if (thisTypes->getObjectCount() != 1) - return InliningStatus_NotInlined; - - ObjectGroup* thisGroup = thisTypes->getGroup(0); - if (!thisGroup) - return InliningStatus_NotInlined; - TypeSet::ObjectKey* thisKey = TypeSet::ObjectKey::get(thisGroup); - if (thisKey->unknownProperties()) - return InliningStatus_NotInlined; - - // Don't inline if 'this' is packed and the argument may not be packed - // (the result array will reuse the 'this' type). - if (!thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED) && - argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED)) - { - trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); - return InliningStatus_NotInlined; - } - - // Constraints modeling this concat have not been generated by inference, - // so check that type information already reflects possible side effects of - // this call. - HeapTypeSetKey thisElemTypes = thisKey->property(JSID_VOID); - - TemporaryTypeSet* resTypes = getInlineReturnTypeSet(); - if (!resTypes->hasType(TypeSet::ObjectType(thisKey))) - return InliningStatus_NotInlined; - - for (unsigned i = 0; i < argTypes->getObjectCount(); i++) { - TypeSet::ObjectKey* key = argTypes->getObject(i); - if (!key) - continue; - - if (key->unknownProperties()) - return InliningStatus_NotInlined; - - HeapTypeSetKey elemTypes = key->property(JSID_VOID); - if (!elemTypes.knownSubset(constraints(), thisElemTypes)) - return InliningStatus_NotInlined; - - if (thisGroup->clasp() == &UnboxedArrayObject::class_ && - !CanStoreUnboxedType(alloc(), thisGroup->unboxedLayout().elementType(), - MIRType_Value, elemTypes.maybeTypes())) - { - return InliningStatus_NotInlined; - } - } - - // Inline the call. - JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat); - if (!templateObj || templateObj->group() != thisGroup) - return InliningStatus_NotInlined; - - callInfo.setImplicitlyUsedUnchecked(); - - MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), thisArg, objArg, - templateObj, - templateObj->group()->initialHeap(constraints()), - unboxedThis, unboxedArg); - current->add(ins); - current->push(ins); - - if (!resumeAfter(ins)) - return InliningStatus_Error; - return InliningStatus_Inlined; -} - IonBuilder::InliningStatus IonBuilder::inlineArraySlice(CallInfo& callInfo) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index e241e430b546..613ca088f33f 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9839,66 +9839,6 @@ class MArrayPush ALLOW_CLONE(MArrayPush) }; -// Array.prototype.concat on two dense arrays. -class MArrayConcat - : public MBinaryInstruction, - public MixPolicy, ObjectPolicy<1> >::Data -{ - CompilerObject templateObj_; - gc::InitialHeap initialHeap_; - bool unboxedThis_, unboxedArg_; - - MArrayConcat(CompilerConstraintList* constraints, MDefinition* lhs, MDefinition* rhs, - JSObject* templateObj, gc::InitialHeap initialHeap, - bool unboxedThis, bool unboxedArg) - : MBinaryInstruction(lhs, rhs), - templateObj_(templateObj), - initialHeap_(initialHeap), - unboxedThis_(unboxedThis), - unboxedArg_(unboxedArg) - { - setResultType(MIRType_Object); - setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj)); - } - - public: - INSTRUCTION_HEADER(ArrayConcat) - - static MArrayConcat* New(TempAllocator& alloc, CompilerConstraintList* constraints, - MDefinition* lhs, MDefinition* rhs, - JSObject* templateObj, gc::InitialHeap initialHeap, - bool unboxedThis, bool unboxedArg) - { - return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj, - initialHeap, unboxedThis, unboxedArg); - } - - JSObject* templateObj() const { - return templateObj_; - } - - gc::InitialHeap initialHeap() const { - return initialHeap_; - } - - bool unboxedThis() const { - return unboxedThis_; - } - - bool unboxedArg() const { - return unboxedArg_; - } - - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedThis() ? JSVAL_TYPE_INT32 : JSVAL_TYPE_MAGIC) | - AliasSet::BoxedOrUnboxedElements(unboxedArg() ? JSVAL_TYPE_INT32 : JSVAL_TYPE_MAGIC) | - AliasSet::ObjectFields); - } - bool possiblyCalls() const override { - return true; - } -}; - // Array.prototype.slice on a dense array. class MArraySlice : public MTernaryInstruction, diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 29dee2520f44..7e3ef9273003 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -210,7 +210,6 @@ namespace jit { _(ConvertUnboxedObjectToNative) \ _(ArrayPopShift) \ _(ArrayPush) \ - _(ArrayConcat) \ _(ArraySlice) \ _(ArrayJoin) \ _(LoadTypedArrayElementHole) \ diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index a087e9789a13..c07a4b65031d 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -371,25 +371,6 @@ ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval) return true; } -JSObject* -ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject objRes) -{ - if (objRes) { - // Fast path if we managed to allocate an object inline. - if (!js::array_concat_dense(cx, obj1, obj2, objRes)) - return nullptr; - return objRes; - } - - JS::AutoValueArray<3> argv(cx); - argv[0].setUndefined(); - argv[1].setObject(*obj1); - argv[2].setObject(*obj2); - if (!js::array_concat(cx, 1, argv.begin())) - return nullptr; - return &argv[0].toObject(); -} - JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep) { diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 129071329844..5afc876686c2 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -613,7 +613,6 @@ bool StringsEqual(JSContext* cx, HandleString left, HandleString right, bool* re bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval); bool ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length); bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval); -JSObject* ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject res); JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep); bool CharCodeAt(JSContext* cx, HandleString str, int32_t index, uint32_t* code); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index ee25ca3f997a..f244450ab590 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -5410,35 +5410,6 @@ class LArrayPushT : public LInstructionHelper<1, 2, 1> } }; -class LArrayConcat : public LCallInstructionHelper<1, 2, 2> -{ - public: - LIR_HEADER(ArrayConcat) - - LArrayConcat(const LAllocation& lhs, const LAllocation& rhs, - const LDefinition& temp1, const LDefinition& temp2) { - setOperand(0, lhs); - setOperand(1, rhs); - setTemp(0, temp1); - setTemp(1, temp2); - } - const MArrayConcat* mir() const { - return mir_->toArrayConcat(); - } - const LAllocation* lhs() { - return getOperand(0); - } - const LAllocation* rhs() { - return getOperand(1); - } - const LDefinition* temp1() { - return getTemp(0); - } - const LDefinition* temp2() { - return getTemp(1); - } -}; - class LArraySlice : public LCallInstructionHelper<1, 3, 2> { public: diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 6b212d7fcc08..06343baeb1ed 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -265,7 +265,6 @@ _(ArrayPopShiftT) \ _(ArrayPushV) \ _(ArrayPushT) \ - _(ArrayConcat) \ _(ArraySlice) \ _(ArrayJoin) \ _(StoreElementHoleV) \ diff --git a/js/src/js.msg b/js/src/js.msg index 99ef8d2c88fa..8b949a9b186e 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -474,6 +474,9 @@ MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG, 0, JSEXN_ERR, "Type is too large to alloc MSG_DEF(JSMSG_SIMD_FAILED_CONVERSION, 0, JSEXN_RANGEERR, "SIMD conversion loses precision") MSG_DEF(JSMSG_SIMD_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert SIMD value to number") +// Array +MSG_DEF(JSMSG_TOO_LONG_ARRAY, 0, JSEXN_TYPEERR, "Too long array") + // Typed array MSG_DEF(JSMSG_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index") MSG_DEF(JSMSG_NON_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected ArrayBuffer, but species constructor returned non-ArrayBuffer") diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index d58093126766..fcbb562b56d7 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2588,184 +2588,6 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI return true; } -template -DenseElementResult -ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* result) -{ - uint32_t initlen1 = GetBoxedOrUnboxedInitializedLength(obj1); - MOZ_ASSERT(initlen1 == GetAnyBoxedOrUnboxedArrayLength(obj1)); - - uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength(obj2); - MOZ_ASSERT(initlen2 == GetAnyBoxedOrUnboxedArrayLength(obj2)); - - /* No overflow here due to nelements limit. */ - uint32_t len = initlen1 + initlen2; - - MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(result) == 0); - - DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements(cx, result, len); - if (rv != DenseElementResult::Success) - return rv; - - CopyBoxedOrUnboxedDenseElements(cx, result, obj1, 0, 0, initlen1); - CopyBoxedOrUnboxedDenseElements(cx, result, obj2, initlen1, 0, initlen2); - - SetAnyBoxedOrUnboxedArrayLength(cx, result, len); - return DenseElementResult::Success; -} - -DefineBoxedOrUnboxedFunctorPair4(ArrayConcatDenseKernel, - JSContext*, JSObject*, JSObject*, JSObject*); - -bool -js::array_concat_dense(JSContext* cx, HandleObject obj1, HandleObject obj2, - HandleObject result) -{ - ArrayConcatDenseKernelFunctor functor(cx, obj1, obj2, result); - DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, obj1, obj2); - MOZ_ASSERT(rv != DenseElementResult::Incomplete); - return rv == DenseElementResult::Success; -} - -/* - * Python-esque sequence operations. - */ -bool -js::array_concat(JSContext* cx, unsigned argc, Value* vp) -{ - AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.concat"); - CallArgs args = CallArgsFromVp(argc, vp); - - /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */ - Value* p = args.array() - 1; - - /* Create a new Array object and root it using *vp. */ - RootedObject aobj(cx, ToObject(cx, args.thisv())); - if (!aobj) - return false; - - RootedObject narr(cx); - uint32_t length; - bool isArray; - if (!IsArray(cx, aobj, &isArray)) - return false; - if (isArray && !ObjectMayHaveExtraIndexedProperties(aobj)) { - if (!GetLengthProperty(cx, aobj, &length)) - return false; - - size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(aobj); - narr = NewFullyAllocatedArrayTryReuseGroup(cx, aobj, initlen); - if (!narr) - return false; - CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen); - SetAnyBoxedOrUnboxedArrayLength(cx, narr, length); - - args.rval().setObject(*narr); - if (argc == 0) - return true; - argc--; - p++; - - if (length == initlen) { - while (argc) { - HandleValue v = HandleValue::fromMarkedLocation(p); - if (!v.isObject()) - break; - RootedObject obj(cx, &v.toObject()); - - // This should be IsConcatSpreadable - bool isArray; - if (!IsArray(cx, obj, &isArray)) - return false; - if (!isArray || ObjectMayHaveExtraIndexedProperties(obj)) - break; - - uint32_t argLength; - if (!GetLengthProperty(cx, obj, &argLength)) - return false; - - initlen = GetAnyBoxedOrUnboxedInitializedLength(obj); - if (argLength != initlen) - break; - - DenseElementResult result = - EnsureAnyBoxedOrUnboxedDenseElements(cx, narr, length + argLength); - if (result == DenseElementResult::Failure) - return false; - if (result == DenseElementResult::Incomplete) - break; - - SetAnyBoxedOrUnboxedInitializedLength(cx, narr, length + argLength); - - bool success = true; - for (size_t i = 0; i < initlen; i++) { - Value v = GetAnyBoxedOrUnboxedDenseElement(obj, i); - if (!InitAnyBoxedOrUnboxedDenseElement(cx, narr, length + i, v)) { - success = false; - break; - } - } - if (!success) { - SetAnyBoxedOrUnboxedInitializedLength(cx, narr, length); - break; - } - - length += argLength; - SetAnyBoxedOrUnboxedArrayLength(cx, narr, length); - - argc--; - p++; - } - } - } else { - narr = NewFullyAllocatedArrayTryReuseGroup(cx, aobj, 0); - if (!narr) - return false; - args.rval().setObject(*narr); - length = 0; - } - - /* Loop over [0, argc] to concat args into narr, expanding all Arrays. */ - for (unsigned i = 0; i <= argc; i++) { - if (!CheckForInterrupt(cx)) - return false; - HandleValue v = HandleValue::fromMarkedLocation(&p[i]); - if (v.isObject()) { - RootedObject obj(cx, &v.toObject()); - // This should be IsConcatSpreadable - bool isArray; - if (!IsArray(cx, obj, &isArray)) - return false; - if (isArray) { - uint32_t alength; - if (!GetLengthProperty(cx, obj, &alength)) - return false; - RootedValue tmp(cx); - for (uint32_t slot = 0; slot < alength; slot++) { - bool hole; - if (!CheckForInterrupt(cx) || !GetElement(cx, obj, slot, &hole, &tmp)) - return false; - - /* - * Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent - * properties. - */ - if (!hole && !SetArrayElement(cx, narr, length + slot, tmp)) - return false; - } - length += alength; - continue; - } - } - - if (!SetArrayElement(cx, narr, length, v)) - return false; - length++; - } - - return SetLengthProperty(cx, narr, length); -} - struct SortComparatorIndexes { bool operator()(uint32_t a, uint32_t b, bool* lessOrEqualp) { @@ -3147,7 +2969,7 @@ static const JSFunctionSpec array_methods[] = { JS_INLINABLE_FN("splice", array_splice, 2,JSFUN_GENERIC_NATIVE, ArraySplice), /* Pythonic sequence methods. */ - JS_INLINABLE_FN("concat", array_concat, 1,JSFUN_GENERIC_NATIVE, ArrayConcat), + JS_SELF_HOSTED_FN("concat", "ArrayConcat", 1,0), JS_INLINABLE_FN("slice", array_slice, 2,JSFUN_GENERIC_NATIVE, ArraySlice), JS_SELF_HOSTED_FN("lastIndexOf", "ArrayLastIndexOf", 1,0), @@ -3179,6 +3001,7 @@ static const JSFunctionSpec array_methods[] = { static const JSFunctionSpec array_static_methods[] = { JS_INLINABLE_FN("isArray", array_isArray, 1,0, ArrayIsArray), + JS_SELF_HOSTED_FN("concat", "ArrayStaticConcat", 2,0), JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0), JS_SELF_HOSTED_FN("indexOf", "ArrayStaticIndexOf", 2,0), JS_SELF_HOSTED_FN("forEach", "ArrayStaticForEach", 2,0), diff --git a/js/src/jsarray.h b/js/src/jsarray.h index a37e7c525708..13ec4b76933a 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -166,13 +166,6 @@ array_pop(JSContext* cx, unsigned argc, js::Value* vp); extern bool array_splice_impl(JSContext* cx, unsigned argc, js::Value* vp, bool pop); -extern bool -array_concat(JSContext* cx, unsigned argc, js::Value* vp); - -extern bool -array_concat_dense(JSContext* cx, HandleObject arr1, HandleObject arr2, - HandleObject result); - extern bool array_join(JSContext* cx, unsigned argc, js::Value* vp); diff --git a/js/src/tests/ecma_3/Array/regress-322135-02.js b/js/src/tests/ecma_3/Array/regress-322135-02.js index 4991cf51bce8..3fbfb59f3738 100644 --- a/js/src/tests/ecma_3/Array/regress-322135-02.js +++ b/js/src/tests/ecma_3/Array/regress-322135-02.js @@ -1,3 +1,4 @@ +// |reftest| skip -- slow (bug 1234947) /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this