Bug 1233642 - Part 2: Self-host Array.prototype.concat. r=efaust

This commit is contained in:
Tooru Fujisawa 2015-12-18 17:02:27 +09:00
parent 0586c2ef05
commit 7155770a42
19 changed files with 93 additions and 490 deletions

View File

@ -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);
}

View File

@ -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()) {

View File

@ -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<ArrayConcatDenseFn>(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<ArraySliceDenseFn>(array_slice_dense);

View File

@ -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);

View File

@ -14,7 +14,6 @@
_(ArrayPop) \
_(ArrayShift) \
_(ArrayPush) \
_(ArrayConcat) \
_(ArraySlice) \
_(ArraySplice) \
\

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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)
{

View File

@ -9839,66 +9839,6 @@ class MArrayPush
ALLOW_CLONE(MArrayPush)
};
// Array.prototype.concat on two dense arrays.
class MArrayConcat
: public MBinaryInstruction,
public MixPolicy<ObjectPolicy<0>, 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,

View File

@ -210,7 +210,6 @@ namespace jit {
_(ConvertUnboxedObjectToNative) \
_(ArrayPopShift) \
_(ArrayPush) \
_(ArrayConcat) \
_(ArraySlice) \
_(ArrayJoin) \
_(LoadTypedArrayElementHole) \

View File

@ -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)
{

View File

@ -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);

View File

@ -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:

View File

@ -265,7 +265,6 @@
_(ArrayPopShiftT) \
_(ArrayPushV) \
_(ArrayPushT) \
_(ArrayConcat) \
_(ArraySlice) \
_(ArrayJoin) \
_(StoreElementHoleV) \

View File

@ -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")

View File

@ -2588,184 +2588,6 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
return true;
}
template <JSValueType TypeOne, JSValueType TypeTwo>
DenseElementResult
ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* result)
{
uint32_t initlen1 = GetBoxedOrUnboxedInitializedLength<TypeOne>(obj1);
MOZ_ASSERT(initlen1 == GetAnyBoxedOrUnboxedArrayLength(obj1));
uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength<TypeTwo>(obj2);
MOZ_ASSERT(initlen2 == GetAnyBoxedOrUnboxedArrayLength(obj2));
/* No overflow here due to nelements limit. */
uint32_t len = initlen1 + initlen2;
MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<TypeOne>(result) == 0);
DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<TypeOne>(cx, result, len);
if (rv != DenseElementResult::Success)
return rv;
CopyBoxedOrUnboxedDenseElements<TypeOne, TypeOne>(cx, result, obj1, 0, 0, initlen1);
CopyBoxedOrUnboxedDenseElements<TypeOne, TypeTwo>(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),

View File

@ -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);

View File

@ -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