summaryrefslogtreecommitdiffstats
path: root/js/src/jsarray.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsarray.cpp')
-rw-r--r--js/src/jsarray.cpp523
1 files changed, 212 insertions, 311 deletions
diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp
index 3b4c957dc..5854fda4c 100644
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -64,7 +64,7 @@ using JS::ToUint32;
bool
JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer)
{
- if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
+ if (obj->is<ArrayObject>()) {
*answer = IsArrayAnswer::Array;
return true;
}
@@ -100,11 +100,6 @@ js::GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp)
return true;
}
- if (obj->is<UnboxedArrayObject>()) {
- *lengthp = obj->as<UnboxedArrayObject>().length();
- return true;
- }
-
if (obj->is<ArgumentsObject>()) {
ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
if (!argsobj.hasOverriddenLength()) {
@@ -253,18 +248,20 @@ static bool
GetElement(JSContext* cx, HandleObject obj, HandleObject receiver,
uint32_t index, bool* hole, MutableHandleValue vp)
{
- AssertGreaterThanZero(index);
- if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
- vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, uint32_t(index)));
- if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
- *hole = false;
- return true;
+ if (obj->isNative()) {
+ NativeObject* nobj = &obj->as<NativeObject>();
+ if (index < nobj->getDenseInitializedLength()) {
+ vp.set(nobj->getDenseElement(size_t(index)));
+ if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
+ *hole = false;
+ return true;
+ }
}
- }
- if (obj->is<ArgumentsObject>()) {
- if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
- *hole = false;
- return true;
+ if (nobj->is<ArgumentsObject>() && index <= UINT32_MAX) {
+ if (nobj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
+ *hole = false;
+ return true;
+ }
}
}
@@ -283,8 +280,8 @@ ElementAdder::append(JSContext* cx, HandleValue v)
{
MOZ_ASSERT(index_ < length_);
if (resObj_) {
- DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1);
+ NativeObject* resObj = &resObj_->as<NativeObject>();
+ DenseElementResult result = resObj->setOrExtendDenseElements(cx, index_, v.address(), 1);
if (result == DenseElementResult::Failure)
return false;
if (result == DenseElementResult::Incomplete) {
@@ -336,37 +333,31 @@ js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver,
return true;
}
-template <JSValueType Type>
-DenseElementResult
-GetBoxedOrUnboxedDenseElements(JSObject* aobj, uint32_t length, Value* vp)
+static bool
+GetDenseElements(NativeObject* aobj, uint32_t length, Value* vp)
{
MOZ_ASSERT(!ObjectMayHaveExtraIndexedProperties(aobj));
- if (length > GetBoxedOrUnboxedInitializedLength<Type>(aobj))
- return DenseElementResult::Incomplete;
+ if (length > aobj->getDenseInitializedLength())
+ return false;
for (size_t i = 0; i < length; i++) {
- vp[i] = GetBoxedOrUnboxedDenseElement<Type>(aobj, i);
+ vp[i] = aobj->getDenseElement(i);
// No other indexed properties so hole => undefined.
if (vp[i].isMagic(JS_ELEMENTS_HOLE))
vp[i] = UndefinedValue();
}
- return DenseElementResult::Success;
+ return true;
}
-DefineBoxedOrUnboxedFunctor3(GetBoxedOrUnboxedDenseElements,
- JSObject*, uint32_t, Value*);
-
bool
js::GetElements(JSContext* cx, HandleObject aobj, uint32_t length, Value* vp)
{
if (!ObjectMayHaveExtraIndexedProperties(aobj)) {
- GetBoxedOrUnboxedDenseElementsFunctor functor(aobj, length, vp);
- DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, aobj);
- if (result != DenseElementResult::Incomplete)
- return result == DenseElementResult::Success;
+ if (GetDenseElements(&aobj->as<NativeObject>(), length, vp))
+ return true;
}
if (aobj->is<ArgumentsObject>()) {
@@ -398,9 +389,9 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v)
{
MOZ_ASSERT(index >= 0);
- if ((obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) && !obj->isIndexed() && index <= UINT32_MAX) {
- DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1);
+ if (obj->is<ArrayObject>() && !obj->isIndexed() && index <= UINT32_MAX) {
+ NativeObject* nobj = &obj->as<NativeObject>();
+ DenseElementResult result = nobj->setOrExtendDenseElements(cx, uint32_t(index), v.address(), 1);
if (result != DenseElementResult::Incomplete)
return result == DenseElementResult::Success;
}
@@ -520,24 +511,6 @@ struct ReverseIndexComparator
}
};
-bool
-js::CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* newLen)
-{
- double d;
-
- if (!ToUint32(cx, v, newLen))
- return false;
-
- if (!ToNumber(cx, v, &d))
- return false;
-
- if (d == *newLen)
- return true;
-
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
- return false;
-}
-
/* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */
bool
js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
@@ -559,12 +532,22 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
} else {
// Step 2 is irrelevant in our implementation.
- // Steps 3-7.
- MOZ_ASSERT_IF(attrs & JSPROP_IGNORE_VALUE, value.isUndefined());
- if (!CanonicalizeArrayLengthValue(cx, value, &newLen))
+ // Step 3.
+ if (!ToUint32(cx, value, &newLen))
+ return false;
+
+ // Step 4.
+ double d;
+ if (!ToNumber(cx, value, &d))
return false;
- // Step 8 is irrelevant in our implementation.
+ // Step 5.
+ if (d != newLen) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
+ return false;
+ }
+
+ // Steps 6-8 are irrelevant in our implementation.
}
// Steps 9-11.
@@ -823,7 +806,7 @@ array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
static inline bool
ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj)
{
- return (!obj->isNative() && !obj->is<UnboxedArrayObject>()) ||
+ return !obj->isNative() ||
obj->isIndexed() ||
obj->is<TypedArrayObject>() ||
ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames,
@@ -854,7 +837,7 @@ js::ObjectMayHaveExtraIndexedProperties(JSObject* obj)
if (ObjectMayHaveExtraIndexedOwnProperties(obj))
return true;
- if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0)
+ if (obj->as<NativeObject>().getDenseInitializedLength() != 0)
return true;
} while (true);
}
@@ -1064,31 +1047,32 @@ struct StringSeparatorOp
}
};
-template <typename SeparatorOp, JSValueType Type>
-static DenseElementResult
-ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length,
+template <typename SeparatorOp>
+static bool
+ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleNativeObject obj, uint64_t length,
StringBuffer& sb, uint32_t* numProcessed)
{
// This loop handles all elements up to initializedLength. If
// length > initLength we rely on the second loop to add the
// other elements.
MOZ_ASSERT(*numProcessed == 0);
- uint32_t initLength = Min<uint32_t>(GetBoxedOrUnboxedInitializedLength<Type>(obj), length);
+ uint32_t initLength = Min<uint32_t>(obj->getDenseInitializedLength(),
+ length);
while (*numProcessed < initLength) {
if (!CheckForInterrupt(cx))
- return DenseElementResult::Failure;
+ return false;
- Value elem = GetBoxedOrUnboxedDenseElement<Type>(obj, *numProcessed);
+ Value elem = obj->as<NativeObject>().getDenseElement(*numProcessed);
if (elem.isString()) {
if (!sb.append(elem.toString()))
- return DenseElementResult::Failure;
+ return false;
} else if (elem.isNumber()) {
if (!NumberValueToStringBuffer(cx, elem, sb))
- return DenseElementResult::Failure;
+ return false;
} else if (elem.isBoolean()) {
if (!BooleanToStringBuffer(elem.toBoolean(), sb))
- return DenseElementResult::Failure;
+ return false;
} else if (elem.isObject() || elem.isSymbol()) {
/*
* Object stringifying could modify the initialized length or make
@@ -1104,33 +1088,13 @@ ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_
}
if (++(*numProcessed) != length && !sepOp(cx, sb))
- return DenseElementResult::Failure;
+ return false;
}
- return DenseElementResult::Incomplete;
+ return true;
}
template <typename SeparatorOp>
-struct ArrayJoinDenseKernelFunctor {
- JSContext* cx;
- SeparatorOp sepOp;
- HandleObject obj;
- uint32_t length;
- StringBuffer& sb;
- uint32_t* numProcessed;
-
- ArrayJoinDenseKernelFunctor(JSContext* cx, SeparatorOp sepOp, HandleObject obj,
- uint32_t length, StringBuffer& sb, uint32_t* numProcessed)
- : cx(cx), sepOp(sepOp), obj(obj), length(length), sb(sb), numProcessed(numProcessed)
- {}
-
- template <JSValueType Type>
- DenseElementResult operator()() {
- return ArrayJoinDenseKernel<SeparatorOp, Type>(cx, sepOp, obj, length, sb, numProcessed);
- }
-};
-
-template <typename SeparatorOp>
static bool
ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length,
StringBuffer& sb)
@@ -1138,10 +1102,10 @@ ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t len
uint32_t i = 0;
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
- ArrayJoinDenseKernelFunctor<SeparatorOp> functor(cx, sepOp, obj, length, sb, &i);
- DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
- if (result == DenseElementResult::Failure)
+ if (!ArrayJoinDenseKernel<SeparatorOp>(cx, sepOp, obj.as<NativeObject>(), length, sb, &i))
+ {
return false;
+ }
}
if (i != length) {
@@ -1212,11 +1176,14 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp)
// An optimized version of a special case of steps 7-11: when length==1 and
// the 0th element is a string, ToString() of that element is a no-op and
// so it can be immediately returned as the result.
- if (length == 1 && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) {
- Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0);
- if (elem0.isString()) {
- args.rval().set(elem0);
- return true;
+ if (length == 1 && obj->isNative()) {
+ NativeObject* nobj = &obj->as<NativeObject>();
+ if (nobj->getDenseInitializedLength() == 1) {
+ Value elem0 = nobj->getDenseElement(0);
+ if (elem0.isString()) {
+ args.rval().set(elem0);
+ return true;
+ }
}
}
@@ -1259,7 +1226,7 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp)
}
// Step 11
- JSString *str = sb.finishString();
+ JSString* str = sb.finishString();
if (!str)
return false;
@@ -1288,10 +1255,6 @@ array_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
args.rval().setString(cx->names().empty);
return true;
}
- if (obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().length() == 0) {
- args.rval().setString(cx->names().empty);
- return true;
- }
AutoCycleDetector detector(cx, obj);
if (!detector.init())
@@ -1328,8 +1291,9 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start,
return false;
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
- DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, start, vector, count, updateTypes);
+ NativeObject* nobj = &obj->as<NativeObject>();
+ DenseElementResult result = nobj->setOrExtendDenseElements(cx, uint32_t(start), vector,
+ count, updateTypes);
if (result != DenseElementResult::Incomplete)
return result == DenseElementResult::Success;
}
@@ -1363,54 +1327,45 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start,
return true;
}
-template <JSValueType Type>
-DenseElementResult
-ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
+static DenseElementResult
+ArrayReverseDenseKernel(JSContext* cx, HandleNativeObject obj, uint32_t length)
{
/* An empty array or an array with no elements is already reversed. */
- if (length == 0 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
+ if (length == 0 || obj->getDenseInitializedLength() == 0)
return DenseElementResult::Success;
- if (Type == JSVAL_TYPE_MAGIC) {
- if (obj->as<NativeObject>().denseElementsAreFrozen())
- return DenseElementResult::Incomplete;
+ if (obj->denseElementsAreFrozen())
+ return DenseElementResult::Incomplete;
- /*
- * It's actually surprisingly complicated to reverse an array due to the
- * orthogonality of array length and array capacity while handling
- * leading and trailing holes correctly. Reversing seems less likely to
- * be a common operation than other array mass-mutation methods, so for
- * now just take a probably-small memory hit (in the absence of too many
- * holes in the array at its start) and ensure that the capacity is
- * sufficient to hold all the elements in the array if it were full.
- */
- DenseElementResult result = obj->as<NativeObject>().ensureDenseElements(cx, length, 0);
- if (result != DenseElementResult::Success)
- return result;
+ /*
+ * It's actually surprisingly complicated to reverse an array due to the
+ * orthogonality of array length and array capacity while handling
+ * leading and trailing holes correctly. Reversing seems less likely to
+ * be a common operation than other array mass-mutation methods, so for
+ * now just take a probably-small memory hit (in the absence of too many
+ * holes in the array at its start) and ensure that the capacity is
+ * sufficient to hold all the elements in the array if it were full.
+ */
+ DenseElementResult result = obj->ensureDenseElements(cx, length, 0);
+ if (result != DenseElementResult::Success)
+ return result;
- /* Fill out the array's initialized length to its proper length. */
- obj->as<NativeObject>().ensureDenseInitializedLength(cx, length, 0);
- } else {
- // Unboxed arrays can only be reversed here if their initialized length
- // matches their actual length. Otherwise the reversal will place holes
- // at the beginning of the array, which we don't support.
- if (length != obj->as<UnboxedArrayObject>().initializedLength())
- return DenseElementResult::Incomplete;
- }
+ /* Fill out the array's initialized length to its proper length. */
+ obj->ensureDenseInitializedLength(cx, length, 0);
RootedValue origlo(cx), orighi(cx);
uint32_t lo = 0, hi = length - 1;
for (; lo < hi; lo++, hi--) {
- origlo = GetBoxedOrUnboxedDenseElement<Type>(obj, lo);
- orighi = GetBoxedOrUnboxedDenseElement<Type>(obj, hi);
- SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, lo, orighi);
+ origlo = obj->getDenseElement(lo);
+ orighi = obj->getDenseElement(hi);
+ obj->setDenseElement(lo, orighi);
if (orighi.isMagic(JS_ELEMENTS_HOLE) &&
!SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo)))
{
return DenseElementResult::Failure;
}
- SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, hi, origlo);
+ obj->setDenseElement(hi, origlo);
if (origlo.isMagic(JS_ELEMENTS_HOLE) &&
!SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi)))
{
@@ -1421,9 +1376,6 @@ ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
return DenseElementResult::Success;
}
-DefineBoxedOrUnboxedFunctor3(ArrayReverseDenseKernel,
- JSContext*, HandleObject, uint32_t);
-
bool
js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
{
@@ -1438,8 +1390,8 @@ js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
return false;
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
- ArrayReverseDenseKernelFunctor functor(cx, obj, len);
- DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
+ DenseElementResult result =
+ ArrayReverseDenseKernel(cx, obj.as<NativeObject>(), uint32_t(len));
if (result != DenseElementResult::Incomplete) {
/*
* Per ECMA-262, don't update the length of the array, even if the new
@@ -2082,8 +2034,8 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, length,
- args.array(), args.length());
+ obj->as<NativeObject>().setOrExtendDenseElements(cx, uint32_t(length),
+ args.array(), args.length());
if (result != DenseElementResult::Incomplete) {
if (result == DenseElementResult::Failure)
return false;
@@ -2091,14 +2043,8 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp)
uint32_t newlength = length + args.length();
args.rval().setNumber(newlength);
- // SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the
- // length for boxed and unboxed arrays. Handle updates to the length of
- // non-arrays here.
- bool isArray;
- if (!IsArray(cx, obj, &isArray))
- return false;
-
- if (!isArray)
+ // Handle updates to the length of non-arrays here.
+ if (!obj->is<ArrayObject>())
return SetLengthProperty(cx, obj, newlength);
return true;
@@ -2154,42 +2100,46 @@ js::array_pop(JSContext* cx, unsigned argc, Value* vp)
return SetLengthProperty(cx, obj, index);
}
-template <JSValueType Type>
-static inline DenseElementResult
-ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
+void
+js::ArrayShiftMoveElements(NativeObject* obj)
{
- MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
+ MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
+
+ size_t initlen = obj->getDenseInitializedLength();
+ MOZ_ASSERT(initlen > 0);
/*
* At this point the length and initialized length have already been
* decremented and the result fetched, so just shift the array elements
* themselves.
*/
- size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
- if (Type == JSVAL_TYPE_MAGIC) {
- obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen);
- } else {
- uint8_t* data = obj->as<UnboxedArrayObject>().elements();
- size_t elementSize = UnboxedTypeSize(Type);
- memmove(data, data + elementSize, initlen * elementSize);
- }
-
- return DenseElementResult::Success;
+ obj->moveDenseElementsNoPreBarrier(0, 1, initlen);
}
-DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*);
+static inline void
+SetInitializedLength(JSContext* cx, NativeObject* obj, size_t initlen)
+{
+ size_t oldInitlen = obj->getDenseInitializedLength();
+ obj->setDenseInitializedLength(initlen);
+ if (initlen < oldInitlen)
+ obj->shrinkElements(cx, initlen);
+}
-void
-js::ArrayShiftMoveElements(JSObject* obj)
+static DenseElementResult
+MoveDenseElements(JSContext* cx, NativeObject* obj, uint32_t dstStart, uint32_t srcStart,
+ uint32_t length)
{
- MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
+ if (obj->denseElementsAreFrozen())
+ return DenseElementResult::Incomplete;
- ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj);
- JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
+ if (!obj->maybeCopyElementsForWrite(cx))
+ return DenseElementResult::Failure;
+ obj->moveDenseElements(dstStart, srcStart, length);
+
+ return DenseElementResult::Success;
}
-template <JSValueType Type>
-DenseElementResult
+static DenseElementResult
ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval)
{
if (ObjectMayHaveExtraIndexedProperties(obj))
@@ -2202,25 +2152,22 @@ ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval)
if (MOZ_UNLIKELY(group->hasAllFlags(OBJECT_FLAG_ITERATED)))
return DenseElementResult::Incomplete;
- size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
+ size_t initlen = obj->as<NativeObject>().getDenseInitializedLength();
if (initlen == 0)
return DenseElementResult::Incomplete;
- rval.set(GetBoxedOrUnboxedDenseElement<Type>(obj, 0));
+ rval.set(obj->as<NativeObject>().getDenseElement(0));
if (rval.isMagic(JS_ELEMENTS_HOLE))
rval.setUndefined();
- DenseElementResult result = MoveBoxedOrUnboxedDenseElements<Type>(cx, obj, 0, 1, initlen - 1);
+ DenseElementResult result = MoveDenseElements(cx, &obj->as<NativeObject>(), 0, 1, initlen - 1);
if (result != DenseElementResult::Success)
return result;
- SetBoxedOrUnboxedInitializedLength<Type>(cx, obj, initlen - 1);
+ SetInitializedLength(cx, obj.as<NativeObject>(), initlen - 1);
return DenseElementResult::Success;
}
-DefineBoxedOrUnboxedFunctor3(ArrayShiftDenseKernel,
- JSContext*, HandleObject, MutableHandleValue);
-
/* ES5 15.4.4.9 */
bool
js::array_shift(JSContext* cx, unsigned argc, Value* vp)
@@ -2252,8 +2199,7 @@ js::array_shift(JSContext* cx, unsigned argc, Value* vp)
uint32_t newlen = len - 1;
/* Fast paths. */
- ArrayShiftDenseKernelFunctor functor(cx, obj, args.rval());
- DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
+ DenseElementResult result = ArrayShiftDenseKernel(cx, obj, args.rval());
if (result != DenseElementResult::Incomplete) {
if (result == DenseElementResult::Failure)
return false;
@@ -2307,9 +2253,6 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
if (args.length() > 0) {
/* Slide up the array to make room for all args at the bottom. */
if (length > 0) {
- // Only include a fast path for boxed arrays. Unboxed arrays can'nt
- // be optimized here because unshifting temporarily places holes at
- // the start of the array.
bool optimized = false;
do {
if (!obj->is<ArrayObject>())
@@ -2369,10 +2312,10 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
}
/*
- * 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
+ * Returns true if this is a dense array whose properties ending at |endIndex|
+ * (exclusive) 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
@@ -2383,11 +2326,11 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co
return false;
/* There's no optimizing possible if it's not an array. */
- if (!arr->is<ArrayObject>() && !arr->is<UnboxedArrayObject>())
+ if (!arr->is<ArrayObject>())
return false;
/* If it's a frozen array, always pick the slow path */
- if (arr->is<ArrayObject>() && arr->as<ArrayObject>().denseElementsAreFrozen())
+ if (arr->as<ArrayObject>().denseElementsAreFrozen())
return false;
/*
@@ -2419,7 +2362,23 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co
* is subsumed by the initializedLength comparison.)
*/
return !ObjectMayHaveExtraIndexedProperties(arr) &&
- startingIndex + count <= GetAnyBoxedOrUnboxedInitializedLength(arr);
+ startingIndex + count <= arr->as<NativeObject>().getDenseInitializedLength();
+}
+
+static inline DenseElementResult
+CopyDenseElements(JSContext* cx, NativeObject* dst, NativeObject* src,
+ uint32_t dstStart, uint32_t srcStart, uint32_t length)
+{
+ MOZ_ASSERT(dst->getDenseInitializedLength() == dstStart);
+ MOZ_ASSERT(src->getDenseInitializedLength() >= srcStart + length);
+ MOZ_ASSERT(dst->getDenseCapacity() >= dstStart + length);
+
+ dst->setDenseInitializedLength(dstStart + length);
+
+ const Value* vp = src->getDenseElements() + srcStart;
+ dst->initDenseElements(dstStart, vp, length);
+
+ return DenseElementResult::Success;
}
/* ES 2016 draft Mar 25, 2016 22.1.3.26. */
@@ -2520,7 +2479,9 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
/* Steps 10-11. */
DebugOnly<DenseElementResult> result =
- CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
+ CopyDenseElements(cx, &arr->as<NativeObject>(),
+ &obj->as<NativeObject>(), 0,
+ actualStart, actualDeleteCount);
MOZ_ASSERT(result.value == DenseElementResult::Success);
/* Step 12 (implicit). */
@@ -2557,14 +2518,13 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
/* Steps 15.a-b. */
DenseElementResult result =
- MoveAnyBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex,
- len - sourceIndex);
+ MoveDenseElements(cx, &obj->as<NativeObject>(), targetIndex, sourceIndex, len - sourceIndex);
MOZ_ASSERT(result != DenseElementResult::Incomplete);
if (result == DenseElementResult::Failure)
return false;
/* Steps 15.c-d. */
- SetAnyBoxedOrUnboxedInitializedLength(cx, obj, finalLength);
+ SetInitializedLength(cx, obj.as<NativeObject>(), finalLength);
} else {
/*
* This is all very slow if the length is very large. We don't yet
@@ -2644,15 +2604,15 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI
if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
DenseElementResult result =
- MoveAnyBoxedOrUnboxedDenseElements(cx, obj, actualStart + itemCount,
- actualStart + actualDeleteCount,
- len - (actualStart + actualDeleteCount));
+ MoveDenseElements(cx, &obj->as<NativeObject>(), actualStart + itemCount,
+ actualStart + actualDeleteCount,
+ len - (actualStart + actualDeleteCount));
MOZ_ASSERT(result != DenseElementResult::Incomplete);
if (result == DenseElementResult::Failure)
return false;
/* Steps 16.a-b. */
- SetAnyBoxedOrUnboxedInitializedLength(cx, obj, len + itemCount - actualDeleteCount);
+ SetInitializedLength(cx, obj.as<NativeObject>(), len + itemCount - actualDeleteCount);
} else {
RootedValue fromValue(cx);
for (double k = len - actualDeleteCount; k > actualStart; k--) {
@@ -2824,7 +2784,7 @@ SliceSlowly(JSContext* cx, HandleObject obj, HandleObject receiver,
}
static bool
-SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, HandleObject result)
+SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, HandleArrayObject result)
{
MOZ_ASSERT(begin <= end);
@@ -2874,26 +2834,28 @@ ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint32_t length, uint32_t be
begin = end;
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
- size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
+ size_t initlen = obj->as<NativeObject>().getDenseInitializedLength();
size_t count = 0;
if (initlen > begin)
count = Min<size_t>(initlen - begin, end - begin);
- RootedObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count));
+ RootedArrayObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count));
if (!narr)
return false;
- SetAnyBoxedOrUnboxedArrayLength(cx, narr, end - begin);
+
+ MOZ_ASSERT(count >= narr->as<ArrayObject>().length());
+ narr->as<ArrayObject>().setLength(cx, count);
if (count) {
DebugOnly<DenseElementResult> result =
- CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
+ CopyDenseElements(cx, &narr->as<NativeObject>(), &obj->as<NativeObject>(), 0, begin, count);
MOZ_ASSERT(result.value == DenseElementResult::Success);
}
arr.set(narr);
return true;
}
- RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
+ RootedArrayObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
if (!narr)
return false;
@@ -3010,11 +2972,10 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp)
return true;
}
-template <JSValueType Type>
-DenseElementResult
-ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t endArg, JSObject* result)
+static bool
+ArraySliceDenseKernel(JSContext* cx, ArrayObject* arr, int32_t beginArg, int32_t endArg, ArrayObject* result)
{
- int32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
+ int32_t length = arr->length();
uint32_t begin = NormalizeSliceTerm(beginArg, length);
uint32_t end = NormalizeSliceTerm(endArg, length);
@@ -3022,33 +2983,33 @@ ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t en
if (begin > end)
begin = end;
- size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
+ size_t initlen = arr->getDenseInitializedLength();
+ size_t count = Min<size_t>(initlen - begin, end - begin);
if (initlen > begin) {
- size_t count = Min<size_t>(initlen - begin, end - begin);
if (count) {
- DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, count);
- if (rv != DenseElementResult::Success)
- return rv;
- CopyBoxedOrUnboxedDenseElements<Type, Type>(cx, result, obj, 0, begin, count);
+ if (!result->ensureElements(cx, count))
+ return false;
+ CopyDenseElements(cx, &result->as<NativeObject>(), &arr->as<NativeObject>(), 0, begin, count);
}
}
- SetAnyBoxedOrUnboxedArrayLength(cx, result, end - begin);
- return DenseElementResult::Success;
-}
+ MOZ_ASSERT(count >= result->length());
+ result->setLength(cx, count);
-DefineBoxedOrUnboxedFunctor5(ArraySliceDenseKernel,
- JSContext*, JSObject*, int32_t, int32_t, JSObject*);
+ return true;
+}
JSObject*
js::array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end,
HandleObject result)
{
if (result && IsArraySpecies(cx, obj)) {
- ArraySliceDenseKernelFunctor functor(cx, obj, begin, end, result);
- DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
- MOZ_ASSERT(rv != DenseElementResult::Incomplete);
- return rv == DenseElementResult::Success ? result : nullptr;
+ if (!ArraySliceDenseKernel(cx, &obj->as<ArrayObject>(), begin, end,
+ &result->as<ArrayObject>()))
+ {
+ return nullptr;
+ }
+ return result;
}
// Slower path if the JIT wasn't able to allocate an object inline.
@@ -3079,7 +3040,7 @@ array_isArray(JSContext* cx, unsigned argc, Value* vp)
static bool
ArrayFromCallArgs(JSContext* cx, CallArgs& args, HandleObject proto = nullptr)
{
- JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto);
+ ArrayObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto);
if (!obj)
return false;
@@ -3244,7 +3205,7 @@ ArrayConstructorImpl(JSContext* cx, CallArgs& args, bool isConstructor)
}
}
- JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto);
+ ArrayObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto);
if (!obj)
return false;
@@ -3270,7 +3231,7 @@ js::array_construct(JSContext* cx, unsigned argc, Value* vp)
return ArrayConstructorImpl(cx, args, /* isConstructor = */ false);
}
-JSObject*
+ArrayObject*
js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt)
{
if (lengthInt < 0) {
@@ -3565,7 +3526,7 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSOb
return arr;
}
-JSObject*
+ArrayObject*
js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap)
{
MOZ_ASSERT(!gc::IsInsideNursery(templateObject));
@@ -3578,30 +3539,21 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc
return arr;
}
-// Return a new boxed or unboxed array with the specified length and allocated
-// capacity (up to maxLength), using the specified group if possible. If the
-// specified group cannot be used, ensure that the created array at least has
-// the given [[Prototype]].
+// Return a new array with the specified length and allocated capacity (up to
+// maxLength), using the specified group if possible. If the specified group
+// cannot be used, ensure that the created array at least has the given
+// [[Prototype]].
template <uint32_t maxLength>
-static inline JSObject*
+static inline ArrayObject*
NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
NewObjectKind newKind = GenericObject)
{
MOZ_ASSERT(newKind != SingletonObject);
- if (group->maybePreliminaryObjects())
- group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
-
- if (group->shouldPreTenure() || group->maybePreliminaryObjects())
+ if (group->shouldPreTenure())
newKind = TenuredObject;
RootedObject proto(cx, group->proto().toObject());
- if (group->maybeUnboxedLayout()) {
- if (length > UnboxedArrayObject::MaximumCapacity)
- return NewArray<maxLength>(cx, length, proto, newKind);
- return UnboxedArrayObject::create(cx, group, length, newKind, maxLength);
- }
-
ArrayObject* res = NewArray<maxLength>(cx, length, proto, newKind);
if (!res)
return nullptr;
@@ -3613,20 +3565,17 @@ NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length
if (res->length() > INT32_MAX)
res->setLength(cx, res->length());
- if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
- preliminaryObjects->registerNewObject(res);
-
return res;
}
-JSObject*
+ArrayObject*
js::NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
NewObjectKind newKind)
{
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
}
-JSObject*
+ArrayObject*
js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length)
{
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
@@ -3635,16 +3584,13 @@ js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup g
// Return a new array with the default prototype and specified allocated
// capacity and length. If possible, try to reuse the group of the input
// object. The resulting array will either reuse the input object's group or
-// will have unknown property types. Additionally, the result will have the
-// same boxed/unboxed elements representation as the input object, unless
-// |length| is larger than the input object's initialized length (in which case
-// UnboxedArrayObject::MaximumCapacity might be exceeded).
+// will have unknown property types.
template <uint32_t maxLength>
-static inline JSObject*
+static inline ArrayObject*
NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
NewObjectKind newKind = GenericObject)
{
- if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
+ if (!obj->is<ArrayObject>())
return NewArray<maxLength>(cx, length, nullptr, newKind);
if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype())
@@ -3657,20 +3603,20 @@ NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
return NewArrayTryUseGroup<maxLength>(cx, group, length, newKind);
}
-JSObject*
+ArrayObject*
js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
NewObjectKind newKind)
{
return NewArrayTryReuseGroup<UINT32_MAX>(cx, obj, length, newKind);
}
-JSObject*
+ArrayObject*
js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length)
{
return NewArrayTryReuseGroup<ArrayObject::EagerAllocationMaxLength>(cx, obj, length);
}
-JSObject*
+ArrayObject*
js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
NewObjectKind newKind)
{
@@ -3680,7 +3626,7 @@ js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
}
-JSObject*
+ArrayObject*
js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto)
{
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
@@ -3689,68 +3635,23 @@ js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
}
-bool
-js::MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup group,
- const Value* vp, size_t length)
-{
- static const size_t EagerPreliminaryObjectAnalysisThreshold = 800;
-
- // Force analysis to see if an unboxed array can be used when making a
- // sufficiently large array, to avoid excessive analysis and copying later
- // on. If this is the first array of its group that is being created, first
- // make a dummy array with the initial elements of the array we are about
- // to make, so there is some basis for the unboxed array analysis.
- if (length > EagerPreliminaryObjectAnalysisThreshold) {
- if (PreliminaryObjectArrayWithTemplate* objects = group->maybePreliminaryObjects()) {
- if (objects->empty()) {
- size_t nlength = Min<size_t>(length, 100);
- JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, nlength);
- if (!obj)
- return false;
- DebugOnly<DenseElementResult> result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, nlength,
- ShouldUpdateTypes::Update);
- MOZ_ASSERT(result.value == DenseElementResult::Success);
- }
- objects->maybeAnalyze(cx, group, /* forceAnalyze = */ true);
- }
- }
- return true;
-}
-
-JSObject*
+ArrayObject*
js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group,
const Value* vp, size_t length, NewObjectKind newKind,
ShouldUpdateTypes updateTypes)
{
- if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length))
- return nullptr;
-
- JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind);
+ ArrayObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind);
if (!obj)
return nullptr;
- DenseElementResult result =
- SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes);
- if (result == DenseElementResult::Failure)
- return nullptr;
- if (result == DenseElementResult::Success)
- return obj;
-
- MOZ_ASSERT(obj->is<UnboxedArrayObject>());
- if (!UnboxedArrayObject::convertToNative(cx->asJSContext(), obj))
- return nullptr;
-
- result = SetOrExtendBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(cx, obj, 0, vp, length,
- updateTypes);
- MOZ_ASSERT(result != DenseElementResult::Incomplete);
+ DenseElementResult result = obj->setOrExtendDenseElements(cx->asJSContext(), 0, vp, length, updateTypes);
if (result == DenseElementResult::Failure)
return nullptr;
-
+ MOZ_ASSERT(result == DenseElementResult::Success);
return obj;
}
-JSObject*
+ArrayObject*
js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length,
HandleObject proto /* = nullptr */)
{