mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Bug 1195030 - Backout of bug 890329 for breaking email reading in gmail. r=bustage a=Ms2ger
This commit is contained in:
parent
6855287ac3
commit
d8c72cf74c
@ -841,111 +841,3 @@ function ArrayToString() {
|
||||
return callFunction(std_Object_toString, array);
|
||||
return callFunction(func, array);
|
||||
}
|
||||
|
||||
//ES 2015, 22.1.3.25 Array.prototype.splice.
|
||||
function ArraySplice(start, deleteCount /*, ...items */) {
|
||||
// Steps 1-2.
|
||||
var O = ToObject(this);
|
||||
|
||||
// Steps 3-4.
|
||||
// FIXME: Array operations should use ToLength (bug 924058).
|
||||
var len = ToInteger(O.length);
|
||||
|
||||
// Steps 5-6.
|
||||
var relativeStart = ToInteger(start);
|
||||
|
||||
// Step 7.
|
||||
var actualStart = relativeStart < 0
|
||||
? std_Math_max(len + relativeStart, 0)
|
||||
: std_Math_min(relativeStart, len);
|
||||
|
||||
// Steps 8-10.
|
||||
var insertCount = 0;
|
||||
var actualDeleteCount = 0;
|
||||
var numArgs = arguments.length;
|
||||
if (numArgs === 1) {
|
||||
actualDeleteCount = len - actualStart;
|
||||
} else if (numArgs > 1) {
|
||||
// Step 10.a.
|
||||
insertCount = numArgs - 2;
|
||||
// Steps 10.b-c.
|
||||
var dc = ToInteger(deleteCount);
|
||||
// Step 10.d.
|
||||
actualDeleteCount = std_Math_min(std_Math_max(dc, 0), len - actualStart);
|
||||
}
|
||||
|
||||
// Step 11.
|
||||
if (len + insertCount - actualDeleteCount > 2 ** 53 - 1)
|
||||
ThrowTypeError(JSMSG_BAD_ARRAY_LENGTH_SPLICE);
|
||||
|
||||
// Steps 12-13.
|
||||
// FIXME: Use ArraySpeciesCreate here.
|
||||
var A = NewDenseArray(actualDeleteCount);
|
||||
|
||||
// Steps 14-15.
|
||||
for (var k = 0; k < actualDeleteCount; k++) {
|
||||
// Step 15.a.
|
||||
var from = actualStart + k;
|
||||
// Steps 15.b-d.
|
||||
if (from in O)
|
||||
_DefineDataProperty(A, k, O[from]);
|
||||
}
|
||||
|
||||
// Steps 16-17.
|
||||
A.length = actualDeleteCount;
|
||||
|
||||
// Step 18 (implicit).
|
||||
// Step 19.
|
||||
var itemCount = insertCount;
|
||||
|
||||
// Step 20.
|
||||
if (itemCount < actualDeleteCount) {
|
||||
// Steps 20.a-b.
|
||||
for (var k = actualStart, kMax = len - actualDeleteCount; k < kMax; k++) {
|
||||
// Step 20.b.i.
|
||||
var from = k + actualDeleteCount;
|
||||
// Step 20.b.ii.
|
||||
var to = k + itemCount;
|
||||
// Steps 20.b.iii-v.
|
||||
if (from in O) {
|
||||
O[to] = O[from];
|
||||
} else {
|
||||
// Step 20.b.vi.
|
||||
delete O[to];
|
||||
}
|
||||
}
|
||||
// Steps 20.c-d.
|
||||
// For packed arrays we can skip these steps: the fact that we don't
|
||||
// delete the elements one by one isn't visible to content code.
|
||||
if (!IsPackedArray(O)) {
|
||||
for (var k = len, kMin = len - actualDeleteCount + itemCount; k > kMin; k--)
|
||||
delete O[k - 1];
|
||||
}
|
||||
} else if (itemCount > actualDeleteCount) {
|
||||
// Step 21.
|
||||
// Steps 21 a-b.
|
||||
for (var k = len - actualDeleteCount, kMin = actualStart; k > kMin; k--) {
|
||||
// Step 21.b.i.
|
||||
var from = k + actualDeleteCount - 1;
|
||||
// Step 21.b.ii.
|
||||
var to = k + itemCount - 1;
|
||||
// Steps 21.b.iii-v.
|
||||
if (from in O) {
|
||||
O[to] = O[from];
|
||||
} else {
|
||||
// Step 21.b.vi.
|
||||
delete O[to];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Steps 22-23.
|
||||
for (var k = actualStart, itemIndex = 2; itemIndex < numArgs; k++, itemIndex++)
|
||||
O[k] = arguments[itemIndex];
|
||||
|
||||
// Steps 24-25.
|
||||
O.length = len - actualDeleteCount + itemCount;
|
||||
|
||||
// Step 26.
|
||||
return A;
|
||||
}
|
||||
|
@ -70,8 +70,8 @@ function Number_isSafeInteger(number) {
|
||||
if (integer !== number)
|
||||
return false;
|
||||
|
||||
// Step 5.
|
||||
if (std_Math_abs(integer) <= 2 ** 53 - 1)
|
||||
// Step 5. If abs(integer) <= 2**53 - 1, return true.
|
||||
if (std_Math_abs(integer) <= 9007199254740991)
|
||||
return true;
|
||||
|
||||
// Step 6.
|
||||
|
@ -110,7 +110,8 @@ function ToLength(v) {
|
||||
if (v <= 0)
|
||||
return 0;
|
||||
|
||||
return std_Math_min(v, 2 ** 53 - 1);
|
||||
// Math.pow(2, 53) - 1 = 0x1fffffffffffff
|
||||
return std_Math_min(v, 0x1fffffffffffff);
|
||||
}
|
||||
|
||||
/* Spec: ECMAScript Draft, 6th edition Oct 14, 2014, 7.2.4 */
|
||||
|
@ -3438,6 +3438,18 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply)
|
||||
emitPopArguments(apply, extraStackSpace);
|
||||
}
|
||||
|
||||
typedef bool (*ArraySpliceDenseFn)(JSContext*, HandleObject, uint32_t, uint32_t);
|
||||
static const VMFunction ArraySpliceDenseInfo = FunctionInfo<ArraySpliceDenseFn>(ArraySpliceDense);
|
||||
|
||||
void
|
||||
CodeGenerator::visitArraySplice(LArraySplice* lir)
|
||||
{
|
||||
pushArg(ToRegister(lir->getDeleteCount()));
|
||||
pushArg(ToRegister(lir->getStart()));
|
||||
pushArg(ToRegister(lir->getObject()));
|
||||
callVM(ArraySpliceDenseInfo, lir);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitBail(LBail* lir)
|
||||
{
|
||||
|
@ -216,6 +216,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void emitSetPropertyPolymorphic(LInstruction* lir, Register obj,
|
||||
Register scratch, const ConstantOrRegister& value);
|
||||
void visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV* ins);
|
||||
void visitArraySplice(LArraySplice* splice);
|
||||
void visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins);
|
||||
void visitAbsI(LAbsI* lir);
|
||||
void visitAtan2D(LAtan2D* lir);
|
||||
|
@ -753,6 +753,7 @@ class IonBuilder
|
||||
InliningStatus inlineArrayConcat(CallInfo& callInfo);
|
||||
InliningStatus inlineArraySlice(CallInfo& callInfo);
|
||||
InliningStatus inlineArrayJoin(CallInfo& callInfo);
|
||||
InliningStatus inlineArraySplice(CallInfo& callInfo);
|
||||
|
||||
// Math natives.
|
||||
InliningStatus inlineMathAbs(CallInfo& callInfo);
|
||||
|
@ -556,6 +556,16 @@ LIRGenerator::visitAssertRecoveredOnBailout(MAssertRecoveredOnBailout* assertion
|
||||
MOZ_CRASH("AssertRecoveredOnBailout nodes are always recovered on bailouts.");
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitArraySplice(MArraySplice* ins)
|
||||
{
|
||||
LArraySplice* lir = new(alloc()) LArraySplice(useRegisterAtStart(ins->object()),
|
||||
useRegisterAtStart(ins->start()),
|
||||
useRegisterAtStart(ins->deleteCount()));
|
||||
add(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitGetDynamicName(MGetDynamicName* ins)
|
||||
{
|
||||
|
@ -99,6 +99,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitLoadArrowThis(MLoadArrowThis* ins);
|
||||
void visitCall(MCall* call);
|
||||
void visitApplyArgs(MApplyArgs* apply);
|
||||
void visitArraySplice(MArraySplice* splice);
|
||||
void visitBail(MBail* bail);
|
||||
void visitUnreachable(MUnreachable* unreachable);
|
||||
void visitEncodeSnapshot(MEncodeSnapshot* ins);
|
||||
|
@ -90,6 +90,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
||||
return inlineArrayConcat(callInfo);
|
||||
if (native == js::array_slice)
|
||||
return inlineArraySlice(callInfo);
|
||||
if (native == js::array_splice)
|
||||
return inlineArraySplice(callInfo);
|
||||
|
||||
// Math natives.
|
||||
if (native == js::math_abs)
|
||||
@ -691,6 +693,46 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineArraySplice(CallInfo& callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 2 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
// Ensure |this|, argument and result are objects.
|
||||
if (getInlineReturnType() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
if (callInfo.thisArg()->type() != MIRType_Object)
|
||||
return InliningStatus_NotInlined;
|
||||
if (callInfo.getArg(0)->type() != MIRType_Int32)
|
||||
return InliningStatus_NotInlined;
|
||||
if (callInfo.getArg(1)->type() != MIRType_Int32)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
// Specialize arr.splice(start, deleteCount) with unused return value and
|
||||
// avoid creating the result array in this case.
|
||||
if (!BytecodeIsPopped(pc)) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MArraySplice* ins = MArraySplice::New(alloc(),
|
||||
callInfo.thisArg(),
|
||||
callInfo.getArg(0),
|
||||
callInfo.getArg(1));
|
||||
|
||||
current->add(ins);
|
||||
pushConstant(UndefinedValue());
|
||||
|
||||
if (!resumeAfter(ins))
|
||||
return InliningStatus_Error;
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineArrayJoin(CallInfo& callInfo)
|
||||
{
|
||||
|
@ -3813,6 +3813,42 @@ class MCallDOMNative : public MCall
|
||||
virtual void computeMovable() override;
|
||||
};
|
||||
|
||||
// arr.splice(start, deleteCount) with unused return value.
|
||||
class MArraySplice
|
||||
: public MTernaryInstruction,
|
||||
public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >::Data
|
||||
{
|
||||
private:
|
||||
|
||||
MArraySplice(MDefinition* object, MDefinition* start, MDefinition* deleteCount)
|
||||
: MTernaryInstruction(object, start, deleteCount)
|
||||
{ }
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(ArraySplice)
|
||||
static MArraySplice* New(TempAllocator& alloc, MDefinition* object,
|
||||
MDefinition* start, MDefinition* deleteCount)
|
||||
{
|
||||
return new(alloc) MArraySplice(object, start, deleteCount);
|
||||
}
|
||||
|
||||
MDefinition* object() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
|
||||
MDefinition* start() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
|
||||
MDefinition* deleteCount() const {
|
||||
return getOperand(2);
|
||||
}
|
||||
|
||||
bool possiblyCalls() const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// fun.apply(self, arguments)
|
||||
class MApplyArgs
|
||||
: public MAryInstruction<3>,
|
||||
|
@ -62,6 +62,7 @@ namespace jit {
|
||||
_(LoadArrowThis) \
|
||||
_(Call) \
|
||||
_(ApplyArgs) \
|
||||
_(ArraySplice) \
|
||||
_(Bail) \
|
||||
_(Unreachable) \
|
||||
_(EncodeSnapshot) \
|
||||
|
@ -251,6 +251,18 @@ StringsEqual(JSContext* cx, HandleString lhs, HandleString rhs, bool* res)
|
||||
template bool StringsEqual<true>(JSContext* cx, HandleString lhs, HandleString rhs, bool* res);
|
||||
template bool StringsEqual<false>(JSContext* cx, HandleString lhs, HandleString rhs, bool* res);
|
||||
|
||||
bool
|
||||
ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t deleteCount)
|
||||
{
|
||||
JS::AutoValueArray<4> argv(cx);
|
||||
argv[0].setUndefined();
|
||||
argv[1].setObject(*obj);
|
||||
argv[2].set(Int32Value(start));
|
||||
argv[3].set(Int32Value(deleteCount));
|
||||
|
||||
return js::array_splice_impl(cx, 2, argv.begin(), false);
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
|
||||
{
|
||||
|
@ -688,6 +688,8 @@ bool InitBaselineFrameForOsr(BaselineFrame* frame, InterpreterFrame* interpFrame
|
||||
JSObject* CreateDerivedTypedObj(JSContext* cx, HandleObject descr,
|
||||
HandleObject owner, int32_t offset);
|
||||
|
||||
bool ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t deleteCount);
|
||||
|
||||
bool Recompile(JSContext* cx);
|
||||
bool ForcedRecompile(JSContext* cx);
|
||||
JSString* RegExpReplace(JSContext* cx, HandleString string, HandleObject regexp,
|
||||
|
@ -1809,6 +1809,34 @@ class LApplyArgsGeneric : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES +
|
||||
}
|
||||
};
|
||||
|
||||
class LArraySplice : public LCallInstructionHelper<0, 3, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(ArraySplice)
|
||||
|
||||
LArraySplice(const LAllocation& object, const LAllocation& start,
|
||||
const LAllocation& deleteCount)
|
||||
{
|
||||
setOperand(0, object);
|
||||
setOperand(1, start);
|
||||
setOperand(2, deleteCount);
|
||||
}
|
||||
|
||||
MArraySplice* mir() const {
|
||||
return mir_->toArraySplice();
|
||||
}
|
||||
|
||||
const LAllocation* getObject() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LAllocation* getStart() {
|
||||
return getOperand(1);
|
||||
}
|
||||
const LAllocation* getDeleteCount() {
|
||||
return getOperand(2);
|
||||
}
|
||||
};
|
||||
|
||||
class LGetDynamicName : public LCallInstructionHelper<BOX_PIECES, 2, 3>
|
||||
{
|
||||
public:
|
||||
|
@ -53,6 +53,7 @@
|
||||
_(NewArray) \
|
||||
_(NewArrayCopyOnWrite) \
|
||||
_(NewArrayDynamicLength) \
|
||||
_(ArraySplice) \
|
||||
_(NewObject) \
|
||||
_(NewTypedObject) \
|
||||
_(NewDeclEnvObject) \
|
||||
|
@ -61,7 +61,6 @@ MSG_DEF(JSMSG_SPREAD_TOO_LARGE, 0, JSEXN_RANGEERR, "array too large due t
|
||||
MSG_DEF(JSMSG_BAD_WEAKMAP_KEY, 0, JSEXN_TYPEERR, "cannot use the given object as a weak map key")
|
||||
MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 1, JSEXN_TYPEERR, "invalid {0} usage")
|
||||
MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 0, JSEXN_RANGEERR, "invalid array length")
|
||||
MSG_DEF(JSMSG_BAD_ARRAY_LENGTH_SPLICE, 0, JSEXN_TYPEERR, "resulting array length too large")
|
||||
MSG_DEF(JSMSG_REDECLARED_VAR, 2, JSEXN_TYPEERR, "redeclaration of {0} {1}")
|
||||
MSG_DEF(JSMSG_UNDECLARED_VAR, 1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}")
|
||||
MSG_DEF(JSMSG_GETTER_ONLY, 0, JSEXN_TYPEERR, "setting a property that has only a getter")
|
||||
|
@ -2261,6 +2261,277 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a dense or unboxed array whose |count| properties
|
||||
* starting from |startingIndex| may be accessed (get, set, delete) directly
|
||||
* through its contiguous vector of elements without fear of getters, setters,
|
||||
* etc. along the prototype chain, or of enumerators requiring notification of
|
||||
* modifications.
|
||||
*/
|
||||
static inline bool
|
||||
CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t count, JSContext* cx)
|
||||
{
|
||||
/* If the desired properties overflow dense storage, we can't optimize. */
|
||||
if (UINT32_MAX - startingIndex < count)
|
||||
return false;
|
||||
|
||||
/* There's no optimizing possible if it's not an array. */
|
||||
if (!arr->is<ArrayObject>() && !arr->is<UnboxedArrayObject>())
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Don't optimize if the array might be in the midst of iteration. We
|
||||
* rely on this to be able to safely move dense array elements around with
|
||||
* just a memmove (see NativeObject::moveDenseArrayElements), without worrying
|
||||
* about updating any in-progress enumerators for properties implicitly
|
||||
* deleted if a hole is moved from one location to another location not yet
|
||||
* visited. See bug 690622.
|
||||
*/
|
||||
ObjectGroup* arrGroup = arr->getGroup(cx);
|
||||
if (MOZ_UNLIKELY(!arrGroup || arrGroup->hasAllFlags(OBJECT_FLAG_ITERATED)))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Another potential wrinkle: what if the enumeration is happening on an
|
||||
* object which merely has |arr| on its prototype chain?
|
||||
*/
|
||||
if (arr->isDelegate())
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Now watch out for getters and setters along the prototype chain or in
|
||||
* other indexed properties on the object. (Note that non-writable length
|
||||
* is subsumed by the initializedLength comparison.)
|
||||
*/
|
||||
return !ObjectMayHaveExtraIndexedProperties(arr) &&
|
||||
startingIndex + count <= GetAnyBoxedOrUnboxedInitializedLength(arr);
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.12. */
|
||||
bool
|
||||
js::array_splice(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
return array_splice_impl(cx, argc, vp, true);
|
||||
}
|
||||
|
||||
bool
|
||||
js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
/* Step 1. */
|
||||
RootedObject obj(cx, ToObject(cx, args.thisv()));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
/* Steps 3-4. */
|
||||
uint32_t len;
|
||||
if (!GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
|
||||
/* Step 5. */
|
||||
double relativeStart;
|
||||
if (!ToInteger(cx, args.get(0), &relativeStart))
|
||||
return false;
|
||||
|
||||
/* Step 6. */
|
||||
uint32_t actualStart;
|
||||
if (relativeStart < 0)
|
||||
actualStart = Max(len + relativeStart, 0.0);
|
||||
else
|
||||
actualStart = Min(relativeStart, double(len));
|
||||
|
||||
/* Step 7. */
|
||||
uint32_t actualDeleteCount;
|
||||
if (args.length() != 1) {
|
||||
double deleteCountDouble;
|
||||
RootedValue cnt(cx, args.length() >= 2 ? args[1] : Int32Value(0));
|
||||
if (!ToInteger(cx, cnt, &deleteCountDouble))
|
||||
return false;
|
||||
actualDeleteCount = Min(Max(deleteCountDouble, 0.0), double(len - actualStart));
|
||||
} else {
|
||||
/*
|
||||
* Non-standard: if start was specified but deleteCount was omitted,
|
||||
* delete to the end of the array. See bug 668024 for discussion.
|
||||
*/
|
||||
actualDeleteCount = len - actualStart;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(len - actualStart >= actualDeleteCount);
|
||||
|
||||
/* Steps 2, 8-9. */
|
||||
RootedObject arr(cx);
|
||||
if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
|
||||
if (returnValueIsUsed) {
|
||||
arr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, actualDeleteCount);
|
||||
if (!arr)
|
||||
return false;
|
||||
DebugOnly<DenseElementResult> result =
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
|
||||
MOZ_ASSERT(result.value == DenseElementResult::Success);
|
||||
}
|
||||
} else {
|
||||
arr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, actualDeleteCount);
|
||||
if (!arr)
|
||||
return false;
|
||||
|
||||
RootedValue fromValue(cx);
|
||||
for (uint32_t k = 0; k < actualDeleteCount; k++) {
|
||||
bool hole;
|
||||
if (!CheckForInterrupt(cx) ||
|
||||
!GetElement(cx, obj, actualStart + k, &hole, &fromValue) ||
|
||||
(!hole && !DefineElement(cx, arr, k, fromValue)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 11. */
|
||||
uint32_t itemCount = (args.length() >= 2) ? (args.length() - 2) : 0;
|
||||
|
||||
if (itemCount < actualDeleteCount) {
|
||||
/* Step 12: the array is being shrunk. */
|
||||
uint32_t sourceIndex = actualStart + actualDeleteCount;
|
||||
uint32_t targetIndex = actualStart + itemCount;
|
||||
uint32_t finalLength = len - actualDeleteCount + itemCount;
|
||||
|
||||
if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
|
||||
/* Steps 12(a)-(b). */
|
||||
DenseElementResult result =
|
||||
MoveAnyBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex,
|
||||
len - sourceIndex);
|
||||
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
|
||||
/* Steps 12(c)-(d). */
|
||||
SetAnyBoxedOrUnboxedInitializedLength(cx, obj, finalLength);
|
||||
} else {
|
||||
/*
|
||||
* This is all very slow if the length is very large. We don't yet
|
||||
* have the ability to iterate in sorted order, so we just do the
|
||||
* pessimistic thing and let CheckForInterrupt handle the
|
||||
* fallout.
|
||||
*/
|
||||
|
||||
/* Steps 12(a)-(b). */
|
||||
RootedValue fromValue(cx);
|
||||
for (uint32_t from = sourceIndex, to = targetIndex; from < len; from++, to++) {
|
||||
if (!CheckForInterrupt(cx))
|
||||
return false;
|
||||
|
||||
bool hole;
|
||||
if (!GetElement(cx, obj, from, &hole, &fromValue))
|
||||
return false;
|
||||
if (hole) {
|
||||
if (!DeletePropertyOrThrow(cx, obj, to))
|
||||
return false;
|
||||
} else {
|
||||
if (!SetArrayElement(cx, obj, to, fromValue))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Steps 12(c)-(d). */
|
||||
for (uint32_t k = len; k > finalLength; k--) {
|
||||
if (!DeletePropertyOrThrow(cx, obj, k - 1))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (itemCount > actualDeleteCount) {
|
||||
/* Step 13. */
|
||||
|
||||
/*
|
||||
* Optimize only if the array is already dense and we can extend it to
|
||||
* its new length. It would be wrong to extend the elements here for a
|
||||
* number of reasons.
|
||||
*
|
||||
* First, this could cause us to fall into the fast-path below. This
|
||||
* would cause elements to be moved into places past the non-writable
|
||||
* length. And when the dense initialized length is updated, that'll
|
||||
* cause the |in| operator to think that those elements actually exist,
|
||||
* even though, properly, setting them must fail.
|
||||
*
|
||||
* Second, extending the elements here will trigger assertions inside
|
||||
* ensureDenseElements that the elements aren't being extended past the
|
||||
* length of a non-writable array. This is because extending elements
|
||||
* will extend capacity -- which might extend them past a non-writable
|
||||
* length, violating the |capacity <= length| invariant for such
|
||||
* arrays. And that would make the various JITted fast-path method
|
||||
* implementations of [].push, [].unshift, and so on wrong.
|
||||
*
|
||||
* If the array length is non-writable, this method *will* throw. For
|
||||
* simplicity, have the slow-path code do it. (Also note that the slow
|
||||
* path may validly *not* throw -- if all the elements being moved are
|
||||
* holes.)
|
||||
*/
|
||||
if (obj->is<ArrayObject>()) {
|
||||
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
|
||||
if (arr->lengthIsWritable()) {
|
||||
DenseElementResult result =
|
||||
arr->ensureDenseElements(cx, arr->length(), itemCount - actualDeleteCount);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
|
||||
DenseElementResult result =
|
||||
MoveAnyBoxedOrUnboxedDenseElements(cx, obj, actualStart + itemCount,
|
||||
actualStart + actualDeleteCount,
|
||||
len - (actualStart + actualDeleteCount));
|
||||
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
|
||||
/* Steps 12(c)-(d). */
|
||||
SetAnyBoxedOrUnboxedInitializedLength(cx, obj, len + itemCount - actualDeleteCount);
|
||||
} else {
|
||||
RootedValue fromValue(cx);
|
||||
for (double k = len - actualDeleteCount; k > actualStart; k--) {
|
||||
if (!CheckForInterrupt(cx))
|
||||
return false;
|
||||
|
||||
double from = k + actualDeleteCount - 1;
|
||||
double to = k + itemCount - 1;
|
||||
|
||||
bool hole;
|
||||
if (!GetElement(cx, obj, from, &hole, &fromValue))
|
||||
return false;
|
||||
|
||||
if (hole) {
|
||||
if (!DeletePropertyOrThrow(cx, obj, to))
|
||||
return false;
|
||||
} else {
|
||||
if (!SetArrayElement(cx, obj, to, fromValue))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 10. */
|
||||
Value* items = args.array() + 2;
|
||||
|
||||
/* Steps 14-15. */
|
||||
for (uint32_t k = actualStart, i = 0; i < itemCount; i++, k++) {
|
||||
if (!SetArrayElement(cx, obj, k, HandleValue::fromMarkedLocation(&items[i])))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Step 16. */
|
||||
double finalLength = double(len) - actualDeleteCount + itemCount;
|
||||
if (!SetLengthProperty(cx, obj, finalLength))
|
||||
return false;
|
||||
|
||||
/* Step 17. */
|
||||
if (returnValueIsUsed)
|
||||
args.rval().setObject(*arr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
DenseElementResult
|
||||
ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* result)
|
||||
@ -2758,7 +3029,7 @@ static const JSFunctionSpec array_methods[] = {
|
||||
JS_FN("pop", array_pop, 0,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("shift", array_shift, 0,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN("unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE),
|
||||
JS_SELF_HOSTED_FN("splice", "ArraySplice", 2,0),
|
||||
JS_FN("splice", array_splice, 2,JSFUN_GENERIC_NATIVE),
|
||||
|
||||
/* Pythonic sequence methods. */
|
||||
JS_FN("concat", array_concat, 1,JSFUN_GENERIC_NATIVE),
|
||||
|
@ -1,14 +0,0 @@
|
||||
var a = {};
|
||||
a[2 ** 53 - 2] = 1;
|
||||
a.length = 2 ** 53 - 1;
|
||||
|
||||
var exception;
|
||||
try {
|
||||
[].splice.call(a, 2 ** 53 - 2, 0, 2, 3, 4, 5);
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
reportCompare(a[2 ** 53 - 2], 1);
|
||||
reportCompare(a.length, 2 ** 53 - 1);
|
||||
reportCompare(exception instanceof TypeError, true, "Array#splice throws TypeError for length overflows");
|
||||
reportCompare(exception.message.indexOf('array length') > -1, true, "Array#splice throws correct error for length overflows");
|
@ -29,11 +29,11 @@ namespace js {
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 300;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 301;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 408,
|
||||
static_assert(JSErr_Limit == 407,
|
||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||
"removed MSG_DEFs from js.msg, you should increment "
|
||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||
|
Loading…
Reference in New Issue
Block a user