1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/builtins/builtins-array-gen.h"
6
7 #include "src/builtins/builtins-iterator-gen.h"
8 #include "src/builtins/builtins-string-gen.h"
9 #include "src/builtins/builtins-typed-array-gen.h"
10 #include "src/builtins/builtins-utils-gen.h"
11 #include "src/builtins/builtins.h"
12 #include "src/codegen/code-stub-assembler.h"
13 #include "src/execution/frame-constants.h"
14 #include "src/heap/factory-inl.h"
15 #include "src/objects/allocation-site-inl.h"
16 #include "src/objects/arguments-inl.h"
17 #include "src/objects/property-cell.h"
18
19 namespace v8 {
20 namespace internal {
21
22 using Node = compiler::Node;
23 using IteratorRecord = TorqueStructIteratorRecord;
24
ArrayBuiltinsAssembler(compiler::CodeAssemblerState * state)25 ArrayBuiltinsAssembler::ArrayBuiltinsAssembler(
26 compiler::CodeAssemblerState* state)
27 : CodeStubAssembler(state),
28 k_(this),
29 a_(this),
30 fully_spec_compliant_(this, {&k_, &a_}) {}
31
TypedArrayMapResultGenerator()32 void ArrayBuiltinsAssembler::TypedArrayMapResultGenerator() {
33 // 6. Let A be ? TypedArraySpeciesCreate(O, len).
34 TNode<JSTypedArray> original_array = CAST(o());
35 const char* method_name = "%TypedArray%.prototype.map";
36
37 TNode<JSTypedArray> a = TypedArraySpeciesCreateByLength(
38 context(), method_name, original_array, len());
39 // In the Spec and our current implementation, the length check is already
40 // performed in TypedArraySpeciesCreate.
41 CSA_ASSERT(this, UintPtrLessThanOrEqual(len(), LoadJSTypedArrayLength(a)));
42 fast_typed_array_target_ =
43 Word32Equal(LoadElementsKind(original_array), LoadElementsKind(a));
44 a_ = a;
45 }
46
47 // See tc39.github.io/ecma262/#sec-%typedarray%.prototype.map.
TypedArrayMapProcessor(TNode<Object> k_value,TNode<UintPtrT> k)48 TNode<Object> ArrayBuiltinsAssembler::TypedArrayMapProcessor(
49 TNode<Object> k_value, TNode<UintPtrT> k) {
50 // 8. c. Let mapped_value be ? Call(callbackfn, T, « kValue, k, O »).
51 TNode<Number> k_number = ChangeUintPtrToTagged(k);
52 TNode<Object> mapped_value =
53 Call(context(), callbackfn(), this_arg(), k_value, k_number, o());
54 Label fast(this), slow(this), done(this), detached(this, Label::kDeferred);
55
56 // 8. d. Perform ? Set(A, Pk, mapped_value, true).
57 // Since we know that A is a TypedArray, this always ends up in
58 // #sec-integer-indexed-exotic-objects-set-p-v-receiver and then
59 // tc39.github.io/ecma262/#sec-integerindexedelementset .
60 Branch(fast_typed_array_target_, &fast, &slow);
61
62 BIND(&fast);
63 // #sec-integerindexedelementset
64 // 5. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let
65 // numValue be ? ToBigInt(v).
66 // 6. Otherwise, let numValue be ? ToNumber(value).
67 TNode<Object> num_value;
68 if (source_elements_kind_ == BIGINT64_ELEMENTS ||
69 source_elements_kind_ == BIGUINT64_ELEMENTS) {
70 num_value = ToBigInt(context(), mapped_value);
71 } else {
72 num_value = ToNumber_Inline(context(), mapped_value);
73 }
74
75 // The only way how this can bailout is because of a detached buffer.
76 // TODO(v8:4153): Consider checking IsDetachedBuffer() and calling
77 // TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric() here
78 // instead to avoid converting k_number back to UintPtrT.
79 EmitElementStore(CAST(a()), k_number, num_value, source_elements_kind_,
80 KeyedAccessStoreMode::STANDARD_STORE, &detached, context());
81 Goto(&done);
82
83 BIND(&slow);
84 {
85 SetPropertyStrict(context(), a(), k_number, mapped_value);
86 Goto(&done);
87 }
88
89 BIND(&detached);
90 // tc39.github.io/ecma262/#sec-integerindexedelementset
91 // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
92 ThrowTypeError(context_, MessageTemplate::kDetachedOperation, name_);
93
94 BIND(&done);
95 return a();
96 }
97
ReturnFromBuiltin(TNode<Object> value)98 void ArrayBuiltinsAssembler::ReturnFromBuiltin(TNode<Object> value) {
99 if (argc_ == nullptr) {
100 Return(value);
101 } else {
102 // argc_ doesn't include the receiver, so it has to be added back in
103 // manually.
104 PopAndReturn(IntPtrAdd(argc_, IntPtrConstant(1)), value);
105 }
106 }
107
InitIteratingArrayBuiltinBody(TNode<Context> context,TNode<Object> receiver,TNode<Object> callbackfn,TNode<Object> this_arg,TNode<IntPtrT> argc)108 void ArrayBuiltinsAssembler::InitIteratingArrayBuiltinBody(
109 TNode<Context> context, TNode<Object> receiver, TNode<Object> callbackfn,
110 TNode<Object> this_arg, TNode<IntPtrT> argc) {
111 context_ = context;
112 receiver_ = receiver;
113 callbackfn_ = callbackfn;
114 this_arg_ = this_arg;
115 argc_ = argc;
116 }
117
GenerateIteratingTypedArrayBuiltinBody(const char * name,const BuiltinResultGenerator & generator,const CallResultProcessor & processor,ForEachDirection direction)118 void ArrayBuiltinsAssembler::GenerateIteratingTypedArrayBuiltinBody(
119 const char* name, const BuiltinResultGenerator& generator,
120 const CallResultProcessor& processor, ForEachDirection direction) {
121 name_ = name;
122
123 // ValidateTypedArray: tc39.github.io/ecma262/#sec-validatetypedarray
124
125 Label throw_not_typed_array(this, Label::kDeferred);
126
127 GotoIf(TaggedIsSmi(receiver_), &throw_not_typed_array);
128 TNode<Map> typed_array_map = LoadMap(CAST(receiver_));
129 GotoIfNot(IsJSTypedArrayMap(typed_array_map), &throw_not_typed_array);
130
131 TNode<JSTypedArray> typed_array = CAST(receiver_);
132 o_ = typed_array;
133
134 TNode<JSArrayBuffer> array_buffer = LoadJSArrayBufferViewBuffer(typed_array);
135 ThrowIfArrayBufferIsDetached(context_, array_buffer, name_);
136
137 len_ = LoadJSTypedArrayLength(typed_array);
138
139 Label throw_not_callable(this, Label::kDeferred);
140 Label distinguish_types(this);
141 GotoIf(TaggedIsSmi(callbackfn_), &throw_not_callable);
142 Branch(IsCallableMap(LoadMap(CAST(callbackfn_))), &distinguish_types,
143 &throw_not_callable);
144
145 BIND(&throw_not_typed_array);
146 ThrowTypeError(context_, MessageTemplate::kNotTypedArray);
147
148 BIND(&throw_not_callable);
149 ThrowTypeError(context_, MessageTemplate::kCalledNonCallable, callbackfn_);
150
151 Label unexpected_instance_type(this);
152 BIND(&unexpected_instance_type);
153 Unreachable();
154
155 std::vector<int32_t> elements_kinds = {
156 #define ELEMENTS_KIND(Type, type, TYPE, ctype) TYPE##_ELEMENTS,
157 TYPED_ARRAYS(ELEMENTS_KIND)
158 #undef ELEMENTS_KIND
159 };
160 std::list<Label> labels;
161 for (size_t i = 0; i < elements_kinds.size(); ++i) {
162 labels.emplace_back(this);
163 }
164 std::vector<Label*> label_ptrs;
165 for (Label& label : labels) {
166 label_ptrs.push_back(&label);
167 }
168
169 BIND(&distinguish_types);
170
171 generator(this);
172
173 TNode<Int32T> elements_kind = LoadMapElementsKind(typed_array_map);
174 Switch(elements_kind, &unexpected_instance_type, elements_kinds.data(),
175 label_ptrs.data(), labels.size());
176
177 size_t i = 0;
178 for (auto it = labels.begin(); it != labels.end(); ++i, ++it) {
179 BIND(&*it);
180 Label done(this);
181 source_elements_kind_ = static_cast<ElementsKind>(elements_kinds[i]);
182 // TODO(tebbi): Silently cancelling the loop on buffer detachment is a
183 // spec violation. Should go to &throw_detached and throw a TypeError
184 // instead.
185 VisitAllTypedArrayElements(array_buffer, processor, &done, direction,
186 typed_array);
187 Goto(&done);
188 // No exception, return success
189 BIND(&done);
190 ReturnFromBuiltin(a_.value());
191 }
192 }
193
VisitAllTypedArrayElements(TNode<JSArrayBuffer> array_buffer,const CallResultProcessor & processor,Label * detached,ForEachDirection direction,TNode<JSTypedArray> typed_array)194 void ArrayBuiltinsAssembler::VisitAllTypedArrayElements(
195 TNode<JSArrayBuffer> array_buffer, const CallResultProcessor& processor,
196 Label* detached, ForEachDirection direction,
197 TNode<JSTypedArray> typed_array) {
198 VariableList list({&a_, &k_}, zone());
199
200 TNode<UintPtrT> start = UintPtrConstant(0);
201 TNode<UintPtrT> end = len_;
202 IndexAdvanceMode advance_mode = IndexAdvanceMode::kPost;
203 int incr = 1;
204 if (direction == ForEachDirection::kReverse) {
205 std::swap(start, end);
206 advance_mode = IndexAdvanceMode::kPre;
207 incr = -1;
208 }
209 k_ = start;
210 BuildFastLoop<UintPtrT>(
211 list, start, end,
212 [&](TNode<UintPtrT> index) {
213 GotoIf(IsDetachedBuffer(array_buffer), detached);
214 TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(typed_array);
215 TNode<Numeric> value = LoadFixedTypedArrayElementAsTagged(
216 data_ptr, index, source_elements_kind_);
217 k_ = index;
218 a_ = processor(this, value, index);
219 },
220 incr, advance_mode);
221 }
222
TF_BUILTIN(ArrayPrototypePop,CodeStubAssembler)223 TF_BUILTIN(ArrayPrototypePop, CodeStubAssembler) {
224 TNode<Int32T> argc =
225 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
226 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
227 CSA_ASSERT(this, IsUndefined(Parameter(Descriptor::kJSNewTarget)));
228
229 CodeStubArguments args(this, argc);
230 TNode<Object> receiver = args.GetReceiver();
231
232 Label runtime(this, Label::kDeferred);
233 Label fast(this);
234
235 // Only pop in this stub if
236 // 1) the array has fast elements
237 // 2) the length is writable,
238 // 3) the elements backing store isn't copy-on-write,
239 // 4) we aren't supposed to shrink the backing store.
240
241 // 1) Check that the array has fast elements.
242 BranchIfFastJSArray(receiver, context, &fast, &runtime);
243
244 BIND(&fast);
245 {
246 TNode<JSArray> array_receiver = CAST(receiver);
247 TNode<IntPtrT> length = SmiUntag(LoadFastJSArrayLength(array_receiver));
248 Label return_undefined(this), fast_elements(this);
249 GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &return_undefined);
250
251 // 2) Ensure that the length is writable.
252 EnsureArrayLengthWritable(context, LoadMap(array_receiver), &runtime);
253
254 // 3) Check that the elements backing store isn't copy-on-write.
255 TNode<FixedArrayBase> elements = LoadElements(array_receiver);
256 GotoIf(TaggedEqual(LoadMap(elements), FixedCOWArrayMapConstant()),
257 &runtime);
258
259 TNode<IntPtrT> new_length = IntPtrSub(length, IntPtrConstant(1));
260
261 // 4) Check that we're not supposed to shrink the backing store, as
262 // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.
263 TNode<IntPtrT> capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
264 GotoIf(IntPtrLessThan(
265 IntPtrAdd(IntPtrAdd(new_length, new_length),
266 IntPtrConstant(JSObject::kMinAddedElementsCapacity)),
267 capacity),
268 &runtime);
269
270 StoreObjectFieldNoWriteBarrier(array_receiver, JSArray::kLengthOffset,
271 SmiTag(new_length));
272
273 TNode<Int32T> elements_kind = LoadElementsKind(array_receiver);
274 GotoIf(Int32LessThanOrEqual(elements_kind,
275 Int32Constant(TERMINAL_FAST_ELEMENTS_KIND)),
276 &fast_elements);
277
278 {
279 TNode<FixedDoubleArray> elements_known_double_array =
280 ReinterpretCast<FixedDoubleArray>(elements);
281 TNode<Float64T> value = LoadFixedDoubleArrayElement(
282 elements_known_double_array, new_length, &return_undefined);
283
284 StoreFixedDoubleArrayHole(elements_known_double_array, new_length);
285 args.PopAndReturn(AllocateHeapNumberWithValue(value));
286 }
287
288 BIND(&fast_elements);
289 {
290 TNode<FixedArray> elements_known_fixed_array = CAST(elements);
291 TNode<Object> value =
292 LoadFixedArrayElement(elements_known_fixed_array, new_length);
293 StoreFixedArrayElement(elements_known_fixed_array, new_length,
294 TheHoleConstant());
295 GotoIf(TaggedEqual(value, TheHoleConstant()), &return_undefined);
296 args.PopAndReturn(value);
297 }
298
299 BIND(&return_undefined);
300 { args.PopAndReturn(UndefinedConstant()); }
301 }
302
303 BIND(&runtime);
304 {
305 // We are not using Parameter(Descriptor::kJSTarget) and loading the value
306 // from the current frame here in order to reduce register pressure on the
307 // fast path.
308 TNode<JSFunction> target = LoadTargetFromFrame();
309 TailCallBuiltin(Builtins::kArrayPop, context, target, UndefinedConstant(),
310 argc);
311 }
312 }
313
TF_BUILTIN(ArrayPrototypePush,CodeStubAssembler)314 TF_BUILTIN(ArrayPrototypePush, CodeStubAssembler) {
315 TVARIABLE(IntPtrT, arg_index);
316 Label default_label(this, &arg_index);
317 Label smi_transition(this);
318 Label object_push_pre(this);
319 Label object_push(this, &arg_index);
320 Label double_push(this, &arg_index);
321 Label double_transition(this);
322 Label runtime(this, Label::kDeferred);
323
324 TNode<Int32T> argc =
325 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
326 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
327 CSA_ASSERT(this, IsUndefined(Parameter(Descriptor::kJSNewTarget)));
328
329 CodeStubArguments args(this, argc);
330 TNode<Object> receiver = args.GetReceiver();
331 TNode<JSArray> array_receiver;
332 TNode<Int32T> kind;
333
334 Label fast(this);
335 BranchIfFastJSArray(receiver, context, &fast, &runtime);
336
337 BIND(&fast);
338 {
339 array_receiver = CAST(receiver);
340 arg_index = IntPtrConstant(0);
341 kind = EnsureArrayPushable(context, LoadMap(array_receiver), &runtime);
342 GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS),
343 &object_push_pre);
344
345 TNode<Smi> new_length =
346 BuildAppendJSArray(PACKED_SMI_ELEMENTS, array_receiver, &args,
347 &arg_index, &smi_transition);
348 args.PopAndReturn(new_length);
349 }
350
351 // If the argument is not a smi, then use a heavyweight SetProperty to
352 // transition the array for only the single next element. If the argument is
353 // a smi, the failure is due to some other reason and we should fall back on
354 // the most generic implementation for the rest of the array.
355 BIND(&smi_transition);
356 {
357 TNode<Object> arg = args.AtIndex(arg_index.value());
358 GotoIf(TaggedIsSmi(arg), &default_label);
359 TNode<Number> length = LoadJSArrayLength(array_receiver);
360 // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
361 // calling into the runtime to do the elements transition is overkill.
362 SetPropertyStrict(context, array_receiver, length, arg);
363 Increment(&arg_index);
364 // The runtime SetProperty call could have converted the array to dictionary
365 // mode, which must be detected to abort the fast-path.
366 TNode<Int32T> kind = LoadElementsKind(array_receiver);
367 GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)),
368 &default_label);
369
370 GotoIfNotNumber(arg, &object_push);
371 Goto(&double_push);
372 }
373
374 BIND(&object_push_pre);
375 {
376 Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), &double_push,
377 &object_push);
378 }
379
380 BIND(&object_push);
381 {
382 TNode<Smi> new_length = BuildAppendJSArray(
383 PACKED_ELEMENTS, array_receiver, &args, &arg_index, &default_label);
384 args.PopAndReturn(new_length);
385 }
386
387 BIND(&double_push);
388 {
389 TNode<Smi> new_length =
390 BuildAppendJSArray(PACKED_DOUBLE_ELEMENTS, array_receiver, &args,
391 &arg_index, &double_transition);
392 args.PopAndReturn(new_length);
393 }
394
395 // If the argument is not a double, then use a heavyweight SetProperty to
396 // transition the array for only the single next element. If the argument is
397 // a double, the failure is due to some other reason and we should fall back
398 // on the most generic implementation for the rest of the array.
399 BIND(&double_transition);
400 {
401 TNode<Object> arg = args.AtIndex(arg_index.value());
402 GotoIfNumber(arg, &default_label);
403 TNode<Number> length = LoadJSArrayLength(array_receiver);
404 // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
405 // calling into the runtime to do the elements transition is overkill.
406 SetPropertyStrict(context, array_receiver, length, arg);
407 Increment(&arg_index);
408 // The runtime SetProperty call could have converted the array to dictionary
409 // mode, which must be detected to abort the fast-path.
410 TNode<Int32T> kind = LoadElementsKind(array_receiver);
411 GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)),
412 &default_label);
413 Goto(&object_push);
414 }
415
416 // Fallback that stores un-processed arguments using the full, heavyweight
417 // SetProperty machinery.
418 BIND(&default_label);
419 {
420 args.ForEach(
421 [=](TNode<Object> arg) {
422 TNode<Number> length = LoadJSArrayLength(array_receiver);
423 SetPropertyStrict(context, array_receiver, length, arg);
424 },
425 arg_index.value());
426 args.PopAndReturn(LoadJSArrayLength(array_receiver));
427 }
428
429 BIND(&runtime);
430 {
431 // We are not using Parameter(Descriptor::kJSTarget) and loading the value
432 // from the current frame here in order to reduce register pressure on the
433 // fast path.
434 TNode<JSFunction> target = LoadTargetFromFrame();
435 TailCallBuiltin(Builtins::kArrayPush, context, target, UndefinedConstant(),
436 argc);
437 }
438 }
439
TF_BUILTIN(ExtractFastJSArray,ArrayBuiltinsAssembler)440 TF_BUILTIN(ExtractFastJSArray, ArrayBuiltinsAssembler) {
441 ParameterMode mode = OptimalParameterMode();
442 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
443 TNode<JSArray> array = CAST(Parameter(Descriptor::kSource));
444 TNode<BInt> begin = SmiToBInt(CAST(Parameter(Descriptor::kBegin)));
445 TNode<BInt> count = SmiToBInt(CAST(Parameter(Descriptor::kCount)));
446
447 CSA_ASSERT(this, Word32BinaryNot(IsNoElementsProtectorCellInvalid()));
448
449 Return(ExtractFastJSArray(context, array, begin, count, mode));
450 }
451
TF_BUILTIN(CloneFastJSArray,ArrayBuiltinsAssembler)452 TF_BUILTIN(CloneFastJSArray, ArrayBuiltinsAssembler) {
453 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
454 TNode<JSArray> array = CAST(Parameter(Descriptor::kSource));
455
456 CSA_ASSERT(this,
457 Word32Or(Word32BinaryNot(IsHoleyFastElementsKindForRead(
458 LoadElementsKind(array))),
459 Word32BinaryNot(IsNoElementsProtectorCellInvalid())));
460
461 Return(CloneFastJSArray(context, array));
462 }
463
464 // This builtin copies the backing store of fast arrays, while converting any
465 // holes to undefined.
466 // - If there are no holes in the source, its ElementsKind will be preserved. In
467 // that case, this builtin should perform as fast as CloneFastJSArray. (In fact,
468 // for fast packed arrays, the behavior is equivalent to CloneFastJSArray.)
469 // - If there are holes in the source, the ElementsKind of the "copy" will be
470 // PACKED_ELEMENTS (such that undefined can be stored).
TF_BUILTIN(CloneFastJSArrayFillingHoles,ArrayBuiltinsAssembler)471 TF_BUILTIN(CloneFastJSArrayFillingHoles, ArrayBuiltinsAssembler) {
472 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
473 TNode<JSArray> array = CAST(Parameter(Descriptor::kSource));
474
475 CSA_ASSERT(this,
476 Word32Or(Word32BinaryNot(IsHoleyFastElementsKindForRead(
477 LoadElementsKind(array))),
478 Word32BinaryNot(IsNoElementsProtectorCellInvalid())));
479
480 Return(CloneFastJSArray(context, array, {},
481 HoleConversionMode::kConvertToUndefined));
482 }
483
484 class ArrayPopulatorAssembler : public CodeStubAssembler {
485 public:
ArrayPopulatorAssembler(compiler::CodeAssemblerState * state)486 explicit ArrayPopulatorAssembler(compiler::CodeAssemblerState* state)
487 : CodeStubAssembler(state) {}
488
ConstructArrayLike(TNode<Context> context,TNode<Object> receiver)489 TNode<Object> ConstructArrayLike(TNode<Context> context,
490 TNode<Object> receiver) {
491 TVARIABLE(Object, array);
492 Label is_constructor(this), is_not_constructor(this), done(this);
493 GotoIf(TaggedIsSmi(receiver), &is_not_constructor);
494 Branch(IsConstructor(CAST(receiver)), &is_constructor, &is_not_constructor);
495
496 BIND(&is_constructor);
497 {
498 array = Construct(context, CAST(receiver));
499 Goto(&done);
500 }
501
502 BIND(&is_not_constructor);
503 {
504 Label allocate_js_array(this);
505
506 TNode<Map> array_map = CAST(LoadContextElement(
507 context, Context::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX));
508
509 TNode<IntPtrT> capacity = IntPtrConstant(0);
510 TNode<Smi> length = SmiConstant(0);
511 array = AllocateJSArray(PACKED_SMI_ELEMENTS, array_map, capacity, length);
512 Goto(&done);
513 }
514
515 BIND(&done);
516 return array.value();
517 }
518
ConstructArrayLike(TNode<Context> context,TNode<Object> receiver,TNode<Number> length)519 TNode<Object> ConstructArrayLike(TNode<Context> context,
520 TNode<Object> receiver,
521 TNode<Number> length) {
522 TVARIABLE(Object, array);
523 Label is_constructor(this), is_not_constructor(this), done(this);
524 CSA_ASSERT(this, IsNumberNormalized(length));
525 GotoIf(TaggedIsSmi(receiver), &is_not_constructor);
526 Branch(IsConstructor(CAST(receiver)), &is_constructor, &is_not_constructor);
527
528 BIND(&is_constructor);
529 {
530 array = Construct(context, CAST(receiver), length);
531 Goto(&done);
532 }
533
534 BIND(&is_not_constructor);
535 {
536 array = ArrayCreate(context, length);
537 Goto(&done);
538 }
539
540 BIND(&done);
541 return array.value();
542 }
543 };
544
TF_BUILTIN(TypedArrayPrototypeMap,ArrayBuiltinsAssembler)545 TF_BUILTIN(TypedArrayPrototypeMap, ArrayBuiltinsAssembler) {
546 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
547 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
548 CodeStubArguments args(this, argc);
549 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
550 TNode<Object> receiver = args.GetReceiver();
551 TNode<Object> callbackfn = args.GetOptionalArgumentValue(0);
552 TNode<Object> this_arg = args.GetOptionalArgumentValue(1);
553
554 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
555
556 GenerateIteratingTypedArrayBuiltinBody(
557 "%TypedArray%.prototype.map",
558 &ArrayBuiltinsAssembler::TypedArrayMapResultGenerator,
559 &ArrayBuiltinsAssembler::TypedArrayMapProcessor);
560 }
561
562 class ArrayIncludesIndexofAssembler : public CodeStubAssembler {
563 public:
ArrayIncludesIndexofAssembler(compiler::CodeAssemblerState * state)564 explicit ArrayIncludesIndexofAssembler(compiler::CodeAssemblerState* state)
565 : CodeStubAssembler(state) {}
566
567 enum SearchVariant { kIncludes, kIndexOf };
568
569 void Generate(SearchVariant variant, TNode<IntPtrT> argc,
570 TNode<Context> context);
571 void GenerateSmiOrObject(SearchVariant variant, TNode<Context> context,
572 TNode<FixedArray> elements,
573 TNode<Object> search_element,
574 TNode<Smi> array_length, TNode<Smi> from_index);
575 void GeneratePackedDoubles(SearchVariant variant,
576 TNode<FixedDoubleArray> elements,
577 TNode<Object> search_element,
578 TNode<Smi> array_length, TNode<Smi> from_index);
579 void GenerateHoleyDoubles(SearchVariant variant,
580 TNode<FixedDoubleArray> elements,
581 TNode<Object> search_element,
582 TNode<Smi> array_length, TNode<Smi> from_index);
583
ReturnIfEmpty(TNode<Smi> length,TNode<Object> value)584 void ReturnIfEmpty(TNode<Smi> length, TNode<Object> value) {
585 Label done(this);
586 GotoIf(SmiGreaterThan(length, SmiConstant(0)), &done);
587 Return(value);
588 BIND(&done);
589 }
590 };
591
Generate(SearchVariant variant,TNode<IntPtrT> argc,TNode<Context> context)592 void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant,
593 TNode<IntPtrT> argc,
594 TNode<Context> context) {
595 const int kSearchElementArg = 0;
596 const int kFromIndexArg = 1;
597
598 CodeStubArguments args(this, argc);
599
600 TNode<Object> receiver = args.GetReceiver();
601 TNode<Object> search_element =
602 args.GetOptionalArgumentValue(kSearchElementArg);
603
604 TNode<IntPtrT> intptr_zero = IntPtrConstant(0);
605
606 Label init_index(this), return_not_found(this), call_runtime(this);
607
608 // Take slow path if not a JSArray, if retrieving elements requires
609 // traversing prototype, or if access checks are required.
610 BranchIfFastJSArrayForRead(receiver, context, &init_index, &call_runtime);
611
612 BIND(&init_index);
613 TVARIABLE(IntPtrT, index_var, intptr_zero);
614 TNode<JSArray> array = CAST(receiver);
615
616 // JSArray length is always a positive Smi for fast arrays.
617 CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array)));
618 TNode<Smi> array_length = LoadFastJSArrayLength(array);
619 TNode<IntPtrT> array_length_untagged = SmiUntag(array_length);
620
621 {
622 // Initialize fromIndex.
623 Label is_smi(this), is_nonsmi(this), done(this);
624
625 // If no fromIndex was passed, default to 0.
626 GotoIf(IntPtrLessThanOrEqual(argc, IntPtrConstant(kFromIndexArg)), &done);
627
628 TNode<Object> start_from = args.AtIndex(kFromIndexArg);
629 // Handle Smis and undefined here and everything else in runtime.
630 // We must be very careful with side effects from the ToInteger conversion,
631 // as the side effects might render previously checked assumptions about
632 // the receiver being a fast JSArray and its length invalid.
633 Branch(TaggedIsSmi(start_from), &is_smi, &is_nonsmi);
634
635 BIND(&is_nonsmi);
636 {
637 GotoIfNot(IsUndefined(start_from), &call_runtime);
638 Goto(&done);
639 }
640 BIND(&is_smi);
641 {
642 TNode<IntPtrT> intptr_start_from = SmiUntag(CAST(start_from));
643 index_var = intptr_start_from;
644
645 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done);
646 // The fromIndex is negative: add it to the array's length.
647 index_var = IntPtrAdd(array_length_untagged, index_var.value());
648 // Clamp negative results at zero.
649 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done);
650 index_var = intptr_zero;
651 Goto(&done);
652 }
653 BIND(&done);
654 }
655
656 // Fail early if startIndex >= array.length.
657 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length_untagged),
658 &return_not_found);
659
660 Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this);
661
662 TNode<Int32T> elements_kind = LoadElementsKind(array);
663 TNode<FixedArrayBase> elements = LoadElements(array);
664 STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
665 STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
666 STATIC_ASSERT(PACKED_ELEMENTS == 2);
667 STATIC_ASSERT(HOLEY_ELEMENTS == 3);
668 GotoIf(IsElementsKindLessThanOrEqual(elements_kind, HOLEY_ELEMENTS),
669 &if_smiorobjects);
670 GotoIf(
671 ElementsKindEqual(elements_kind, Int32Constant(PACKED_DOUBLE_ELEMENTS)),
672 &if_packed_doubles);
673 GotoIf(ElementsKindEqual(elements_kind, Int32Constant(HOLEY_DOUBLE_ELEMENTS)),
674 &if_holey_doubles);
675 GotoIf(IsElementsKindLessThanOrEqual(elements_kind,
676 LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND),
677 &if_smiorobjects);
678 Goto(&return_not_found);
679
680 BIND(&if_smiorobjects);
681 {
682 Callable callable =
683 (variant == kIncludes)
684 ? Builtins::CallableFor(isolate(),
685 Builtins::kArrayIncludesSmiOrObject)
686 : Builtins::CallableFor(isolate(),
687 Builtins::kArrayIndexOfSmiOrObject);
688 TNode<Object> result = CallStub(callable, context, elements, search_element,
689 array_length, SmiTag(index_var.value()));
690 args.PopAndReturn(result);
691 }
692
693 BIND(&if_packed_doubles);
694 {
695 Callable callable =
696 (variant == kIncludes)
697 ? Builtins::CallableFor(isolate(),
698 Builtins::kArrayIncludesPackedDoubles)
699 : Builtins::CallableFor(isolate(),
700 Builtins::kArrayIndexOfPackedDoubles);
701 TNode<Object> result = CallStub(callable, context, elements, search_element,
702 array_length, SmiTag(index_var.value()));
703 args.PopAndReturn(result);
704 }
705
706 BIND(&if_holey_doubles);
707 {
708 Callable callable =
709 (variant == kIncludes)
710 ? Builtins::CallableFor(isolate(),
711 Builtins::kArrayIncludesHoleyDoubles)
712 : Builtins::CallableFor(isolate(),
713 Builtins::kArrayIndexOfHoleyDoubles);
714 TNode<Object> result = CallStub(callable, context, elements, search_element,
715 array_length, SmiTag(index_var.value()));
716 args.PopAndReturn(result);
717 }
718
719 BIND(&return_not_found);
720 if (variant == kIncludes) {
721 args.PopAndReturn(FalseConstant());
722 } else {
723 args.PopAndReturn(NumberConstant(-1));
724 }
725
726 BIND(&call_runtime);
727 {
728 TNode<Object> start_from = args.GetOptionalArgumentValue(kFromIndexArg);
729 Runtime::FunctionId function = variant == kIncludes
730 ? Runtime::kArrayIncludes_Slow
731 : Runtime::kArrayIndexOf;
732 args.PopAndReturn(
733 CallRuntime(function, context, array, search_element, start_from));
734 }
735 }
736
GenerateSmiOrObject(SearchVariant variant,TNode<Context> context,TNode<FixedArray> elements,TNode<Object> search_element,TNode<Smi> array_length,TNode<Smi> from_index)737 void ArrayIncludesIndexofAssembler::GenerateSmiOrObject(
738 SearchVariant variant, TNode<Context> context, TNode<FixedArray> elements,
739 TNode<Object> search_element, TNode<Smi> array_length,
740 TNode<Smi> from_index) {
741 TVARIABLE(IntPtrT, index_var, SmiUntag(from_index));
742 TVARIABLE(Float64T, search_num);
743 TNode<IntPtrT> array_length_untagged = SmiUntag(array_length);
744
745 Label ident_loop(this, &index_var), heap_num_loop(this, &search_num),
746 string_loop(this), bigint_loop(this, &index_var),
747 undef_loop(this, &index_var), not_smi(this), not_heap_num(this),
748 return_found(this), return_not_found(this);
749
750 GotoIfNot(TaggedIsSmi(search_element), ¬_smi);
751 search_num = SmiToFloat64(CAST(search_element));
752 Goto(&heap_num_loop);
753
754 BIND(¬_smi);
755 if (variant == kIncludes) {
756 GotoIf(IsUndefined(search_element), &undef_loop);
757 }
758 TNode<Map> map = LoadMap(CAST(search_element));
759 GotoIfNot(IsHeapNumberMap(map), ¬_heap_num);
760 search_num = LoadHeapNumberValue(CAST(search_element));
761 Goto(&heap_num_loop);
762
763 BIND(¬_heap_num);
764 TNode<Uint16T> search_type = LoadMapInstanceType(map);
765 GotoIf(IsStringInstanceType(search_type), &string_loop);
766 GotoIf(IsBigIntInstanceType(search_type), &bigint_loop);
767 Goto(&ident_loop);
768
769 BIND(&ident_loop);
770 {
771 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
772 &return_not_found);
773 TNode<Object> element_k =
774 UnsafeLoadFixedArrayElement(elements, index_var.value());
775 GotoIf(TaggedEqual(element_k, search_element), &return_found);
776
777 Increment(&index_var);
778 Goto(&ident_loop);
779 }
780
781 if (variant == kIncludes) {
782 BIND(&undef_loop);
783
784 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
785 &return_not_found);
786 TNode<Object> element_k =
787 UnsafeLoadFixedArrayElement(elements, index_var.value());
788 GotoIf(IsUndefined(element_k), &return_found);
789 GotoIf(IsTheHole(element_k), &return_found);
790
791 Increment(&index_var);
792 Goto(&undef_loop);
793 }
794
795 BIND(&heap_num_loop);
796 {
797 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var);
798 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
799 BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop);
800
801 BIND(¬_nan_loop);
802 {
803 Label continue_loop(this), not_smi(this);
804 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
805 &return_not_found);
806 TNode<Object> element_k =
807 UnsafeLoadFixedArrayElement(elements, index_var.value());
808 GotoIfNot(TaggedIsSmi(element_k), ¬_smi);
809 Branch(Float64Equal(search_num.value(), SmiToFloat64(CAST(element_k))),
810 &return_found, &continue_loop);
811
812 BIND(¬_smi);
813 GotoIfNot(IsHeapNumber(CAST(element_k)), &continue_loop);
814 Branch(Float64Equal(search_num.value(),
815 LoadHeapNumberValue(CAST(element_k))),
816 &return_found, &continue_loop);
817
818 BIND(&continue_loop);
819 Increment(&index_var);
820 Goto(¬_nan_loop);
821 }
822
823 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
824 if (variant == kIncludes) {
825 BIND(&nan_loop);
826 Label continue_loop(this);
827 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
828 &return_not_found);
829 TNode<Object> element_k =
830 UnsafeLoadFixedArrayElement(elements, index_var.value());
831 GotoIf(TaggedIsSmi(element_k), &continue_loop);
832 GotoIfNot(IsHeapNumber(CAST(element_k)), &continue_loop);
833 BranchIfFloat64IsNaN(LoadHeapNumberValue(CAST(element_k)), &return_found,
834 &continue_loop);
835
836 BIND(&continue_loop);
837 Increment(&index_var);
838 Goto(&nan_loop);
839 }
840 }
841
842 BIND(&string_loop);
843 {
844 TNode<String> search_element_string = CAST(search_element);
845 Label continue_loop(this), next_iteration(this, &index_var),
846 slow_compare(this), runtime(this, Label::kDeferred);
847 TNode<IntPtrT> search_length =
848 LoadStringLengthAsWord(search_element_string);
849 Goto(&next_iteration);
850 BIND(&next_iteration);
851 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
852 &return_not_found);
853 TNode<Object> element_k =
854 UnsafeLoadFixedArrayElement(elements, index_var.value());
855 GotoIf(TaggedIsSmi(element_k), &continue_loop);
856 GotoIf(TaggedEqual(search_element_string, element_k), &return_found);
857 TNode<Uint16T> element_k_type = LoadInstanceType(CAST(element_k));
858 GotoIfNot(IsStringInstanceType(element_k_type), &continue_loop);
859 Branch(IntPtrEqual(search_length, LoadStringLengthAsWord(CAST(element_k))),
860 &slow_compare, &continue_loop);
861
862 BIND(&slow_compare);
863 StringBuiltinsAssembler string_asm(state());
864 string_asm.StringEqual_Core(search_element_string, search_type,
865 CAST(element_k), element_k_type, search_length,
866 &return_found, &continue_loop, &runtime);
867 BIND(&runtime);
868 TNode<Object> result = CallRuntime(Runtime::kStringEqual, context,
869 search_element_string, element_k);
870 Branch(TaggedEqual(result, TrueConstant()), &return_found, &continue_loop);
871
872 BIND(&continue_loop);
873 Increment(&index_var);
874 Goto(&next_iteration);
875 }
876
877 BIND(&bigint_loop);
878 {
879 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
880 &return_not_found);
881
882 TNode<Object> element_k =
883 UnsafeLoadFixedArrayElement(elements, index_var.value());
884 Label continue_loop(this);
885 GotoIf(TaggedIsSmi(element_k), &continue_loop);
886 GotoIfNot(IsBigInt(CAST(element_k)), &continue_loop);
887 TNode<Object> result = CallRuntime(Runtime::kBigIntEqualToBigInt, context,
888 search_element, element_k);
889 Branch(TaggedEqual(result, TrueConstant()), &return_found, &continue_loop);
890
891 BIND(&continue_loop);
892 Increment(&index_var);
893 Goto(&bigint_loop);
894 }
895 BIND(&return_found);
896 if (variant == kIncludes) {
897 Return(TrueConstant());
898 } else {
899 Return(SmiTag(index_var.value()));
900 }
901
902 BIND(&return_not_found);
903 if (variant == kIncludes) {
904 Return(FalseConstant());
905 } else {
906 Return(NumberConstant(-1));
907 }
908 }
909
GeneratePackedDoubles(SearchVariant variant,TNode<FixedDoubleArray> elements,TNode<Object> search_element,TNode<Smi> array_length,TNode<Smi> from_index)910 void ArrayIncludesIndexofAssembler::GeneratePackedDoubles(
911 SearchVariant variant, TNode<FixedDoubleArray> elements,
912 TNode<Object> search_element, TNode<Smi> array_length,
913 TNode<Smi> from_index) {
914 TVARIABLE(IntPtrT, index_var, SmiUntag(from_index));
915 TNode<IntPtrT> array_length_untagged = SmiUntag(array_length);
916
917 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var),
918 hole_loop(this, &index_var), search_notnan(this), return_found(this),
919 return_not_found(this);
920 TVARIABLE(Float64T, search_num);
921 search_num = Float64Constant(0);
922
923 GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
924 search_num = SmiToFloat64(CAST(search_element));
925 Goto(¬_nan_loop);
926
927 BIND(&search_notnan);
928 GotoIfNot(IsHeapNumber(CAST(search_element)), &return_not_found);
929
930 search_num = LoadHeapNumberValue(CAST(search_element));
931
932 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
933 BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop);
934
935 BIND(¬_nan_loop);
936 {
937 Label continue_loop(this);
938 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
939 &return_not_found);
940 TNode<Float64T> element_k = LoadFixedDoubleArrayElement(
941 elements, index_var.value(), MachineType::Float64());
942 Branch(Float64Equal(element_k, search_num.value()), &return_found,
943 &continue_loop);
944 BIND(&continue_loop);
945 Increment(&index_var);
946 Goto(¬_nan_loop);
947 }
948
949 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
950 if (variant == kIncludes) {
951 BIND(&nan_loop);
952 Label continue_loop(this);
953 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
954 &return_not_found);
955 TNode<Float64T> element_k = LoadFixedDoubleArrayElement(
956 elements, index_var.value(), MachineType::Float64());
957 BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop);
958 BIND(&continue_loop);
959 Increment(&index_var);
960 Goto(&nan_loop);
961 }
962
963 BIND(&return_found);
964 if (variant == kIncludes) {
965 Return(TrueConstant());
966 } else {
967 Return(SmiTag(index_var.value()));
968 }
969
970 BIND(&return_not_found);
971 if (variant == kIncludes) {
972 Return(FalseConstant());
973 } else {
974 Return(NumberConstant(-1));
975 }
976 }
977
GenerateHoleyDoubles(SearchVariant variant,TNode<FixedDoubleArray> elements,TNode<Object> search_element,TNode<Smi> array_length,TNode<Smi> from_index)978 void ArrayIncludesIndexofAssembler::GenerateHoleyDoubles(
979 SearchVariant variant, TNode<FixedDoubleArray> elements,
980 TNode<Object> search_element, TNode<Smi> array_length,
981 TNode<Smi> from_index) {
982 TVARIABLE(IntPtrT, index_var, SmiUntag(from_index));
983 TNode<IntPtrT> array_length_untagged = SmiUntag(array_length);
984
985 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var),
986 hole_loop(this, &index_var), search_notnan(this), return_found(this),
987 return_not_found(this);
988 TVARIABLE(Float64T, search_num);
989 search_num = Float64Constant(0);
990
991 GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
992 search_num = SmiToFloat64(CAST(search_element));
993 Goto(¬_nan_loop);
994
995 BIND(&search_notnan);
996 if (variant == kIncludes) {
997 GotoIf(IsUndefined(search_element), &hole_loop);
998 }
999 GotoIfNot(IsHeapNumber(CAST(search_element)), &return_not_found);
1000
1001 search_num = LoadHeapNumberValue(CAST(search_element));
1002
1003 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
1004 BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop);
1005
1006 BIND(¬_nan_loop);
1007 {
1008 Label continue_loop(this);
1009 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1010 &return_not_found);
1011
1012 // No need for hole checking here; the following Float64Equal will
1013 // return 'not equal' for holes anyway.
1014 TNode<Float64T> element_k = LoadFixedDoubleArrayElement(
1015 elements, index_var.value(), MachineType::Float64());
1016
1017 Branch(Float64Equal(element_k, search_num.value()), &return_found,
1018 &continue_loop);
1019 BIND(&continue_loop);
1020 Increment(&index_var);
1021 Goto(¬_nan_loop);
1022 }
1023
1024 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
1025 if (variant == kIncludes) {
1026 BIND(&nan_loop);
1027 Label continue_loop(this);
1028 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1029 &return_not_found);
1030
1031 // Load double value or continue if it's the hole NaN.
1032 TNode<Float64T> element_k = LoadFixedDoubleArrayElement(
1033 elements, index_var.value(), MachineType::Float64(), 0,
1034 INTPTR_PARAMETERS, &continue_loop);
1035
1036 BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop);
1037 BIND(&continue_loop);
1038 Increment(&index_var);
1039 Goto(&nan_loop);
1040 }
1041
1042 // Array.p.includes treats the hole as undefined.
1043 if (variant == kIncludes) {
1044 BIND(&hole_loop);
1045 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
1046 &return_not_found);
1047
1048 // Check if the element is a double hole, but don't load it.
1049 LoadFixedDoubleArrayElement(elements, index_var.value(),
1050 MachineType::None(), 0, INTPTR_PARAMETERS,
1051 &return_found);
1052
1053 Increment(&index_var);
1054 Goto(&hole_loop);
1055 }
1056
1057 BIND(&return_found);
1058 if (variant == kIncludes) {
1059 Return(TrueConstant());
1060 } else {
1061 Return(SmiTag(index_var.value()));
1062 }
1063
1064 BIND(&return_not_found);
1065 if (variant == kIncludes) {
1066 Return(FalseConstant());
1067 } else {
1068 Return(NumberConstant(-1));
1069 }
1070 }
1071
TF_BUILTIN(ArrayIncludes,ArrayIncludesIndexofAssembler)1072 TF_BUILTIN(ArrayIncludes, ArrayIncludesIndexofAssembler) {
1073 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
1074 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
1075 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1076
1077 Generate(kIncludes, argc, context);
1078 }
1079
TF_BUILTIN(ArrayIncludesSmiOrObject,ArrayIncludesIndexofAssembler)1080 TF_BUILTIN(ArrayIncludesSmiOrObject, ArrayIncludesIndexofAssembler) {
1081 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1082 TNode<FixedArray> elements = CAST(Parameter(Descriptor::kElements));
1083 TNode<Object> search_element = CAST(Parameter(Descriptor::kSearchElement));
1084 TNode<Smi> array_length = CAST(Parameter(Descriptor::kLength));
1085 TNode<Smi> from_index = CAST(Parameter(Descriptor::kFromIndex));
1086
1087 GenerateSmiOrObject(kIncludes, context, elements, search_element,
1088 array_length, from_index);
1089 }
1090
TF_BUILTIN(ArrayIncludesPackedDoubles,ArrayIncludesIndexofAssembler)1091 TF_BUILTIN(ArrayIncludesPackedDoubles, ArrayIncludesIndexofAssembler) {
1092 TNode<FixedArrayBase> elements = CAST(Parameter(Descriptor::kElements));
1093 TNode<Object> search_element = CAST(Parameter(Descriptor::kSearchElement));
1094 TNode<Smi> array_length = CAST(Parameter(Descriptor::kLength));
1095 TNode<Smi> from_index = CAST(Parameter(Descriptor::kFromIndex));
1096
1097 ReturnIfEmpty(array_length, FalseConstant());
1098 GeneratePackedDoubles(kIncludes, CAST(elements), search_element, array_length,
1099 from_index);
1100 }
1101
TF_BUILTIN(ArrayIncludesHoleyDoubles,ArrayIncludesIndexofAssembler)1102 TF_BUILTIN(ArrayIncludesHoleyDoubles, ArrayIncludesIndexofAssembler) {
1103 TNode<FixedArrayBase> elements = CAST(Parameter(Descriptor::kElements));
1104 TNode<Object> search_element = CAST(Parameter(Descriptor::kSearchElement));
1105 TNode<Smi> array_length = CAST(Parameter(Descriptor::kLength));
1106 TNode<Smi> from_index = CAST(Parameter(Descriptor::kFromIndex));
1107
1108 ReturnIfEmpty(array_length, FalseConstant());
1109 GenerateHoleyDoubles(kIncludes, CAST(elements), search_element, array_length,
1110 from_index);
1111 }
1112
TF_BUILTIN(ArrayIndexOf,ArrayIncludesIndexofAssembler)1113 TF_BUILTIN(ArrayIndexOf, ArrayIncludesIndexofAssembler) {
1114 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
1115 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
1116 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1117
1118 Generate(kIndexOf, argc, context);
1119 }
1120
TF_BUILTIN(ArrayIndexOfSmiOrObject,ArrayIncludesIndexofAssembler)1121 TF_BUILTIN(ArrayIndexOfSmiOrObject, ArrayIncludesIndexofAssembler) {
1122 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1123 TNode<FixedArray> elements = CAST(Parameter(Descriptor::kElements));
1124 TNode<Object> search_element = CAST(Parameter(Descriptor::kSearchElement));
1125 TNode<Smi> array_length = CAST(Parameter(Descriptor::kLength));
1126 TNode<Smi> from_index = CAST(Parameter(Descriptor::kFromIndex));
1127
1128 GenerateSmiOrObject(kIndexOf, context, elements, search_element, array_length,
1129 from_index);
1130 }
1131
TF_BUILTIN(ArrayIndexOfPackedDoubles,ArrayIncludesIndexofAssembler)1132 TF_BUILTIN(ArrayIndexOfPackedDoubles, ArrayIncludesIndexofAssembler) {
1133 TNode<FixedArrayBase> elements = CAST(Parameter(Descriptor::kElements));
1134 TNode<Object> search_element = CAST(Parameter(Descriptor::kSearchElement));
1135 TNode<Smi> array_length = CAST(Parameter(Descriptor::kLength));
1136 TNode<Smi> from_index = CAST(Parameter(Descriptor::kFromIndex));
1137
1138 ReturnIfEmpty(array_length, NumberConstant(-1));
1139 GeneratePackedDoubles(kIndexOf, CAST(elements), search_element, array_length,
1140 from_index);
1141 }
1142
TF_BUILTIN(ArrayIndexOfHoleyDoubles,ArrayIncludesIndexofAssembler)1143 TF_BUILTIN(ArrayIndexOfHoleyDoubles, ArrayIncludesIndexofAssembler) {
1144 TNode<FixedArrayBase> elements = CAST(Parameter(Descriptor::kElements));
1145 TNode<Object> search_element = CAST(Parameter(Descriptor::kSearchElement));
1146 TNode<Smi> array_length = CAST(Parameter(Descriptor::kLength));
1147 TNode<Smi> from_index = CAST(Parameter(Descriptor::kFromIndex));
1148
1149 ReturnIfEmpty(array_length, NumberConstant(-1));
1150 GenerateHoleyDoubles(kIndexOf, CAST(elements), search_element, array_length,
1151 from_index);
1152 }
1153
1154 // ES #sec-array.prototype.values
TF_BUILTIN(ArrayPrototypeValues,CodeStubAssembler)1155 TF_BUILTIN(ArrayPrototypeValues, CodeStubAssembler) {
1156 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1157 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1158 Return(CreateArrayIterator(context, ToObject_Inline(context, receiver),
1159 IterationKind::kValues));
1160 }
1161
1162 // ES #sec-array.prototype.entries
TF_BUILTIN(ArrayPrototypeEntries,CodeStubAssembler)1163 TF_BUILTIN(ArrayPrototypeEntries, CodeStubAssembler) {
1164 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1165 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1166 Return(CreateArrayIterator(context, ToObject_Inline(context, receiver),
1167 IterationKind::kEntries));
1168 }
1169
1170 // ES #sec-array.prototype.keys
TF_BUILTIN(ArrayPrototypeKeys,CodeStubAssembler)1171 TF_BUILTIN(ArrayPrototypeKeys, CodeStubAssembler) {
1172 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1173 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1174 Return(CreateArrayIterator(context, ToObject_Inline(context, receiver),
1175 IterationKind::kKeys));
1176 }
1177
1178 // ES #sec-%arrayiteratorprototype%.next
TF_BUILTIN(ArrayIteratorPrototypeNext,CodeStubAssembler)1179 TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) {
1180 const char* method_name = "Array Iterator.prototype.next";
1181
1182 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1183 TNode<Object> maybe_iterator = CAST(Parameter(Descriptor::kReceiver));
1184
1185 TVARIABLE(Oddball, var_done, TrueConstant());
1186 TVARIABLE(Object, var_value, UndefinedConstant());
1187
1188 Label allocate_entry_if_needed(this);
1189 Label allocate_iterator_result(this);
1190 Label if_typedarray(this), if_other(this, Label::kDeferred), if_array(this),
1191 if_generic(this, Label::kDeferred);
1192 Label set_done(this, Label::kDeferred);
1193
1194 // If O does not have all of the internal slots of an Array Iterator Instance
1195 // (22.1.5.3), throw a TypeError exception
1196 ThrowIfNotInstanceType(context, maybe_iterator, JS_ARRAY_ITERATOR_TYPE,
1197 method_name);
1198
1199 TNode<JSArrayIterator> iterator = CAST(maybe_iterator);
1200
1201 // Let a be O.[[IteratedObject]].
1202 TNode<JSReceiver> array = LoadJSArrayIteratorIteratedObject(iterator);
1203
1204 // Let index be O.[[ArrayIteratorNextIndex]].
1205 TNode<Number> index = LoadJSArrayIteratorNextIndex(iterator);
1206 CSA_ASSERT(this, IsNumberNonNegativeSafeInteger(index));
1207
1208 // Dispatch based on the type of the {array}.
1209 TNode<Map> array_map = LoadMap(array);
1210 TNode<Uint16T> array_type = LoadMapInstanceType(array_map);
1211 GotoIf(InstanceTypeEqual(array_type, JS_ARRAY_TYPE), &if_array);
1212 Branch(InstanceTypeEqual(array_type, JS_TYPED_ARRAY_TYPE), &if_typedarray,
1213 &if_other);
1214
1215 BIND(&if_array);
1216 {
1217 // If {array} is a JSArray, then the {index} must be in Unsigned32 range.
1218 CSA_ASSERT(this, IsNumberArrayIndex(index));
1219
1220 // Check that the {index} is within range for the {array}. We handle all
1221 // kinds of JSArray's here, so we do the computation on Uint32.
1222 TNode<Uint32T> index32 = ChangeNumberToUint32(index);
1223 TNode<Uint32T> length32 =
1224 ChangeNumberToUint32(LoadJSArrayLength(CAST(array)));
1225 GotoIfNot(Uint32LessThan(index32, length32), &set_done);
1226 StoreJSArrayIteratorNextIndex(
1227 iterator, ChangeUint32ToTagged(Uint32Add(index32, Uint32Constant(1))));
1228
1229 var_done = FalseConstant();
1230 var_value = index;
1231
1232 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
1233 iterator, JSArrayIterator::kKindOffset),
1234 Int32Constant(static_cast<int>(IterationKind::kKeys))),
1235 &allocate_iterator_result);
1236
1237 Label if_hole(this, Label::kDeferred);
1238 TNode<Int32T> elements_kind = LoadMapElementsKind(array_map);
1239 TNode<FixedArrayBase> elements = LoadElements(CAST(array));
1240 GotoIfForceSlowPath(&if_generic);
1241 var_value = LoadFixedArrayBaseElementAsTagged(
1242 elements, Signed(ChangeUint32ToWord(index32)), elements_kind,
1243 &if_generic, &if_hole);
1244 Goto(&allocate_entry_if_needed);
1245
1246 BIND(&if_hole);
1247 {
1248 GotoIf(IsNoElementsProtectorCellInvalid(), &if_generic);
1249 GotoIfNot(IsPrototypeInitialArrayPrototype(context, array_map),
1250 &if_generic);
1251 var_value = UndefinedConstant();
1252 Goto(&allocate_entry_if_needed);
1253 }
1254 }
1255
1256 BIND(&if_other);
1257 {
1258 // We cannot enter here with either JSArray's or JSTypedArray's.
1259 CSA_ASSERT(this, Word32BinaryNot(IsJSArray(array)));
1260 CSA_ASSERT(this, Word32BinaryNot(IsJSTypedArray(array)));
1261
1262 // Check that the {index} is within the bounds of the {array}s "length".
1263 TNode<Number> length = CAST(
1264 CallBuiltin(Builtins::kToLength, context,
1265 GetProperty(context, array, factory()->length_string())));
1266 GotoIfNumberGreaterThanOrEqual(index, length, &set_done);
1267 StoreJSArrayIteratorNextIndex(iterator, NumberInc(index));
1268
1269 var_done = FalseConstant();
1270 var_value = index;
1271
1272 Branch(Word32Equal(LoadAndUntagToWord32ObjectField(
1273 iterator, JSArrayIterator::kKindOffset),
1274 Int32Constant(static_cast<int>(IterationKind::kKeys))),
1275 &allocate_iterator_result, &if_generic);
1276 }
1277
1278 BIND(&set_done);
1279 {
1280 // Change the [[ArrayIteratorNextIndex]] such that the {iterator} will
1281 // never produce values anymore, because it will always fail the bounds
1282 // check. Note that this is different from what the specification does,
1283 // which is changing the [[IteratedObject]] to undefined, because leaving
1284 // [[IteratedObject]] alone helps TurboFan to generate better code with
1285 // the inlining in JSCallReducer::ReduceArrayIteratorPrototypeNext().
1286 //
1287 // The terminal value we chose here depends on the type of the {array},
1288 // for JSArray's we use kMaxUInt32 so that TurboFan can always use
1289 // Word32 representation for fast-path indices (and this is safe since
1290 // the "length" of JSArray's is limited to Unsigned32 range). For other
1291 // JSReceiver's we have to use kMaxSafeInteger, since the "length" can
1292 // be any arbitrary value in the safe integer range.
1293 //
1294 // Note specifically that JSTypedArray's will never take this path, so
1295 // we don't need to worry about their maximum value.
1296 CSA_ASSERT(this, Word32BinaryNot(IsJSTypedArray(array)));
1297 TNode<Number> max_length =
1298 SelectConstant(IsJSArray(array), NumberConstant(kMaxUInt32),
1299 NumberConstant(kMaxSafeInteger));
1300 StoreJSArrayIteratorNextIndex(iterator, max_length);
1301 Goto(&allocate_iterator_result);
1302 }
1303
1304 BIND(&if_generic);
1305 {
1306 var_value = GetProperty(context, array, index);
1307 Goto(&allocate_entry_if_needed);
1308 }
1309
1310 BIND(&if_typedarray);
1311 {
1312 // Overflowing uintptr range also means end of iteration.
1313 TNode<UintPtrT> index_uintptr =
1314 ChangeSafeIntegerNumberToUintPtr(index, &allocate_iterator_result);
1315
1316 // Check that the {array}s buffer wasn't detached.
1317 ThrowIfArrayBufferViewBufferIsDetached(context, CAST(array), method_name);
1318
1319 // If we go outside of the {length}, we don't need to update the
1320 // [[ArrayIteratorNextIndex]] anymore, since a JSTypedArray's
1321 // length cannot change anymore, so this {iterator} will never
1322 // produce values again anyways.
1323 TNode<UintPtrT> length = LoadJSTypedArrayLength(CAST(array));
1324 GotoIfNot(UintPtrLessThan(index_uintptr, length),
1325 &allocate_iterator_result);
1326 // TODO(v8:4153): Consider storing next index as uintptr. Update this and
1327 // the relevant TurboFan code.
1328 StoreJSArrayIteratorNextIndex(
1329 iterator,
1330 ChangeUintPtrToTagged(UintPtrAdd(index_uintptr, UintPtrConstant(1))));
1331
1332 var_done = FalseConstant();
1333 var_value = index;
1334
1335 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
1336 iterator, JSArrayIterator::kKindOffset),
1337 Int32Constant(static_cast<int>(IterationKind::kKeys))),
1338 &allocate_iterator_result);
1339
1340 TNode<Int32T> elements_kind = LoadMapElementsKind(array_map);
1341 TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(CAST(array));
1342 var_value = LoadFixedTypedArrayElementAsTagged(data_ptr, index_uintptr,
1343 elements_kind);
1344 Goto(&allocate_entry_if_needed);
1345 }
1346
1347 BIND(&allocate_entry_if_needed);
1348 {
1349 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
1350 iterator, JSArrayIterator::kKindOffset),
1351 Int32Constant(static_cast<int>(IterationKind::kValues))),
1352 &allocate_iterator_result);
1353
1354 TNode<JSObject> result =
1355 AllocateJSIteratorResultForEntry(context, index, var_value.value());
1356 Return(result);
1357 }
1358
1359 BIND(&allocate_iterator_result);
1360 {
1361 TNode<JSObject> result =
1362 AllocateJSIteratorResult(context, var_value.value(), var_done.value());
1363 Return(result);
1364 }
1365 }
1366
1367 class ArrayFlattenAssembler : public CodeStubAssembler {
1368 public:
ArrayFlattenAssembler(compiler::CodeAssemblerState * state)1369 explicit ArrayFlattenAssembler(compiler::CodeAssemblerState* state)
1370 : CodeStubAssembler(state) {}
1371
1372 // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
FlattenIntoArray(TNode<Context> context,TNode<JSReceiver> target,TNode<JSReceiver> source,TNode<Number> source_length,TNode<Number> start,TNode<Number> depth,base::Optional<TNode<HeapObject>> mapper_function=base::nullopt,base::Optional<TNode<Object>> this_arg=base::nullopt)1373 TNode<Number> FlattenIntoArray(
1374 TNode<Context> context, TNode<JSReceiver> target,
1375 TNode<JSReceiver> source, TNode<Number> source_length,
1376 TNode<Number> start, TNode<Number> depth,
1377 base::Optional<TNode<HeapObject>> mapper_function = base::nullopt,
1378 base::Optional<TNode<Object>> this_arg = base::nullopt) {
1379 CSA_ASSERT(this, IsNumberPositive(source_length));
1380 CSA_ASSERT(this, IsNumberPositive(start));
1381
1382 // 1. Let targetIndex be start.
1383 TVARIABLE(Number, var_target_index, start);
1384
1385 // 2. Let sourceIndex be 0.
1386 TVARIABLE(Number, var_source_index, SmiConstant(0));
1387
1388 // 3. Repeat...
1389 Label loop(this, {&var_target_index, &var_source_index}), done_loop(this);
1390 Goto(&loop);
1391 BIND(&loop);
1392 {
1393 TNode<Number> source_index = var_source_index.value();
1394 TNode<Number> target_index = var_target_index.value();
1395
1396 // ...while sourceIndex < sourceLen
1397 GotoIfNumberGreaterThanOrEqual(source_index, source_length, &done_loop);
1398
1399 // a. Let P be ! ToString(sourceIndex).
1400 // b. Let exists be ? HasProperty(source, P).
1401 CSA_ASSERT(this,
1402 SmiGreaterThanOrEqual(CAST(source_index), SmiConstant(0)));
1403 const TNode<Oddball> exists =
1404 HasProperty(context, source, source_index, kHasProperty);
1405
1406 // c. If exists is true, then
1407 Label next(this);
1408 GotoIfNot(IsTrue(exists), &next);
1409 {
1410 // i. Let element be ? Get(source, P).
1411 TNode<Object> element_maybe_smi =
1412 GetProperty(context, source, source_index);
1413
1414 // ii. If mapperFunction is present, then
1415 if (mapper_function) {
1416 CSA_ASSERT(this, Word32Or(IsUndefined(mapper_function.value()),
1417 IsCallable(mapper_function.value())));
1418 DCHECK(this_arg.has_value());
1419
1420 // 1. Set element to ? Call(mapperFunction, thisArg , « element,
1421 // sourceIndex, source »).
1422 element_maybe_smi =
1423 Call(context, mapper_function.value(), this_arg.value(),
1424 element_maybe_smi, source_index, source);
1425 }
1426
1427 // iii. Let shouldFlatten be false.
1428 Label if_flatten_array(this), if_flatten_proxy(this, Label::kDeferred),
1429 if_noflatten(this);
1430 // iv. If depth > 0, then
1431 GotoIfNumberGreaterThanOrEqual(SmiConstant(0), depth, &if_noflatten);
1432 // 1. Set shouldFlatten to ? IsArray(element).
1433 GotoIf(TaggedIsSmi(element_maybe_smi), &if_noflatten);
1434 TNode<HeapObject> element = CAST(element_maybe_smi);
1435 GotoIf(IsJSArray(element), &if_flatten_array);
1436 GotoIfNot(IsJSProxy(element), &if_noflatten);
1437 Branch(IsTrue(CallRuntime(Runtime::kArrayIsArray, context, element)),
1438 &if_flatten_proxy, &if_noflatten);
1439
1440 BIND(&if_flatten_array);
1441 {
1442 CSA_ASSERT(this, IsJSArray(element));
1443
1444 // 1. Let elementLen be ? ToLength(? Get(element, "length")).
1445 const TNode<Object> element_length =
1446 LoadObjectField(element, JSArray::kLengthOffset);
1447
1448 // 2. Set targetIndex to ? FlattenIntoArray(target, element,
1449 // elementLen, targetIndex,
1450 // depth - 1).
1451 var_target_index = CAST(
1452 CallBuiltin(Builtins::kFlattenIntoArray, context, target, element,
1453 element_length, target_index, NumberDec(depth)));
1454 Goto(&next);
1455 }
1456
1457 BIND(&if_flatten_proxy);
1458 {
1459 CSA_ASSERT(this, IsJSProxy(element));
1460
1461 // 1. Let elementLen be ? ToLength(? Get(element, "length")).
1462 const TNode<Number> element_length = ToLength_Inline(
1463 context, GetProperty(context, element, LengthStringConstant()));
1464
1465 // 2. Set targetIndex to ? FlattenIntoArray(target, element,
1466 // elementLen, targetIndex,
1467 // depth - 1).
1468 var_target_index = CAST(
1469 CallBuiltin(Builtins::kFlattenIntoArray, context, target, element,
1470 element_length, target_index, NumberDec(depth)));
1471 Goto(&next);
1472 }
1473
1474 BIND(&if_noflatten);
1475 {
1476 // 1. If targetIndex >= 2^53-1, throw a TypeError exception.
1477 Label throw_error(this, Label::kDeferred);
1478 GotoIfNumberGreaterThanOrEqual(
1479 target_index, NumberConstant(kMaxSafeInteger), &throw_error);
1480
1481 // 2. Perform ? CreateDataPropertyOrThrow(target,
1482 // ! ToString(targetIndex),
1483 // element).
1484 CallRuntime(Runtime::kCreateDataProperty, context, target,
1485 target_index, element);
1486
1487 // 3. Increase targetIndex by 1.
1488 var_target_index = NumberInc(target_index);
1489 Goto(&next);
1490
1491 BIND(&throw_error);
1492 ThrowTypeError(context, MessageTemplate::kFlattenPastSafeLength,
1493 source_length, target_index);
1494 }
1495 }
1496 BIND(&next);
1497
1498 // d. Increase sourceIndex by 1.
1499 var_source_index = NumberInc(source_index);
1500 Goto(&loop);
1501 }
1502
1503 BIND(&done_loop);
1504 return var_target_index.value();
1505 }
1506 };
1507
1508 // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
TF_BUILTIN(FlattenIntoArray,ArrayFlattenAssembler)1509 TF_BUILTIN(FlattenIntoArray, ArrayFlattenAssembler) {
1510 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1511 TNode<JSReceiver> target = CAST(Parameter(Descriptor::kTarget));
1512 TNode<JSReceiver> source = CAST(Parameter(Descriptor::kSource));
1513 TNode<Number> source_length = CAST(Parameter(Descriptor::kSourceLength));
1514 TNode<Number> start = CAST(Parameter(Descriptor::kStart));
1515 TNode<Number> depth = CAST(Parameter(Descriptor::kDepth));
1516
1517 // FlattenIntoArray might get called recursively, check stack for overflow
1518 // manually as it has stub linkage.
1519 PerformStackCheck(context);
1520
1521 Return(
1522 FlattenIntoArray(context, target, source, source_length, start, depth));
1523 }
1524
1525 // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
TF_BUILTIN(FlatMapIntoArray,ArrayFlattenAssembler)1526 TF_BUILTIN(FlatMapIntoArray, ArrayFlattenAssembler) {
1527 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1528 TNode<JSReceiver> target = CAST(Parameter(Descriptor::kTarget));
1529 TNode<JSReceiver> source = CAST(Parameter(Descriptor::kSource));
1530 TNode<Number> source_length = CAST(Parameter(Descriptor::kSourceLength));
1531 TNode<Number> start = CAST(Parameter(Descriptor::kStart));
1532 TNode<Number> depth = CAST(Parameter(Descriptor::kDepth));
1533 TNode<HeapObject> mapper_function =
1534 CAST(Parameter(Descriptor::kMapperFunction));
1535 TNode<Object> this_arg = CAST(Parameter(Descriptor::kThisArg));
1536
1537 Return(FlattenIntoArray(context, target, source, source_length, start, depth,
1538 mapper_function, this_arg));
1539 }
1540
1541 // https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flat
TF_BUILTIN(ArrayPrototypeFlat,CodeStubAssembler)1542 TF_BUILTIN(ArrayPrototypeFlat, CodeStubAssembler) {
1543 const TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
1544 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
1545 CodeStubArguments args(this, argc);
1546 const TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1547 const TNode<Object> receiver = args.GetReceiver();
1548 const TNode<Object> depth = args.GetOptionalArgumentValue(0);
1549
1550 // 1. Let O be ? ToObject(this value).
1551 const TNode<JSReceiver> o = ToObject_Inline(context, receiver);
1552
1553 // 2. Let sourceLen be ? ToLength(? Get(O, "length")).
1554 const TNode<Number> source_length =
1555 ToLength_Inline(context, GetProperty(context, o, LengthStringConstant()));
1556
1557 // 3. Let depthNum be 1.
1558 TVARIABLE(Number, var_depth_num, SmiConstant(1));
1559
1560 // 4. If depth is not undefined, then
1561 Label done(this);
1562 GotoIf(IsUndefined(depth), &done);
1563 {
1564 // a. Set depthNum to ? ToInteger(depth).
1565 var_depth_num = ToInteger_Inline(context, depth);
1566 Goto(&done);
1567 }
1568 BIND(&done);
1569
1570 // 5. Let A be ? ArraySpeciesCreate(O, 0).
1571 const TNode<JSReceiver> constructor =
1572 CAST(CallRuntime(Runtime::kArraySpeciesConstructor, context, o));
1573 const TNode<JSReceiver> a = Construct(context, constructor, SmiConstant(0));
1574
1575 // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
1576 CallBuiltin(Builtins::kFlattenIntoArray, context, a, o, source_length,
1577 SmiConstant(0), var_depth_num.value());
1578
1579 // 7. Return A.
1580 args.PopAndReturn(a);
1581 }
1582
1583 // https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap
TF_BUILTIN(ArrayPrototypeFlatMap,CodeStubAssembler)1584 TF_BUILTIN(ArrayPrototypeFlatMap, CodeStubAssembler) {
1585 const TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
1586 UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
1587 CodeStubArguments args(this, argc);
1588 const TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1589 const TNode<Object> receiver = args.GetReceiver();
1590 const TNode<Object> mapper_function = args.GetOptionalArgumentValue(0);
1591
1592 // 1. Let O be ? ToObject(this value).
1593 const TNode<JSReceiver> o = ToObject_Inline(context, receiver);
1594
1595 // 2. Let sourceLen be ? ToLength(? Get(O, "length")).
1596 const TNode<Number> source_length =
1597 ToLength_Inline(context, GetProperty(context, o, LengthStringConstant()));
1598
1599 // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception.
1600 Label if_not_callable(this, Label::kDeferred);
1601 GotoIf(TaggedIsSmi(mapper_function), &if_not_callable);
1602 GotoIfNot(IsCallable(CAST(mapper_function)), &if_not_callable);
1603
1604 // 4. If thisArg is present, let T be thisArg; else let T be undefined.
1605 const TNode<Object> t = args.GetOptionalArgumentValue(1);
1606
1607 // 5. Let A be ? ArraySpeciesCreate(O, 0).
1608 const TNode<JSReceiver> constructor =
1609 CAST(CallRuntime(Runtime::kArraySpeciesConstructor, context, o));
1610 const TNode<JSReceiver> a = Construct(context, constructor, SmiConstant(0));
1611
1612 // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T).
1613 CallBuiltin(Builtins::kFlatMapIntoArray, context, a, o, source_length,
1614 SmiConstant(0), SmiConstant(1), mapper_function, t);
1615
1616 // 7. Return A.
1617 args.PopAndReturn(a);
1618
1619 BIND(&if_not_callable);
1620 { ThrowTypeError(context, MessageTemplate::kMapperFunctionNonCallable); }
1621 }
1622
TF_BUILTIN(ArrayConstructor,ArrayBuiltinsAssembler)1623 TF_BUILTIN(ArrayConstructor, ArrayBuiltinsAssembler) {
1624 // This is a trampoline to ArrayConstructorImpl which just adds
1625 // allocation_site parameter value and sets new_target if necessary.
1626 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1627 TNode<JSFunction> function = CAST(Parameter(Descriptor::kTarget));
1628 TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
1629 TNode<Int32T> argc =
1630 UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
1631
1632 // If new_target is undefined, then this is the 'Call' case, so set new_target
1633 // to function.
1634 new_target =
1635 SelectConstant<Object>(IsUndefined(new_target), function, new_target);
1636
1637 // Run the native code for the Array function called as a normal function.
1638 TNode<Oddball> no_allocation_site = UndefinedConstant();
1639 TailCallBuiltin(Builtins::kArrayConstructorImpl, context, function,
1640 new_target, argc, no_allocation_site);
1641 }
1642
TailCallArrayConstructorStub(const Callable & callable,TNode<Context> context,TNode<JSFunction> target,TNode<HeapObject> allocation_site_or_undefined,TNode<Int32T> argc)1643 void ArrayBuiltinsAssembler::TailCallArrayConstructorStub(
1644 const Callable& callable, TNode<Context> context, TNode<JSFunction> target,
1645 TNode<HeapObject> allocation_site_or_undefined, TNode<Int32T> argc) {
1646 TNode<Code> code = HeapConstant(callable.code());
1647
1648 // We are going to call here ArrayNoArgumentsConstructor or
1649 // ArraySingleArgumentsConstructor which in addition to the register arguments
1650 // also expect some number of arguments on the expression stack.
1651 // Since
1652 // 1) incoming JS arguments are still on the stack,
1653 // 2) the ArrayNoArgumentsConstructor, ArraySingleArgumentsConstructor and
1654 // ArrayNArgumentsConstructor are defined so that the register arguments
1655 // are passed on the same registers,
1656 // in order to be able to generate a tail call to those builtins we do the
1657 // following trick here: we tail call to the constructor builtin using
1658 // ArrayNArgumentsConstructorDescriptor, so the tail call instruction
1659 // pops the current frame but leaves all the incoming JS arguments on the
1660 // expression stack so that the target builtin can still find them where it
1661 // expects.
1662 TailCallStub(ArrayNArgumentsConstructorDescriptor{}, code, context, target,
1663 allocation_site_or_undefined, argc);
1664 }
1665
CreateArrayDispatchNoArgument(TNode<Context> context,TNode<JSFunction> target,TNode<Int32T> argc,AllocationSiteOverrideMode mode,TNode<AllocationSite> allocation_site)1666 void ArrayBuiltinsAssembler::CreateArrayDispatchNoArgument(
1667 TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc,
1668 AllocationSiteOverrideMode mode, TNode<AllocationSite> allocation_site) {
1669 if (mode == DISABLE_ALLOCATION_SITES) {
1670 Callable callable = CodeFactory::ArrayNoArgumentConstructor(
1671 isolate(), GetInitialFastElementsKind(), mode);
1672
1673 TailCallArrayConstructorStub(callable, context, target, UndefinedConstant(),
1674 argc);
1675 } else {
1676 DCHECK_EQ(mode, DONT_OVERRIDE);
1677 TNode<Int32T> elements_kind = LoadElementsKind(allocation_site);
1678
1679 // TODO(ishell): Compute the builtin index dynamically instead of
1680 // iterating over all expected elements kinds.
1681 int last_index =
1682 GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND);
1683 for (int i = 0; i <= last_index; ++i) {
1684 Label next(this);
1685 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
1686 GotoIfNot(Word32Equal(elements_kind, Int32Constant(kind)), &next);
1687
1688 Callable callable =
1689 CodeFactory::ArrayNoArgumentConstructor(isolate(), kind, mode);
1690
1691 TailCallArrayConstructorStub(callable, context, target, allocation_site,
1692 argc);
1693
1694 BIND(&next);
1695 }
1696
1697 // If we reached this point there is a problem.
1698 Abort(AbortReason::kUnexpectedElementsKindInArrayConstructor);
1699 }
1700 }
1701
CreateArrayDispatchSingleArgument(TNode<Context> context,TNode<JSFunction> target,TNode<Int32T> argc,AllocationSiteOverrideMode mode,TNode<AllocationSite> allocation_site)1702 void ArrayBuiltinsAssembler::CreateArrayDispatchSingleArgument(
1703 TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc,
1704 AllocationSiteOverrideMode mode, TNode<AllocationSite> allocation_site) {
1705 if (mode == DISABLE_ALLOCATION_SITES) {
1706 ElementsKind initial = GetInitialFastElementsKind();
1707 ElementsKind holey_initial = GetHoleyElementsKind(initial);
1708 Callable callable = CodeFactory::ArraySingleArgumentConstructor(
1709 isolate(), holey_initial, mode);
1710
1711 TailCallArrayConstructorStub(callable, context, target, UndefinedConstant(),
1712 argc);
1713 } else {
1714 DCHECK_EQ(mode, DONT_OVERRIDE);
1715 TNode<Smi> transition_info = LoadTransitionInfo(allocation_site);
1716
1717 // Least significant bit in fast array elements kind means holeyness.
1718 STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
1719 STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
1720 STATIC_ASSERT(PACKED_ELEMENTS == 2);
1721 STATIC_ASSERT(HOLEY_ELEMENTS == 3);
1722 STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
1723 STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
1724
1725 Label normal_sequence(this);
1726 TVARIABLE(Int32T, var_elements_kind,
1727 Signed(DecodeWord32<AllocationSite::ElementsKindBits>(
1728 SmiToInt32(transition_info))));
1729 // Is the low bit set? If so, we are holey and that is good.
1730 int fast_elements_kind_holey_mask =
1731 AllocationSite::ElementsKindBits::encode(static_cast<ElementsKind>(1));
1732 GotoIf(IsSetSmi(transition_info, fast_elements_kind_holey_mask),
1733 &normal_sequence);
1734 {
1735 // Make elements kind holey and update elements kind in the type info.
1736 var_elements_kind = Word32Or(var_elements_kind.value(), Int32Constant(1));
1737 StoreObjectFieldNoWriteBarrier(
1738 allocation_site, AllocationSite::kTransitionInfoOrBoilerplateOffset,
1739 SmiOr(transition_info, SmiConstant(fast_elements_kind_holey_mask)));
1740 Goto(&normal_sequence);
1741 }
1742 BIND(&normal_sequence);
1743
1744 // TODO(ishell): Compute the builtin index dynamically instead of
1745 // iterating over all expected elements kinds.
1746 // TODO(ishell): Given that the code above ensures that the elements kind
1747 // is holey we can skip checking with non-holey elements kinds.
1748 int last_index =
1749 GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND);
1750 for (int i = 0; i <= last_index; ++i) {
1751 Label next(this);
1752 ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
1753 GotoIfNot(Word32Equal(var_elements_kind.value(), Int32Constant(kind)),
1754 &next);
1755
1756 Callable callable =
1757 CodeFactory::ArraySingleArgumentConstructor(isolate(), kind, mode);
1758
1759 TailCallArrayConstructorStub(callable, context, target, allocation_site,
1760 argc);
1761
1762 BIND(&next);
1763 }
1764
1765 // If we reached this point there is a problem.
1766 Abort(AbortReason::kUnexpectedElementsKindInArrayConstructor);
1767 }
1768 }
1769
GenerateDispatchToArrayStub(TNode<Context> context,TNode<JSFunction> target,TNode<Int32T> argc,AllocationSiteOverrideMode mode,TNode<AllocationSite> allocation_site)1770 void ArrayBuiltinsAssembler::GenerateDispatchToArrayStub(
1771 TNode<Context> context, TNode<JSFunction> target, TNode<Int32T> argc,
1772 AllocationSiteOverrideMode mode, TNode<AllocationSite> allocation_site) {
1773 Label check_one_case(this), fallthrough(this);
1774 GotoIfNot(Word32Equal(argc, Int32Constant(0)), &check_one_case);
1775 CreateArrayDispatchNoArgument(context, target, argc, mode, allocation_site);
1776
1777 BIND(&check_one_case);
1778 GotoIfNot(Word32Equal(argc, Int32Constant(1)), &fallthrough);
1779 CreateArrayDispatchSingleArgument(context, target, argc, mode,
1780 allocation_site);
1781
1782 BIND(&fallthrough);
1783 }
1784
TF_BUILTIN(ArrayConstructorImpl,ArrayBuiltinsAssembler)1785 TF_BUILTIN(ArrayConstructorImpl, ArrayBuiltinsAssembler) {
1786 TNode<JSFunction> target = CAST(Parameter(Descriptor::kTarget));
1787 TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
1788 TNode<Int32T> argc =
1789 UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
1790 TNode<HeapObject> maybe_allocation_site =
1791 CAST(Parameter(Descriptor::kAllocationSite));
1792
1793 // Initial map for the builtin Array functions should be Map.
1794 CSA_ASSERT(this, IsMap(CAST(LoadObjectField(
1795 target, JSFunction::kPrototypeOrInitialMapOffset))));
1796
1797 // We should either have undefined or a valid AllocationSite
1798 CSA_ASSERT(this, Word32Or(IsUndefined(maybe_allocation_site),
1799 IsAllocationSite(maybe_allocation_site)));
1800
1801 // "Enter" the context of the Array function.
1802 TNode<Context> context =
1803 CAST(LoadObjectField(target, JSFunction::kContextOffset));
1804
1805 Label runtime(this, Label::kDeferred);
1806 GotoIf(TaggedNotEqual(target, new_target), &runtime);
1807
1808 Label no_info(this);
1809 // If the feedback vector is the undefined value call an array constructor
1810 // that doesn't use AllocationSites.
1811 GotoIf(IsUndefined(maybe_allocation_site), &no_info);
1812
1813 GenerateDispatchToArrayStub(context, target, argc, DONT_OVERRIDE,
1814 CAST(maybe_allocation_site));
1815 Goto(&runtime);
1816
1817 BIND(&no_info);
1818 GenerateDispatchToArrayStub(context, target, argc, DISABLE_ALLOCATION_SITES);
1819 Goto(&runtime);
1820
1821 BIND(&runtime);
1822 GenerateArrayNArgumentsConstructor(context, target, new_target, argc,
1823 maybe_allocation_site);
1824 }
1825
GenerateConstructor(TNode<Context> context,TNode<HeapObject> array_function,TNode<Map> array_map,TNode<Object> array_size,TNode<HeapObject> allocation_site,ElementsKind elements_kind,AllocationSiteMode mode)1826 void ArrayBuiltinsAssembler::GenerateConstructor(
1827 TNode<Context> context, TNode<HeapObject> array_function,
1828 TNode<Map> array_map, TNode<Object> array_size,
1829 TNode<HeapObject> allocation_site, ElementsKind elements_kind,
1830 AllocationSiteMode mode) {
1831 Label ok(this);
1832 Label smi_size(this);
1833 Label small_smi_size(this);
1834 Label call_runtime(this, Label::kDeferred);
1835
1836 Branch(TaggedIsSmi(array_size), &smi_size, &call_runtime);
1837
1838 BIND(&smi_size);
1839 {
1840 TNode<Smi> array_size_smi = CAST(array_size);
1841
1842 if (IsFastPackedElementsKind(elements_kind)) {
1843 Label abort(this, Label::kDeferred);
1844 Branch(SmiEqual(array_size_smi, SmiConstant(0)), &small_smi_size, &abort);
1845
1846 BIND(&abort);
1847 TNode<Smi> reason =
1848 SmiConstant(AbortReason::kAllocatingNonEmptyPackedArray);
1849 TailCallRuntime(Runtime::kAbort, context, reason);
1850 } else {
1851 int element_size =
1852 IsDoubleElementsKind(elements_kind) ? kDoubleSize : kTaggedSize;
1853 int max_fast_elements =
1854 (kMaxRegularHeapObjectSize - FixedArray::kHeaderSize -
1855 JSArray::kHeaderSize - AllocationMemento::kSize) /
1856 element_size;
1857 Branch(SmiAboveOrEqual(array_size_smi, SmiConstant(max_fast_elements)),
1858 &call_runtime, &small_smi_size);
1859 }
1860
1861 BIND(&small_smi_size);
1862 {
1863 TNode<JSArray> array = AllocateJSArray(
1864 elements_kind, array_map, array_size_smi, array_size_smi,
1865 mode == DONT_TRACK_ALLOCATION_SITE ? TNode<AllocationSite>()
1866 : CAST(allocation_site));
1867 Return(array);
1868 }
1869 }
1870
1871 BIND(&call_runtime);
1872 {
1873 TailCallRuntimeNewArray(context, array_function, array_size, array_function,
1874 allocation_site);
1875 }
1876 }
1877
GenerateArrayNoArgumentConstructor(ElementsKind kind,AllocationSiteOverrideMode mode)1878 void ArrayBuiltinsAssembler::GenerateArrayNoArgumentConstructor(
1879 ElementsKind kind, AllocationSiteOverrideMode mode) {
1880 using Descriptor = ArrayNoArgumentConstructorDescriptor;
1881 TNode<NativeContext> native_context = CAST(LoadObjectField(
1882 Parameter(Descriptor::kFunction), JSFunction::kContextOffset));
1883 bool track_allocation_site =
1884 AllocationSite::ShouldTrack(kind) && mode != DISABLE_ALLOCATION_SITES;
1885 TNode<AllocationSite> allocation_site =
1886 track_allocation_site ? CAST(Parameter(Descriptor::kAllocationSite))
1887 : TNode<AllocationSite>();
1888 TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
1889 TNode<JSArray> array = AllocateJSArray(
1890 kind, array_map, IntPtrConstant(JSArray::kPreallocatedArrayElements),
1891 SmiConstant(0), allocation_site);
1892 Return(array);
1893 }
1894
GenerateArraySingleArgumentConstructor(ElementsKind kind,AllocationSiteOverrideMode mode)1895 void ArrayBuiltinsAssembler::GenerateArraySingleArgumentConstructor(
1896 ElementsKind kind, AllocationSiteOverrideMode mode) {
1897 using Descriptor = ArraySingleArgumentConstructorDescriptor;
1898 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1899 TNode<HeapObject> function = CAST(Parameter(Descriptor::kFunction));
1900 TNode<NativeContext> native_context =
1901 CAST(LoadObjectField(function, JSFunction::kContextOffset));
1902 TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
1903
1904 AllocationSiteMode allocation_site_mode = DONT_TRACK_ALLOCATION_SITE;
1905 if (mode == DONT_OVERRIDE) {
1906 allocation_site_mode = AllocationSite::ShouldTrack(kind)
1907 ? TRACK_ALLOCATION_SITE
1908 : DONT_TRACK_ALLOCATION_SITE;
1909 }
1910
1911 TNode<Object> array_size =
1912 CAST(Parameter(Descriptor::kArraySizeSmiParameter));
1913 // allocation_site can be Undefined or an AllocationSite
1914 TNode<HeapObject> allocation_site =
1915 CAST(Parameter(Descriptor::kAllocationSite));
1916
1917 GenerateConstructor(context, function, array_map, array_size, allocation_site,
1918 kind, allocation_site_mode);
1919 }
1920
GenerateArrayNArgumentsConstructor(TNode<Context> context,TNode<JSFunction> target,TNode<Object> new_target,TNode<Int32T> argc,TNode<HeapObject> maybe_allocation_site)1921 void ArrayBuiltinsAssembler::GenerateArrayNArgumentsConstructor(
1922 TNode<Context> context, TNode<JSFunction> target, TNode<Object> new_target,
1923 TNode<Int32T> argc, TNode<HeapObject> maybe_allocation_site) {
1924 // Replace incoming JS receiver argument with the target.
1925 // TODO(ishell): Avoid replacing the target on the stack and just add it
1926 // as another additional parameter for Runtime::kNewArray.
1927 CodeStubArguments args(this, argc);
1928 args.SetReceiver(target);
1929
1930 // Adjust arguments count for the runtime call: +1 for implicit receiver
1931 // and +2 for new_target and maybe_allocation_site.
1932 argc = Int32Add(argc, Int32Constant(3));
1933 TailCallRuntime(Runtime::kNewArray, argc, context, new_target,
1934 maybe_allocation_site);
1935 }
1936
TF_BUILTIN(ArrayNArgumentsConstructor,ArrayBuiltinsAssembler)1937 TF_BUILTIN(ArrayNArgumentsConstructor, ArrayBuiltinsAssembler) {
1938 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1939 TNode<JSFunction> target = CAST(Parameter(Descriptor::kFunction));
1940 TNode<Int32T> argc =
1941 UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
1942 TNode<HeapObject> maybe_allocation_site =
1943 CAST(Parameter(Descriptor::kAllocationSite));
1944
1945 GenerateArrayNArgumentsConstructor(context, target, target, argc,
1946 maybe_allocation_site);
1947 }
1948
1949 #define GENERATE_ARRAY_CTOR(name, kind_camel, kind_caps, mode_camel, \
1950 mode_caps) \
1951 TF_BUILTIN(Array##name##Constructor_##kind_camel##_##mode_camel, \
1952 ArrayBuiltinsAssembler) { \
1953 GenerateArray##name##Constructor(kind_caps, mode_caps); \
1954 }
1955
1956 // The ArrayNoArgumentConstructor builtin family.
1957 GENERATE_ARRAY_CTOR(NoArgument, PackedSmi, PACKED_SMI_ELEMENTS, DontOverride,
1958 DONT_OVERRIDE)
1959 GENERATE_ARRAY_CTOR(NoArgument, HoleySmi, HOLEY_SMI_ELEMENTS, DontOverride,
1960 DONT_OVERRIDE)
1961 GENERATE_ARRAY_CTOR(NoArgument, PackedSmi, PACKED_SMI_ELEMENTS,
1962 DisableAllocationSites, DISABLE_ALLOCATION_SITES)
1963 GENERATE_ARRAY_CTOR(NoArgument, HoleySmi, HOLEY_SMI_ELEMENTS,
1964 DisableAllocationSites, DISABLE_ALLOCATION_SITES)
1965 GENERATE_ARRAY_CTOR(NoArgument, Packed, PACKED_ELEMENTS, DisableAllocationSites,
1966 DISABLE_ALLOCATION_SITES)
1967 GENERATE_ARRAY_CTOR(NoArgument, Holey, HOLEY_ELEMENTS, DisableAllocationSites,
1968 DISABLE_ALLOCATION_SITES)
1969 GENERATE_ARRAY_CTOR(NoArgument, PackedDouble, PACKED_DOUBLE_ELEMENTS,
1970 DisableAllocationSites, DISABLE_ALLOCATION_SITES)
1971 GENERATE_ARRAY_CTOR(NoArgument, HoleyDouble, HOLEY_DOUBLE_ELEMENTS,
1972 DisableAllocationSites, DISABLE_ALLOCATION_SITES)
1973
1974 // The ArraySingleArgumentConstructor builtin family.
1975 GENERATE_ARRAY_CTOR(SingleArgument, PackedSmi, PACKED_SMI_ELEMENTS,
1976 DontOverride, DONT_OVERRIDE)
1977 GENERATE_ARRAY_CTOR(SingleArgument, HoleySmi, HOLEY_SMI_ELEMENTS, DontOverride,
1978 DONT_OVERRIDE)
1979 GENERATE_ARRAY_CTOR(SingleArgument, PackedSmi, PACKED_SMI_ELEMENTS,
1980 DisableAllocationSites, DISABLE_ALLOCATION_SITES)
1981 GENERATE_ARRAY_CTOR(SingleArgument, HoleySmi, HOLEY_SMI_ELEMENTS,
1982 DisableAllocationSites, DISABLE_ALLOCATION_SITES)
1983 GENERATE_ARRAY_CTOR(SingleArgument, Packed, PACKED_ELEMENTS,
1984 DisableAllocationSites, DISABLE_ALLOCATION_SITES)
1985 GENERATE_ARRAY_CTOR(SingleArgument, Holey, HOLEY_ELEMENTS,
1986 DisableAllocationSites, DISABLE_ALLOCATION_SITES)
1987 GENERATE_ARRAY_CTOR(SingleArgument, PackedDouble, PACKED_DOUBLE_ELEMENTS,
1988 DisableAllocationSites, DISABLE_ALLOCATION_SITES)
1989 GENERATE_ARRAY_CTOR(SingleArgument, HoleyDouble, HOLEY_DOUBLE_ELEMENTS,
1990 DisableAllocationSites, DISABLE_ALLOCATION_SITES)
1991
1992 #undef GENERATE_ARRAY_CTOR
1993
1994 } // namespace internal
1995 } // namespace v8
1996