1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* JS Array interface. */
9 #ifndef builtin_Array_h
10 #define builtin_Array_h
12 #include "mozilla/Attributes.h"
14 #include "vm/JSObject.h"
19 class TrampolineNativeFrameLayout
;
24 MOZ_ALWAYS_INLINE
bool IdIsIndex(jsid id
, uint32_t* indexp
) {
26 int32_t i
= id
.toInt();
28 *indexp
= uint32_t(i
);
32 if (MOZ_UNLIKELY(!id
.isAtom())) {
36 JSAtom
* atom
= id
.toAtom();
37 return atom
->isIndex(indexp
);
40 // The methods below only create dense boxed arrays.
42 // Create a dense array with no capacity allocated, length set to 0, in the
43 // normal (i.e. non-tenured) heap.
44 extern ArrayObject
* NewDenseEmptyArray(JSContext
* cx
);
46 // Create a dense array with no capacity allocated, length set to 0, in the
48 extern ArrayObject
* NewTenuredDenseEmptyArray(JSContext
* cx
);
50 // Create a dense array with a set length, but without allocating space for the
51 // contents. This is useful, e.g., when accepting length from the user.
52 extern ArrayObject
* NewDenseUnallocatedArray(
53 JSContext
* cx
, uint32_t length
, NewObjectKind newKind
= GenericObject
);
55 // Create a dense array with length and capacity == 'length', initialized length
57 extern ArrayObject
* NewDenseFullyAllocatedArray(
58 JSContext
* cx
, uint32_t length
, NewObjectKind newKind
= GenericObject
,
59 gc::AllocSite
* site
= nullptr);
61 // Create a dense array with length == 'length', initialized length set to 0,
62 // and capacity == 'length' clamped to EagerAllocationMaxLength.
63 extern ArrayObject
* NewDensePartlyAllocatedArray(
64 JSContext
* cx
, uint32_t length
, NewObjectKind newKind
= GenericObject
);
66 // Like NewDensePartlyAllocatedArray, but the array will have |proto| as
67 // prototype (or Array.prototype if |proto| is nullptr).
68 extern ArrayObject
* NewDensePartlyAllocatedArrayWithProto(JSContext
* cx
,
72 // Create a dense array from the given array values, which must be rooted.
73 extern ArrayObject
* NewDenseCopiedArray(JSContext
* cx
, uint32_t length
,
75 NewObjectKind newKind
= GenericObject
);
77 // Create a dense array from the given (linear)string values, which must be
79 extern ArrayObject
* NewDenseCopiedArray(JSContext
* cx
, uint32_t length
,
80 JSLinearString
** values
,
81 NewObjectKind newKind
= GenericObject
);
83 // Like NewDenseCopiedArray, but the array will have |proto| as prototype (or
84 // Array.prototype if |proto| is nullptr).
85 extern ArrayObject
* NewDenseCopiedArrayWithProto(JSContext
* cx
, uint32_t length
,
89 // Create a dense array with the given shape and length.
90 extern ArrayObject
* NewDenseFullyAllocatedArrayWithShape(
91 JSContext
* cx
, uint32_t length
, Handle
<SharedShape
*> shape
);
93 extern ArrayObject
* NewArrayWithShape(JSContext
* cx
, uint32_t length
,
94 Handle
<Shape
*> shape
);
96 extern bool ToLength(JSContext
* cx
, HandleValue v
, uint64_t* out
);
98 extern bool GetLengthProperty(JSContext
* cx
, HandleObject obj
,
101 extern bool SetLengthProperty(JSContext
* cx
, HandleObject obj
, uint32_t length
);
104 * Copy 'length' elements from aobj to vp.
106 * This function assumes 'length' is effectively the result of calling
107 * GetLengthProperty on aobj. vp must point to rooted memory.
109 extern bool GetElements(JSContext
* cx
, HandleObject aobj
, uint32_t length
,
112 /* Natives exposed for optimization by the interpreter and JITs. */
114 extern bool array_includes(JSContext
* cx
, unsigned argc
, js::Value
* vp
);
115 extern bool array_indexOf(JSContext
* cx
, unsigned argc
, js::Value
* vp
);
116 extern bool array_lastIndexOf(JSContext
* cx
, unsigned argc
, js::Value
* vp
);
117 extern bool array_pop(JSContext
* cx
, unsigned argc
, js::Value
* vp
);
118 extern bool array_join(JSContext
* cx
, unsigned argc
, js::Value
* vp
);
119 extern bool array_sort(JSContext
* cx
, unsigned argc
, js::Value
* vp
);
121 extern void ArrayShiftMoveElements(ArrayObject
* arr
);
123 extern JSObject
* ArraySliceDense(JSContext
* cx
, HandleObject obj
, int32_t begin
,
124 int32_t end
, HandleObject result
);
126 extern JSObject
* ArgumentsSliceDense(JSContext
* cx
, HandleObject obj
,
127 int32_t begin
, int32_t end
,
128 HandleObject result
);
130 extern ArrayObject
* NewArrayWithNullProto(JSContext
* cx
);
133 * Append the given (non-hole) value to the end of an array. The array must be
134 * a newborn array -- that is, one which has not been exposed to script for
135 * arbitrary manipulation. (This method optimizes on the assumption that
136 * extending the array to accommodate the element will never make the array
137 * sparse, which requires that the array be completely filled.)
139 extern bool NewbornArrayPush(JSContext
* cx
, HandleObject obj
, const Value
& v
);
141 extern ArrayObject
* ArrayConstructorOneArg(JSContext
* cx
,
142 Handle
<ArrayObject
*> templateObject
,
146 extern bool ArrayInfo(JSContext
* cx
, unsigned argc
, Value
* vp
);
149 /* Array constructor native. Exposed only so the JIT can know its address. */
150 extern bool ArrayConstructor(JSContext
* cx
, unsigned argc
, Value
* vp
);
152 // Like Array constructor, but doesn't perform GetPrototypeFromConstructor.
153 extern bool array_construct(JSContext
* cx
, unsigned argc
, Value
* vp
);
155 extern JSString
* ArrayToSource(JSContext
* cx
, HandleObject obj
);
157 extern bool IsCrossRealmArrayConstructor(JSContext
* cx
, JSObject
* obj
,
160 extern bool ObjectMayHaveExtraIndexedOwnProperties(JSObject
* obj
);
162 extern bool ObjectMayHaveExtraIndexedProperties(JSObject
* obj
);
164 extern bool PrototypeMayHaveIndexedProperties(NativeObject
* obj
);
166 // JS::IsArray has multiple overloads, use js::IsArrayFromJit to disambiguate.
167 extern bool IsArrayFromJit(JSContext
* cx
, HandleObject obj
, bool* isArray
);
169 extern bool ArrayLengthGetter(JSContext
* cx
, HandleObject obj
, HandleId id
,
170 MutableHandleValue vp
);
172 extern bool ArrayLengthSetter(JSContext
* cx
, HandleObject obj
, HandleId id
,
173 HandleValue v
, ObjectOpResult
& result
);
175 // Note: we use uint32_t because the JIT code uses branch32.
176 enum class ArraySortResult
: uint32_t {
180 CallJSSameRealmNoRectifier
183 // We use a JIT trampoline to optimize sorting with a comparator function. The
184 // trampoline frame has an ArraySortData instance that contains all state used
185 // by the sorting algorithm. The sorting algorithm is implemented as a C++
186 // "generator" that can yield to the trampoline to perform a fast JIT => JIT
187 // call to the comparator function. When the comparator function returns, the
188 // trampoline calls back into C++ to resume the sorting algorithm.
190 // ArraySortData stores the JS Values in a js::Vector. To ensure we don't leak
191 // its memory, we have debug assertions to check that for each C++ constructor
192 // call we call |freeMallocData| exactly once. C++ code calls |freeMallocData|
193 // when it's done sorting and the JIT exception handler calls it when unwinding
194 // the trampoline frame.
195 class ArraySortData
{
197 enum class ComparatorKind
: uint8_t {
200 JSSameRealmNoRectifier
,
203 static constexpr size_t ComparatorActualArgs
= 2;
205 // Insertion sort is used if the length is < InsertionSortLimit.
206 static constexpr size_t InsertionSortLimit
= 24;
208 using ValueVector
= GCVector
<Value
, 8, SystemAllocPolicy
>;
210 protected: // Silence Clang warning about unused private fields.
211 // Data for the comparator call. These fields must match the JitFrameLayout
212 // to let us perform efficient calls to the comparator from JIT code.
213 // This is asserted in the JIT trampoline code.
214 // callArgs[0] is also used to store the return value of the sort function and
216 uintptr_t descriptor_
;
217 JSObject
* comparator_
= nullptr;
219 Value callArgs
[ComparatorActualArgs
];
225 JSObject
* obj_
= nullptr;
230 // The value of the .length property.
233 // The number of items to sort. Can be less than |length| if the object has
243 // The state value determines where we resume in sortWithComparator.
244 enum class State
: uint8_t {
251 State state
= State::Initial
;
252 ComparatorKind comparatorKind_
;
254 // Optional padding to ensure proper alignment of the comparator JIT frame.
255 #if !defined(JS_64BIT) && !defined(DEBUG)
256 protected: // Silence Clang warning about unused private field.
261 explicit ArraySortData(JSContext
* cx
);
263 void MOZ_ALWAYS_INLINE
init(JSObject
* obj
, JSObject
* comparator
,
264 ValueVector
&& vec
, uint32_t length
,
267 JSContext
* cx() const { return cx_
; }
269 JSObject
* comparator() const {
270 MOZ_ASSERT(comparator_
);
274 Value
returnValue() const { return callArgs
[0]; }
275 void setReturnValue(JSObject
* obj
) { callArgs
[0].setObject(*obj
); }
277 Value
comparatorArg(size_t index
) {
278 MOZ_ASSERT(index
< ComparatorActualArgs
);
279 return callArgs
[index
];
281 Value
comparatorThisValue() const { return thisv
; }
282 Value
comparatorReturnValue() const { return callArgs
[0]; }
283 void setComparatorArgs(const Value
& x
, const Value
& y
) {
287 void setComparatorReturnValue(const Value
& v
) { callArgs
[0] = v
; }
289 ComparatorKind
comparatorKind() const { return comparatorKind_
; }
291 static ArraySortResult
sortWithComparator(ArraySortData
* d
);
293 void freeMallocData();
294 void trace(JSTracer
* trc
);
296 static constexpr int32_t offsetOfDescriptor() {
297 return offsetof(ArraySortData
, descriptor_
);
299 static constexpr int32_t offsetOfComparator() {
300 return offsetof(ArraySortData
, comparator_
);
302 static constexpr int32_t offsetOfComparatorReturnValue() {
303 return offsetof(ArraySortData
, callArgs
[0]);
305 static constexpr int32_t offsetOfComparatorThis() {
306 return offsetof(ArraySortData
, thisv
);
308 static constexpr int32_t offsetOfComparatorArgs() {
309 return offsetof(ArraySortData
, callArgs
);
313 extern ArraySortResult
ArraySortFromJit(
314 JSContext
* cx
, jit::TrampolineNativeFrameLayout
* frame
);
316 class MOZ_NON_TEMPORARY_CLASS ArraySpeciesLookup final
{
318 * An ArraySpeciesLookup holds the following:
320 * Array.prototype (arrayProto_)
321 * To ensure that the incoming array has the standard proto.
323 * Array.prototype's shape (arrayProtoShape_)
324 * To ensure that Array.prototype has not been modified.
326 * Array (arrayConstructor_)
327 * Array's shape (arrayConstructorShape_)
328 * To ensure that Array has not been modified.
330 * Array.prototype's slot number for constructor (arrayProtoConstructorSlot_)
331 * To quickly retrieve and ensure that the Array constructor
332 * stored in the slot has not changed.
334 * Array's slot number for the @@species getter. (arraySpeciesGetterSlot_)
335 * Array's canonical value for @@species (canonicalSpeciesFunc_)
336 * To quickly retrieve and ensure that the @@species getter for Array
339 * MOZ_INIT_OUTSIDE_CTOR fields below are set in |initialize()|. The
340 * constructor only initializes a |state_| field, that defines whether the
341 * other fields are accessible.
344 // Pointer to canonical Array.prototype and Array.
345 MOZ_INIT_OUTSIDE_CTOR NativeObject
* arrayProto_
;
346 MOZ_INIT_OUTSIDE_CTOR NativeObject
* arrayConstructor_
;
348 // Shape of matching Array, and slot containing the @@species property, and
349 // the canonical value.
350 MOZ_INIT_OUTSIDE_CTOR Shape
* arrayConstructorShape_
;
351 MOZ_INIT_OUTSIDE_CTOR
uint32_t arraySpeciesGetterSlot_
;
352 MOZ_INIT_OUTSIDE_CTOR JSFunction
* canonicalSpeciesFunc_
;
354 // Shape of matching Array.prototype object, and slot containing the
355 // constructor for it.
356 MOZ_INIT_OUTSIDE_CTOR Shape
* arrayProtoShape_
;
357 MOZ_INIT_OUTSIDE_CTOR
uint32_t arrayProtoConstructorSlot_
;
359 enum class State
: uint8_t {
360 // Flags marking the lazy initialization of the above fields.
364 // The disabled flag is set when we don't want to try optimizing
365 // anymore because core objects were changed.
369 State state_
= State::Uninitialized
;
371 // Initialize the internal fields.
372 void initialize(JSContext
* cx
);
377 // Check if the global array-related objects have not been messed with
378 // in a way that would disable this cache.
379 bool isArrayStateStillSane();
382 /** Construct an |ArraySpeciesLookup| in the uninitialized state. */
383 ArraySpeciesLookup() { reset(); }
385 // Try to optimize the @@species lookup for an array.
386 bool tryOptimizeArray(JSContext
* cx
, ArrayObject
* array
);
388 // Purge the cache and all info associated with it.
390 if (state_
== State::Initialized
) {
398 #endif /* builtin_Array_h */