mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-26 03:35:33 +00:00
Bug 1233642 - Part 2: Self-host Array.prototype.concat. r=efaust
This commit is contained in:
parent
0586c2ef05
commit
7155770a42
@ -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);
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -14,7 +14,6 @@
|
||||
_(ArrayPop) \
|
||||
_(ArrayShift) \
|
||||
_(ArrayPush) \
|
||||
_(ArrayConcat) \
|
||||
_(ArraySlice) \
|
||||
_(ArraySplice) \
|
||||
\
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -210,7 +210,6 @@ namespace jit {
|
||||
_(ConvertUnboxedObjectToNative) \
|
||||
_(ArrayPopShift) \
|
||||
_(ArrayPush) \
|
||||
_(ArrayConcat) \
|
||||
_(ArraySlice) \
|
||||
_(ArrayJoin) \
|
||||
_(LoadTypedArrayElementHole) \
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -265,7 +265,6 @@
|
||||
_(ArrayPopShiftT) \
|
||||
_(ArrayPushV) \
|
||||
_(ArrayPushT) \
|
||||
_(ArrayConcat) \
|
||||
_(ArraySlice) \
|
||||
_(ArrayJoin) \
|
||||
_(StoreElementHoleV) \
|
||||
|
@ -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")
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user