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