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-iterator-gen.h"
6 #include "src/builtins/builtins-string-gen.h"
7 #include "src/builtins/builtins-typed-array-gen.h"
8 #include "src/builtins/builtins-utils-gen.h"
9 #include "src/builtins/builtins.h"
10 #include "src/code-stub-assembler.h"
11 #include "src/frame-constants.h"
12 #include "src/heap/factory-inl.h"
13
14 #include "src/builtins/builtins-array-gen.h"
15
16 namespace v8 {
17 namespace internal {
18
19 using Node = compiler::Node;
20
ArrayBuiltinsAssembler(compiler::CodeAssemblerState * state)21 ArrayBuiltinsAssembler::ArrayBuiltinsAssembler(
22 compiler::CodeAssemblerState* state)
23 : BaseBuiltinsFromDSLAssembler(state),
24 k_(this, MachineRepresentation::kTagged),
25 a_(this, MachineRepresentation::kTagged),
26 to_(this, MachineRepresentation::kTagged, SmiConstant(0)),
27 fully_spec_compliant_(this, {&k_, &a_, &to_}) {}
28
FindResultGenerator()29 void ArrayBuiltinsAssembler::FindResultGenerator() {
30 a_.Bind(UndefinedConstant());
31 }
32
FindProcessor(Node * k_value,Node * k)33 Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
34 Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(),
35 this_arg(), k_value, k, o());
36 Label false_continue(this), return_true(this);
37 BranchIfToBooleanIsTrue(value, &return_true, &false_continue);
38 BIND(&return_true);
39 ReturnFromBuiltin(k_value);
40 BIND(&false_continue);
41 return a();
42 }
43
FindIndexResultGenerator()44 void ArrayBuiltinsAssembler::FindIndexResultGenerator() {
45 a_.Bind(SmiConstant(-1));
46 }
47
FindIndexProcessor(Node * k_value,Node * k)48 Node* ArrayBuiltinsAssembler::FindIndexProcessor(Node* k_value, Node* k) {
49 Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(),
50 this_arg(), k_value, k, o());
51 Label false_continue(this), return_true(this);
52 BranchIfToBooleanIsTrue(value, &return_true, &false_continue);
53 BIND(&return_true);
54 ReturnFromBuiltin(k);
55 BIND(&false_continue);
56 return a();
57 }
58
ForEachResultGenerator()59 void ArrayBuiltinsAssembler::ForEachResultGenerator() {
60 a_.Bind(UndefinedConstant());
61 }
62
ForEachProcessor(Node * k_value,Node * k)63 Node* ArrayBuiltinsAssembler::ForEachProcessor(Node* k_value, Node* k) {
64 CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), this_arg(),
65 k_value, k, o());
66 return a();
67 }
68
SomeResultGenerator()69 void ArrayBuiltinsAssembler::SomeResultGenerator() {
70 a_.Bind(FalseConstant());
71 }
72
SomeProcessor(Node * k_value,Node * k)73 Node* ArrayBuiltinsAssembler::SomeProcessor(Node* k_value, Node* k) {
74 Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(),
75 this_arg(), k_value, k, o());
76 Label false_continue(this), return_true(this);
77 BranchIfToBooleanIsTrue(value, &return_true, &false_continue);
78 BIND(&return_true);
79 ReturnFromBuiltin(TrueConstant());
80 BIND(&false_continue);
81 return a();
82 }
83
EveryResultGenerator()84 void ArrayBuiltinsAssembler::EveryResultGenerator() {
85 a_.Bind(TrueConstant());
86 }
87
EveryProcessor(Node * k_value,Node * k)88 Node* ArrayBuiltinsAssembler::EveryProcessor(Node* k_value, Node* k) {
89 Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(),
90 this_arg(), k_value, k, o());
91 Label true_continue(this), return_false(this);
92 BranchIfToBooleanIsTrue(value, &true_continue, &return_false);
93 BIND(&return_false);
94 ReturnFromBuiltin(FalseConstant());
95 BIND(&true_continue);
96 return a();
97 }
98
ReduceResultGenerator()99 void ArrayBuiltinsAssembler::ReduceResultGenerator() {
100 return a_.Bind(this_arg());
101 }
102
ReduceProcessor(Node * k_value,Node * k)103 Node* ArrayBuiltinsAssembler::ReduceProcessor(Node* k_value, Node* k) {
104 VARIABLE(result, MachineRepresentation::kTagged);
105 Label done(this, {&result}), initial(this);
106 GotoIf(WordEqual(a(), TheHoleConstant()), &initial);
107 result.Bind(CallJS(CodeFactory::Call(isolate()), context(), callbackfn(),
108 UndefinedConstant(), a(), k_value, k, o()));
109 Goto(&done);
110
111 BIND(&initial);
112 result.Bind(k_value);
113 Goto(&done);
114
115 BIND(&done);
116 return result.value();
117 }
118
ReducePostLoopAction()119 void ArrayBuiltinsAssembler::ReducePostLoopAction() {
120 Label ok(this);
121 GotoIf(WordNotEqual(a(), TheHoleConstant()), &ok);
122 ThrowTypeError(context(), MessageTemplate::kReduceNoInitial);
123 BIND(&ok);
124 }
125
FilterResultGenerator()126 void ArrayBuiltinsAssembler::FilterResultGenerator() {
127 // 7. Let A be ArraySpeciesCreate(O, 0).
128 // This version of ArraySpeciesCreate will create with the correct
129 // ElementsKind in the fast case.
130 GenerateArraySpeciesCreate();
131 }
132
FilterProcessor(Node * k_value,Node * k)133 Node* ArrayBuiltinsAssembler::FilterProcessor(Node* k_value, Node* k) {
134 // ii. Let selected be ToBoolean(? Call(callbackfn, T, kValue, k, O)).
135 Node* selected = CallJS(CodeFactory::Call(isolate()), context(),
136 callbackfn(), this_arg(), k_value, k, o());
137 Label true_continue(this, &to_), false_continue(this);
138 BranchIfToBooleanIsTrue(selected, &true_continue, &false_continue);
139 BIND(&true_continue);
140 // iii. If selected is true, then...
141 {
142 Label after_work(this, &to_);
143 Node* kind = nullptr;
144
145 // If a() is a JSArray, we can have a fast path.
146 Label fast(this);
147 Label runtime(this);
148 Label object_push_pre(this), object_push(this), double_push(this);
149 BranchIfFastJSArray(a(), context(), &fast, &runtime);
150
151 BIND(&fast);
152 {
153 GotoIf(WordNotEqual(LoadJSArrayLength(a()), to_.value()), &runtime);
154 kind = EnsureArrayPushable(LoadMap(a()), &runtime);
155 GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS),
156 &object_push_pre);
157
158 BuildAppendJSArray(HOLEY_SMI_ELEMENTS, a(), k_value, &runtime);
159 Goto(&after_work);
160 }
161
162 BIND(&object_push_pre);
163 {
164 Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), &double_push,
165 &object_push);
166 }
167
168 BIND(&object_push);
169 {
170 BuildAppendJSArray(HOLEY_ELEMENTS, a(), k_value, &runtime);
171 Goto(&after_work);
172 }
173
174 BIND(&double_push);
175 {
176 BuildAppendJSArray(HOLEY_DOUBLE_ELEMENTS, a(), k_value, &runtime);
177 Goto(&after_work);
178 }
179
180 BIND(&runtime);
181 {
182 // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue).
183 CallRuntime(Runtime::kCreateDataProperty, context(), a(), to_.value(),
184 k_value);
185 Goto(&after_work);
186 }
187
188 BIND(&after_work);
189 {
190 // 2. Increase to by 1.
191 to_.Bind(NumberInc(to_.value()));
192 Goto(&false_continue);
193 }
194 }
195 BIND(&false_continue);
196 return a();
197 }
198
MapResultGenerator()199 void ArrayBuiltinsAssembler::MapResultGenerator() {
200 GenerateArraySpeciesCreate(len_);
201 }
202
TypedArrayMapResultGenerator()203 void ArrayBuiltinsAssembler::TypedArrayMapResultGenerator() {
204 // 6. Let A be ? TypedArraySpeciesCreate(O, len).
205 TNode<JSTypedArray> original_array = CAST(o());
206 TNode<Smi> length = CAST(len_);
207 const char* method_name = "%TypedArray%.prototype.map";
208
209 TypedArrayBuiltinsAssembler typedarray_asm(state());
210 TNode<JSTypedArray> a = typedarray_asm.SpeciesCreateByLength(
211 context(), original_array, length, method_name);
212 // In the Spec and our current implementation, the length check is already
213 // performed in TypedArraySpeciesCreate.
214 CSA_ASSERT(
215 this,
216 SmiLessThanOrEqual(
217 CAST(len_), CAST(LoadObjectField(a, JSTypedArray::kLengthOffset))));
218 fast_typed_array_target_ =
219 Word32Equal(LoadInstanceType(LoadElements(original_array)),
220 LoadInstanceType(LoadElements(a)));
221 a_.Bind(a);
222 }
223
SpecCompliantMapProcessor(Node * k_value,Node * k)224 Node* ArrayBuiltinsAssembler::SpecCompliantMapProcessor(Node* k_value,
225 Node* k) {
226 // i. Let kValue be ? Get(O, Pk). Performed by the caller of
227 // SpecCompliantMapProcessor.
228 // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O).
229 Node* mapped_value = CallJS(CodeFactory::Call(isolate()), context(),
230 callbackfn(), this_arg(), k_value, k, o());
231
232 // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value).
233 CallRuntime(Runtime::kCreateDataProperty, context(), a(), k, mapped_value);
234 return a();
235 }
236
FastMapProcessor(Node * k_value,Node * k)237 Node* ArrayBuiltinsAssembler::FastMapProcessor(Node* k_value, Node* k) {
238 // i. Let kValue be ? Get(O, Pk). Performed by the caller of
239 // FastMapProcessor.
240 // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O).
241 Node* mapped_value = CallJS(CodeFactory::Call(isolate()), context(),
242 callbackfn(), this_arg(), k_value, k, o());
243
244 // mode is SMI_PARAMETERS because k has tagged representation.
245 ParameterMode mode = SMI_PARAMETERS;
246 Label runtime(this), finished(this);
247 Label transition_pre(this), transition_smi_fast(this),
248 transition_smi_double(this);
249 Label array_not_smi(this), array_fast(this), array_double(this);
250
251 Node* kind = LoadMapElementsKind(LoadMap(a()));
252 Node* elements = LoadElements(a());
253 GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS), &array_not_smi);
254 TryStoreArrayElement(HOLEY_SMI_ELEMENTS, mode, &transition_pre, elements, k,
255 mapped_value);
256 Goto(&finished);
257
258 BIND(&transition_pre);
259 {
260 // array is smi. Value is either tagged or a heap number.
261 CSA_ASSERT(this, TaggedIsNotSmi(mapped_value));
262 GotoIf(IsHeapNumberMap(LoadMap(mapped_value)), &transition_smi_double);
263 Goto(&transition_smi_fast);
264 }
265
266 BIND(&array_not_smi);
267 {
268 Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), &array_double,
269 &array_fast);
270 }
271
272 BIND(&transition_smi_fast);
273 {
274 // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value).
275 Node* const native_context = LoadNativeContext(context());
276 Node* const fast_map = LoadContextElement(
277 native_context, Context::JS_ARRAY_HOLEY_ELEMENTS_MAP_INDEX);
278
279 // Since this transition is only a map change, just do it right here.
280 // Since a() doesn't have an allocation site, it's safe to do the
281 // map store directly, otherwise I'd call TransitionElementsKind().
282 StoreMap(a(), fast_map);
283 Goto(&array_fast);
284 }
285
286 BIND(&array_fast);
287 {
288 TryStoreArrayElement(HOLEY_ELEMENTS, mode, &runtime, elements, k,
289 mapped_value);
290 Goto(&finished);
291 }
292
293 BIND(&transition_smi_double);
294 {
295 // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value).
296 Node* const native_context = LoadNativeContext(context());
297 Node* const double_map = LoadContextElement(
298 native_context, Context::JS_ARRAY_HOLEY_DOUBLE_ELEMENTS_MAP_INDEX);
299 CallStub(CodeFactory::TransitionElementsKind(
300 isolate(), HOLEY_SMI_ELEMENTS, HOLEY_DOUBLE_ELEMENTS, true),
301 context(), a(), double_map);
302 Goto(&array_double);
303 }
304
305 BIND(&array_double);
306 {
307 // TODO(mvstanton): If we use a variable for elements and bind it
308 // appropriately, we can avoid an extra load of elements by binding the
309 // value only after a transition from smi to double.
310 elements = LoadElements(a());
311 // If the mapped_value isn't a number, this will bail out to the runtime
312 // to make the transition.
313 TryStoreArrayElement(HOLEY_DOUBLE_ELEMENTS, mode, &runtime, elements, k,
314 mapped_value);
315 Goto(&finished);
316 }
317
318 BIND(&runtime);
319 {
320 // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value).
321 CallRuntime(Runtime::kCreateDataProperty, context(), a(), k,
322 mapped_value);
323 Goto(&finished);
324 }
325
326 BIND(&finished);
327 return a();
328 }
329
330 // See tc39.github.io/ecma262/#sec-%typedarray%.prototype.map.
TypedArrayMapProcessor(Node * k_value,Node * k)331 Node* ArrayBuiltinsAssembler::TypedArrayMapProcessor(Node* k_value, Node* k) {
332 // 8. c. Let mapped_value be ? Call(callbackfn, T, « kValue, k, O »).
333 Node* mapped_value = CallJS(CodeFactory::Call(isolate()), context(),
334 callbackfn(), this_arg(), k_value, k, o());
335 Label fast(this), slow(this), done(this), detached(this, Label::kDeferred);
336
337 // 8. d. Perform ? Set(A, Pk, mapped_value, true).
338 // Since we know that A is a TypedArray, this always ends up in
339 // #sec-integer-indexed-exotic-objects-set-p-v-receiver and then
340 // tc39.github.io/ecma262/#sec-integerindexedelementset .
341 Branch(fast_typed_array_target_, &fast, &slow);
342
343 BIND(&fast);
344 // #sec-integerindexedelementset
345 // 5. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let
346 // numValue be ? ToBigInt(v).
347 // 6. Otherwise, let numValue be ? ToNumber(value).
348 Node* num_value;
349 if (source_elements_kind_ == BIGINT64_ELEMENTS ||
350 source_elements_kind_ == BIGUINT64_ELEMENTS) {
351 num_value = ToBigInt(context(), mapped_value);
352 } else {
353 num_value = ToNumber_Inline(context(), mapped_value);
354 }
355 // The only way how this can bailout is because of a detached buffer.
356 EmitElementStore(a(), k, num_value, false, source_elements_kind_,
357 KeyedAccessStoreMode::STANDARD_STORE, &detached,
358 context());
359 Goto(&done);
360
361 BIND(&slow);
362 CallRuntime(Runtime::kSetProperty, context(), a(), k, mapped_value,
363 SmiConstant(LanguageMode::kStrict));
364 Goto(&done);
365
366 BIND(&detached);
367 // tc39.github.io/ecma262/#sec-integerindexedelementset
368 // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
369 ThrowTypeError(context_, MessageTemplate::kDetachedOperation, name_);
370
371 BIND(&done);
372 return a();
373 }
374
NullPostLoopAction()375 void ArrayBuiltinsAssembler::NullPostLoopAction() {}
376
ReturnFromBuiltin(Node * value)377 void ArrayBuiltinsAssembler::ReturnFromBuiltin(Node* value) {
378 if (argc_ == nullptr) {
379 Return(value);
380 } else {
381 // argc_ doesn't include the receiver, so it has to be added back in
382 // manually.
383 PopAndReturn(IntPtrAdd(argc_, IntPtrConstant(1)), value);
384 }
385 }
386
InitIteratingArrayBuiltinBody(TNode<Context> context,TNode<Object> receiver,Node * callbackfn,Node * this_arg,TNode<IntPtrT> argc)387 void ArrayBuiltinsAssembler::InitIteratingArrayBuiltinBody(
388 TNode<Context> context, TNode<Object> receiver, Node* callbackfn,
389 Node* this_arg, TNode<IntPtrT> argc) {
390 context_ = context;
391 receiver_ = receiver;
392 callbackfn_ = callbackfn;
393 this_arg_ = this_arg;
394 argc_ = argc;
395 }
396
GenerateIteratingArrayBuiltinBody(const char * name,const BuiltinResultGenerator & generator,const CallResultProcessor & processor,const PostLoopAction & action,const Callable & slow_case_continuation,MissingPropertyMode missing_property_mode,ForEachDirection direction)397 void ArrayBuiltinsAssembler::GenerateIteratingArrayBuiltinBody(
398 const char* name, const BuiltinResultGenerator& generator,
399 const CallResultProcessor& processor, const PostLoopAction& action,
400 const Callable& slow_case_continuation,
401 MissingPropertyMode missing_property_mode, ForEachDirection direction) {
402 Label non_array(this), array_changes(this, {&k_, &a_, &to_});
403
404 // TODO(danno): Seriously? Do we really need to throw the exact error
405 // message on null and undefined so that the webkit tests pass?
406 Label throw_null_undefined_exception(this, Label::kDeferred);
407 GotoIf(IsNullOrUndefined(receiver()), &throw_null_undefined_exception);
408
409 // By the book: taken directly from the ECMAScript 2015 specification
410
411 // 1. Let O be ToObject(this value).
412 // 2. ReturnIfAbrupt(O)
413 o_ = ToObject(context(), receiver());
414
415 // 3. Let len be ToLength(Get(O, "length")).
416 // 4. ReturnIfAbrupt(len).
417 TVARIABLE(Number, merged_length);
418 Label has_length(this, &merged_length), not_js_array(this);
419 GotoIf(DoesntHaveInstanceType(o(), JS_ARRAY_TYPE), ¬_js_array);
420 merged_length = LoadJSArrayLength(CAST(o()));
421 Goto(&has_length);
422 BIND(¬_js_array);
423 Node* len_property =
424 GetProperty(context(), o(), isolate()->factory()->length_string());
425 merged_length = ToLength_Inline(context(), len_property);
426 Goto(&has_length);
427 BIND(&has_length);
428 len_ = merged_length.value();
429
430 // 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
431 Label type_exception(this, Label::kDeferred);
432 Label done(this);
433 GotoIf(TaggedIsSmi(callbackfn()), &type_exception);
434 Branch(IsCallableMap(LoadMap(callbackfn())), &done, &type_exception);
435
436 BIND(&throw_null_undefined_exception);
437 ThrowTypeError(context(), MessageTemplate::kCalledOnNullOrUndefined, name);
438
439 BIND(&type_exception);
440 ThrowTypeError(context(), MessageTemplate::kCalledNonCallable,
441 callbackfn());
442
443 BIND(&done);
444
445 // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
446 // [Already done by the arguments adapter]
447
448 if (direction == ForEachDirection::kForward) {
449 // 7. Let k be 0.
450 k_.Bind(SmiConstant(0));
451 } else {
452 k_.Bind(NumberDec(len()));
453 }
454
455 generator(this);
456
457 HandleFastElements(processor, action, &fully_spec_compliant_, direction,
458 missing_property_mode);
459
460 BIND(&fully_spec_compliant_);
461
462 Node* result =
463 CallStub(slow_case_continuation, context(), receiver(), callbackfn(),
464 this_arg(), a_.value(), o(), k_.value(), len(), to_.value());
465 ReturnFromBuiltin(result);
466 }
467
InitIteratingArrayBuiltinLoopContinuation(TNode<Context> context,TNode<Object> receiver,Node * callbackfn,Node * this_arg,Node * a,TNode<JSReceiver> o,Node * initial_k,TNode<Number> len,Node * to)468 void ArrayBuiltinsAssembler::InitIteratingArrayBuiltinLoopContinuation(
469 TNode<Context> context, TNode<Object> receiver, Node* callbackfn,
470 Node* this_arg, Node* a, TNode<JSReceiver> o, Node* initial_k,
471 TNode<Number> len, Node* to) {
472 context_ = context;
473 this_arg_ = this_arg;
474 callbackfn_ = callbackfn;
475 a_.Bind(a);
476 k_.Bind(initial_k);
477 o_ = o;
478 len_ = len;
479 to_.Bind(to);
480 }
481
GenerateIteratingTypedArrayBuiltinBody(const char * name,const BuiltinResultGenerator & generator,const CallResultProcessor & processor,const PostLoopAction & action,ForEachDirection direction)482 void ArrayBuiltinsAssembler::GenerateIteratingTypedArrayBuiltinBody(
483 const char* name, const BuiltinResultGenerator& generator,
484 const CallResultProcessor& processor, const PostLoopAction& action,
485 ForEachDirection direction) {
486 name_ = name;
487
488 // ValidateTypedArray: tc39.github.io/ecma262/#sec-validatetypedarray
489
490 Label throw_not_typed_array(this, Label::kDeferred),
491 throw_detached(this, Label::kDeferred);
492
493 GotoIf(TaggedIsSmi(receiver_), &throw_not_typed_array);
494 GotoIfNot(HasInstanceType(CAST(receiver_), JS_TYPED_ARRAY_TYPE),
495 &throw_not_typed_array);
496
497 TNode<JSTypedArray> typed_array = CAST(receiver_);
498 o_ = typed_array;
499
500 Node* array_buffer =
501 LoadObjectField(typed_array, JSTypedArray::kBufferOffset);
502 GotoIf(IsDetachedBuffer(array_buffer), &throw_detached);
503
504 len_ = LoadObjectField<Smi>(typed_array, JSTypedArray::kLengthOffset);
505
506 Label throw_not_callable(this, Label::kDeferred);
507 Label distinguish_types(this);
508 GotoIf(TaggedIsSmi(callbackfn_), &throw_not_callable);
509 Branch(IsCallableMap(LoadMap(callbackfn_)), &distinguish_types,
510 &throw_not_callable);
511
512 BIND(&throw_not_typed_array);
513 ThrowTypeError(context_, MessageTemplate::kNotTypedArray);
514
515 BIND(&throw_detached);
516 ThrowTypeError(context_, MessageTemplate::kDetachedOperation, name_);
517
518 BIND(&throw_not_callable);
519 ThrowTypeError(context_, MessageTemplate::kCalledNonCallable, callbackfn_);
520
521 Label unexpected_instance_type(this);
522 BIND(&unexpected_instance_type);
523 Unreachable();
524
525 std::vector<int32_t> instance_types = {
526 #define INSTANCE_TYPE(Type, type, TYPE, ctype, size) FIXED_##TYPE##_ARRAY_TYPE,
527 TYPED_ARRAYS(INSTANCE_TYPE)
528 #undef INSTANCE_TYPE
529 };
530 std::vector<Label> labels;
531 for (size_t i = 0; i < instance_types.size(); ++i) {
532 labels.push_back(Label(this));
533 }
534 std::vector<Label*> label_ptrs;
535 for (Label& label : labels) {
536 label_ptrs.push_back(&label);
537 }
538
539 BIND(&distinguish_types);
540
541 generator(this);
542
543 if (direction == ForEachDirection::kForward) {
544 k_.Bind(SmiConstant(0));
545 } else {
546 k_.Bind(NumberDec(len()));
547 }
548 Node* instance_type = LoadInstanceType(LoadElements(typed_array));
549 Switch(instance_type, &unexpected_instance_type, instance_types.data(),
550 label_ptrs.data(), labels.size());
551
552 for (size_t i = 0; i < labels.size(); ++i) {
553 BIND(&labels[i]);
554 Label done(this);
555 source_elements_kind_ = ElementsKindForInstanceType(
556 static_cast<InstanceType>(instance_types[i]));
557 // TODO(tebbi): Silently cancelling the loop on buffer detachment is a
558 // spec violation. Should go to &throw_detached and throw a TypeError
559 // instead.
560 VisitAllTypedArrayElements(array_buffer, processor, &done, direction,
561 typed_array);
562 Goto(&done);
563 // No exception, return success
564 BIND(&done);
565 action(this);
566 ReturnFromBuiltin(a_.value());
567 }
568 }
569
GenerateIteratingArrayBuiltinLoopContinuation(const CallResultProcessor & processor,const PostLoopAction & action,MissingPropertyMode missing_property_mode,ForEachDirection direction)570 void ArrayBuiltinsAssembler::GenerateIteratingArrayBuiltinLoopContinuation(
571 const CallResultProcessor& processor, const PostLoopAction& action,
572 MissingPropertyMode missing_property_mode, ForEachDirection direction) {
573 Label loop(this, {&k_, &a_, &to_});
574 Label after_loop(this);
575 Goto(&loop);
576 BIND(&loop);
577 {
578 if (direction == ForEachDirection::kForward) {
579 // 8. Repeat, while k < len
580 GotoIfNumberGreaterThanOrEqual(k(), len_, &after_loop);
581 } else {
582 // OR
583 // 10. Repeat, while k >= 0
584 GotoIfNumberGreaterThanOrEqual(SmiConstant(-1), k(), &after_loop);
585 }
586
587 Label done_element(this, &to_);
588 // a. Let Pk be ToString(k).
589 // We never have to perform a ToString conversion as the above guards
590 // guarantee that we have a positive {k} which also is a valid array
591 // index in the range [0, 2^32-1).
592 CSA_ASSERT(this, IsNumberArrayIndex(k()));
593
594 if (missing_property_mode == MissingPropertyMode::kSkip) {
595 // b. Let kPresent be HasProperty(O, Pk).
596 // c. ReturnIfAbrupt(kPresent).
597 TNode<Oddball> k_present =
598 HasProperty(o(), k(), context(), kHasProperty);
599
600 // d. If kPresent is true, then
601 GotoIf(IsFalse(k_present), &done_element);
602 }
603
604 // i. Let kValue be Get(O, Pk).
605 // ii. ReturnIfAbrupt(kValue).
606 Node* k_value = GetProperty(context(), o(), k());
607
608 // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»).
609 // iv. ReturnIfAbrupt(funcResult).
610 a_.Bind(processor(this, k_value, k()));
611 Goto(&done_element);
612
613 BIND(&done_element);
614
615 if (direction == ForEachDirection::kForward) {
616 // e. Increase k by 1.
617 k_.Bind(NumberInc(k()));
618 } else {
619 // e. Decrease k by 1.
620 k_.Bind(NumberDec(k()));
621 }
622 Goto(&loop);
623 }
624 BIND(&after_loop);
625
626 action(this);
627 Return(a_.value());
628 }
629
ElementsKindForInstanceType(InstanceType type)630 ElementsKind ArrayBuiltinsAssembler::ElementsKindForInstanceType(
631 InstanceType type) {
632 switch (type) {
633 #define INSTANCE_TYPE_TO_ELEMENTS_KIND(Type, type, TYPE, ctype, size) \
634 case FIXED_##TYPE##_ARRAY_TYPE: \
635 return TYPE##_ELEMENTS;
636
637 TYPED_ARRAYS(INSTANCE_TYPE_TO_ELEMENTS_KIND)
638 #undef INSTANCE_TYPE_TO_ELEMENTS_KIND
639
640 default:
641 UNREACHABLE();
642 }
643 }
644
VisitAllTypedArrayElements(Node * array_buffer,const CallResultProcessor & processor,Label * detached,ForEachDirection direction,TNode<JSTypedArray> typed_array)645 void ArrayBuiltinsAssembler::VisitAllTypedArrayElements(
646 Node* array_buffer, const CallResultProcessor& processor, Label* detached,
647 ForEachDirection direction, TNode<JSTypedArray> typed_array) {
648 VariableList list({&a_, &k_, &to_}, zone());
649
650 FastLoopBody body = [&](Node* index) {
651 GotoIf(IsDetachedBuffer(array_buffer), detached);
652 Node* elements = LoadElements(typed_array);
653 Node* base_ptr =
654 LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset);
655 Node* external_ptr =
656 LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset,
657 MachineType::Pointer());
658 Node* data_ptr = IntPtrAdd(BitcastTaggedToWord(base_ptr), external_ptr);
659 Node* value = LoadFixedTypedArrayElementAsTagged(
660 data_ptr, index, source_elements_kind_, SMI_PARAMETERS);
661 k_.Bind(index);
662 a_.Bind(processor(this, value, index));
663 };
664 Node* start = SmiConstant(0);
665 Node* end = len_;
666 IndexAdvanceMode advance_mode = IndexAdvanceMode::kPost;
667 int incr = 1;
668 if (direction == ForEachDirection::kReverse) {
669 std::swap(start, end);
670 advance_mode = IndexAdvanceMode::kPre;
671 incr = -1;
672 }
673 BuildFastLoop(list, start, end, body, incr, ParameterMode::SMI_PARAMETERS,
674 advance_mode);
675 }
676
VisitAllFastElementsOneKind(ElementsKind kind,const CallResultProcessor & processor,Label * array_changed,ParameterMode mode,ForEachDirection direction,MissingPropertyMode missing_property_mode,TNode<Smi> length)677 void ArrayBuiltinsAssembler::VisitAllFastElementsOneKind(
678 ElementsKind kind, const CallResultProcessor& processor,
679 Label* array_changed, ParameterMode mode, ForEachDirection direction,
680 MissingPropertyMode missing_property_mode, TNode<Smi> length) {
681 Comment("begin VisitAllFastElementsOneKind");
682 // We only use this kind of processing if the no-elements protector is
683 // in place at the start. We'll continue checking during array iteration.
684 CSA_ASSERT(this, Word32BinaryNot(IsNoElementsProtectorCellInvalid()));
685 VARIABLE(original_map, MachineRepresentation::kTagged);
686 original_map.Bind(LoadMap(o()));
687 VariableList list({&original_map, &a_, &k_, &to_}, zone());
688 Node* start = IntPtrOrSmiConstant(0, mode);
689 Node* end = TaggedToParameter(length, mode);
690 IndexAdvanceMode advance_mode = direction == ForEachDirection::kReverse
691 ? IndexAdvanceMode::kPre
692 : IndexAdvanceMode::kPost;
693 if (direction == ForEachDirection::kReverse) std::swap(start, end);
694 BuildFastLoop(
695 list, start, end,
696 [=, &original_map](Node* index) {
697 k_.Bind(ParameterToTagged(index, mode));
698 Label one_element_done(this), hole_element(this),
699 process_element(this);
700
701 // Check if o's map has changed during the callback. If so, we have to
702 // fall back to the slower spec implementation for the rest of the
703 // iteration.
704 Node* o_map = LoadMap(o());
705 GotoIf(WordNotEqual(o_map, original_map.value()), array_changed);
706
707 TNode<JSArray> o_array = CAST(o());
708 // Check if o's length has changed during the callback and if the
709 // index is now out of range of the new length.
710 GotoIf(SmiGreaterThanOrEqual(CAST(k_.value()),
711 CAST(LoadJSArrayLength(o_array))),
712 array_changed);
713
714 // Re-load the elements array. If may have been resized.
715 Node* elements = LoadElements(o_array);
716
717 // Fast case: load the element directly from the elements FixedArray
718 // and call the callback if the element is not the hole.
719 DCHECK(kind == PACKED_ELEMENTS || kind == PACKED_DOUBLE_ELEMENTS);
720 int base_size = kind == PACKED_ELEMENTS
721 ? FixedArray::kHeaderSize
722 : (FixedArray::kHeaderSize - kHeapObjectTag);
723 Node* offset = ElementOffsetFromIndex(index, kind, mode, base_size);
724 VARIABLE(value, MachineRepresentation::kTagged);
725 if (kind == PACKED_ELEMENTS) {
726 value.Bind(LoadObjectField(elements, offset));
727 GotoIf(WordEqual(value.value(), TheHoleConstant()), &hole_element);
728 } else {
729 Node* double_value =
730 LoadDoubleWithHoleCheck(elements, offset, &hole_element);
731 value.Bind(AllocateHeapNumberWithValue(double_value));
732 }
733 Goto(&process_element);
734
735 BIND(&hole_element);
736 if (missing_property_mode == MissingPropertyMode::kSkip) {
737 // The NoElementsProtectorCell could go invalid during callbacks.
738 Branch(IsNoElementsProtectorCellInvalid(), array_changed,
739 &one_element_done);
740 } else {
741 value.Bind(UndefinedConstant());
742 Goto(&process_element);
743 }
744 BIND(&process_element);
745 {
746 a_.Bind(processor(this, value.value(), k()));
747 Goto(&one_element_done);
748 }
749 BIND(&one_element_done);
750 },
751 1, mode, advance_mode);
752 Comment("end VisitAllFastElementsOneKind");
753 }
754
HandleFastElements(const CallResultProcessor & processor,const PostLoopAction & action,Label * slow,ForEachDirection direction,MissingPropertyMode missing_property_mode)755 void ArrayBuiltinsAssembler::HandleFastElements(
756 const CallResultProcessor& processor, const PostLoopAction& action,
757 Label* slow, ForEachDirection direction,
758 MissingPropertyMode missing_property_mode) {
759 Label switch_on_elements_kind(this), fast_elements(this),
760 maybe_double_elements(this), fast_double_elements(this);
761
762 Comment("begin HandleFastElements");
763 // Non-smi lengths must use the slow path.
764 GotoIf(TaggedIsNotSmi(len()), slow);
765
766 BranchIfFastJSArray(o(), context(),
767 &switch_on_elements_kind, slow);
768
769 BIND(&switch_on_elements_kind);
770 TNode<Smi> smi_len = CAST(len());
771 // Select by ElementsKind
772 Node* o_map = LoadMap(o());
773 Node* bit_field2 = LoadMapBitField2(o_map);
774 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
775 Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS),
776 &maybe_double_elements, &fast_elements);
777
778 ParameterMode mode = OptimalParameterMode();
779 BIND(&fast_elements);
780 {
781 VisitAllFastElementsOneKind(PACKED_ELEMENTS, processor, slow, mode,
782 direction, missing_property_mode, smi_len);
783
784 action(this);
785
786 // No exception, return success
787 ReturnFromBuiltin(a_.value());
788 }
789
790 BIND(&maybe_double_elements);
791 Branch(IsElementsKindGreaterThan(kind, HOLEY_DOUBLE_ELEMENTS), slow,
792 &fast_double_elements);
793
794 BIND(&fast_double_elements);
795 {
796 VisitAllFastElementsOneKind(PACKED_DOUBLE_ELEMENTS, processor, slow, mode,
797 direction, missing_property_mode, smi_len);
798
799 action(this);
800
801 // No exception, return success
802 ReturnFromBuiltin(a_.value());
803 }
804 }
805
806 // Perform ArraySpeciesCreate (ES6 #sec-arrayspeciescreate).
807 // This version is specialized to create a zero length array
808 // of the elements kind of the input array.
GenerateArraySpeciesCreate()809 void ArrayBuiltinsAssembler::GenerateArraySpeciesCreate() {
810 Label runtime(this, Label::kDeferred), done(this);
811
812 TNode<Smi> len = SmiConstant(0);
813 TNode<Map> original_map = LoadMap(o());
814 GotoIfNot(
815 InstanceTypeEqual(LoadMapInstanceType(original_map), JS_ARRAY_TYPE),
816 &runtime);
817
818 GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map),
819 &runtime);
820
821 Node* species_protector = ArraySpeciesProtectorConstant();
822 Node* value =
823 LoadObjectField(species_protector, PropertyCell::kValueOffset);
824 TNode<Smi> const protector_invalid =
825 SmiConstant(Isolate::kProtectorInvalid);
826 GotoIf(WordEqual(value, protector_invalid), &runtime);
827
828 // Respect the ElementsKind of the input array.
829 TNode<Int32T> elements_kind = LoadMapElementsKind(original_map);
830 GotoIfNot(IsFastElementsKind(elements_kind), &runtime);
831 TNode<Context> native_context = LoadNativeContext(context());
832 TNode<Map> array_map =
833 LoadJSArrayElementsMap(elements_kind, native_context);
834 TNode<JSArray> array =
835 CAST(AllocateJSArray(GetInitialFastElementsKind(), array_map, len, len,
836 nullptr, CodeStubAssembler::SMI_PARAMETERS));
837 a_.Bind(array);
838
839 Goto(&done);
840
841 BIND(&runtime);
842 {
843 // 5. Let A be ? ArraySpeciesCreate(O, len).
844 Node* constructor =
845 CallRuntime(Runtime::kArraySpeciesConstructor, context(), o());
846 a_.Bind(ConstructJS(CodeFactory::Construct(isolate()), context(),
847 constructor, len));
848 Goto(&fully_spec_compliant_);
849 }
850
851 BIND(&done);
852 }
853
854 // Perform ArraySpeciesCreate (ES6 #sec-arrayspeciescreate).
GenerateArraySpeciesCreate(TNode<Number> len)855 void ArrayBuiltinsAssembler::GenerateArraySpeciesCreate(TNode<Number> len) {
856 Label runtime(this, Label::kDeferred), done(this);
857
858 Node* const original_map = LoadMap(o());
859 GotoIfNot(
860 InstanceTypeEqual(LoadMapInstanceType(original_map), JS_ARRAY_TYPE),
861 &runtime);
862
863 GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map),
864 &runtime);
865
866 Node* species_protector = ArraySpeciesProtectorConstant();
867 Node* value =
868 LoadObjectField(species_protector, PropertyCell::kValueOffset);
869 Node* const protector_invalid = SmiConstant(Isolate::kProtectorInvalid);
870 GotoIf(WordEqual(value, protector_invalid), &runtime);
871
872 GotoIfNot(TaggedIsPositiveSmi(len), &runtime);
873 GotoIf(
874 SmiAbove(CAST(len), SmiConstant(JSArray::kInitialMaxFastElementArray)),
875 &runtime);
876
877 // We need to be conservative and start with holey because the builtins
878 // that create output arrays aren't guaranteed to be called for every
879 // element in the input array (maybe the callback deletes an element).
880 const ElementsKind elements_kind =
881 GetHoleyElementsKind(GetInitialFastElementsKind());
882 TNode<Context> native_context = LoadNativeContext(context());
883 TNode<Map> array_map =
884 LoadJSArrayElementsMap(elements_kind, native_context);
885 a_.Bind(AllocateJSArray(PACKED_SMI_ELEMENTS, array_map, len, len, nullptr,
886 CodeStubAssembler::SMI_PARAMETERS));
887
888 Goto(&done);
889
890 BIND(&runtime);
891 {
892 // 5. Let A be ? ArraySpeciesCreate(O, len).
893 Node* constructor =
894 CallRuntime(Runtime::kArraySpeciesConstructor, context(), o());
895 a_.Bind(ConstructJS(CodeFactory::Construct(isolate()), context(),
896 constructor, len));
897 Goto(&fully_spec_compliant_);
898 }
899
900 BIND(&done);
901 }
902
TF_BUILTIN(ArrayPrototypePop,CodeStubAssembler)903 TF_BUILTIN(ArrayPrototypePop, CodeStubAssembler) {
904 TNode<Int32T> argc =
905 UncheckedCast<Int32T>(Parameter(BuiltinDescriptor::kArgumentsCount));
906 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
907 CSA_ASSERT(this, IsUndefined(Parameter(BuiltinDescriptor::kNewTarget)));
908
909 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
910 TNode<Object> receiver = args.GetReceiver();
911
912 Label runtime(this, Label::kDeferred);
913 Label fast(this);
914
915 // Only pop in this stub if
916 // 1) the array has fast elements
917 // 2) the length is writable,
918 // 3) the elements backing store isn't copy-on-write,
919 // 4) we aren't supposed to shrink the backing store.
920
921 // 1) Check that the array has fast elements.
922 BranchIfFastJSArray(receiver, context, &fast, &runtime);
923
924 BIND(&fast);
925 {
926 TNode<JSArray> array_receiver = CAST(receiver);
927 CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array_receiver)));
928 Node* length =
929 LoadAndUntagObjectField(array_receiver, JSArray::kLengthOffset);
930 Label return_undefined(this), fast_elements(this);
931 GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &return_undefined);
932
933 // 2) Ensure that the length is writable.
934 EnsureArrayLengthWritable(LoadMap(array_receiver), &runtime);
935
936 // 3) Check that the elements backing store isn't copy-on-write.
937 Node* elements = LoadElements(array_receiver);
938 GotoIf(WordEqual(LoadMap(elements),
939 LoadRoot(Heap::kFixedCOWArrayMapRootIndex)),
940 &runtime);
941
942 Node* new_length = IntPtrSub(length, IntPtrConstant(1));
943
944 // 4) Check that we're not supposed to shrink the backing store, as
945 // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.
946 Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
947 GotoIf(IntPtrLessThan(
948 IntPtrAdd(IntPtrAdd(new_length, new_length),
949 IntPtrConstant(JSObject::kMinAddedElementsCapacity)),
950 capacity),
951 &runtime);
952
953 StoreObjectFieldNoWriteBarrier(array_receiver, JSArray::kLengthOffset,
954 SmiTag(new_length));
955
956 Node* elements_kind = LoadMapElementsKind(LoadMap(array_receiver));
957 GotoIf(Int32LessThanOrEqual(elements_kind,
958 Int32Constant(TERMINAL_FAST_ELEMENTS_KIND)),
959 &fast_elements);
960
961 Node* value = LoadFixedDoubleArrayElement(
962 elements, new_length, MachineType::Float64(), 0, INTPTR_PARAMETERS,
963 &return_undefined);
964
965 int32_t header_size = FixedDoubleArray::kHeaderSize - kHeapObjectTag;
966 Node* offset = ElementOffsetFromIndex(new_length, HOLEY_DOUBLE_ELEMENTS,
967 INTPTR_PARAMETERS, header_size);
968 if (Is64()) {
969 Node* double_hole = Int64Constant(kHoleNanInt64);
970 StoreNoWriteBarrier(MachineRepresentation::kWord64, elements, offset,
971 double_hole);
972 } else {
973 STATIC_ASSERT(kHoleNanLower32 == kHoleNanUpper32);
974 Node* double_hole = Int32Constant(kHoleNanLower32);
975 StoreNoWriteBarrier(MachineRepresentation::kWord32, elements, offset,
976 double_hole);
977 StoreNoWriteBarrier(MachineRepresentation::kWord32, elements,
978 IntPtrAdd(offset, IntPtrConstant(kPointerSize)),
979 double_hole);
980 }
981 args.PopAndReturn(AllocateHeapNumberWithValue(value));
982
983 BIND(&fast_elements);
984 {
985 Node* value = LoadFixedArrayElement(elements, new_length);
986 StoreFixedArrayElement(elements, new_length, TheHoleConstant());
987 GotoIf(WordEqual(value, TheHoleConstant()), &return_undefined);
988 args.PopAndReturn(value);
989 }
990
991 BIND(&return_undefined);
992 { args.PopAndReturn(UndefinedConstant()); }
993 }
994
995 BIND(&runtime);
996 {
997 Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
998 MachineType::TaggedPointer());
999 TailCallStub(CodeFactory::ArrayPop(isolate()), context, target,
1000 UndefinedConstant(), argc);
1001 }
1002 }
1003
TF_BUILTIN(ArrayPrototypePush,CodeStubAssembler)1004 TF_BUILTIN(ArrayPrototypePush, CodeStubAssembler) {
1005 TVARIABLE(IntPtrT, arg_index);
1006 Label default_label(this, &arg_index);
1007 Label smi_transition(this);
1008 Label object_push_pre(this);
1009 Label object_push(this, &arg_index);
1010 Label double_push(this, &arg_index);
1011 Label double_transition(this);
1012 Label runtime(this, Label::kDeferred);
1013
1014 // TODO(ishell): use constants from Descriptor once the JSFunction linkage
1015 // arguments are reordered.
1016 TNode<Int32T> argc =
1017 UncheckedCast<Int32T>(Parameter(BuiltinDescriptor::kArgumentsCount));
1018 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1019 CSA_ASSERT(this, IsUndefined(Parameter(BuiltinDescriptor::kNewTarget)));
1020
1021 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
1022 TNode<Object> receiver = args.GetReceiver();
1023 TNode<JSArray> array_receiver;
1024 Node* kind = nullptr;
1025
1026 Label fast(this);
1027 BranchIfFastJSArray(receiver, context, &fast, &runtime);
1028
1029 BIND(&fast);
1030 {
1031 array_receiver = CAST(receiver);
1032 arg_index = IntPtrConstant(0);
1033 kind = EnsureArrayPushable(LoadMap(array_receiver), &runtime);
1034 GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS),
1035 &object_push_pre);
1036
1037 Node* new_length = BuildAppendJSArray(PACKED_SMI_ELEMENTS, array_receiver,
1038 &args, &arg_index, &smi_transition);
1039 args.PopAndReturn(new_length);
1040 }
1041
1042 // If the argument is not a smi, then use a heavyweight SetProperty to
1043 // transition the array for only the single next element. If the argument is
1044 // a smi, the failure is due to some other reason and we should fall back on
1045 // the most generic implementation for the rest of the array.
1046 BIND(&smi_transition);
1047 {
1048 Node* arg = args.AtIndex(arg_index.value());
1049 GotoIf(TaggedIsSmi(arg), &default_label);
1050 Node* length = LoadJSArrayLength(array_receiver);
1051 // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
1052 // calling into the runtime to do the elements transition is overkill.
1053 CallRuntime(Runtime::kSetProperty, context, array_receiver, length, arg,
1054 SmiConstant(LanguageMode::kStrict));
1055 Increment(&arg_index);
1056 // The runtime SetProperty call could have converted the array to dictionary
1057 // mode, which must be detected to abort the fast-path.
1058 Node* map = LoadMap(array_receiver);
1059 Node* bit_field2 = LoadMapBitField2(map);
1060 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
1061 GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)),
1062 &default_label);
1063
1064 GotoIfNotNumber(arg, &object_push);
1065 Goto(&double_push);
1066 }
1067
1068 BIND(&object_push_pre);
1069 {
1070 Branch(IsElementsKindGreaterThan(kind, HOLEY_ELEMENTS), &double_push,
1071 &object_push);
1072 }
1073
1074 BIND(&object_push);
1075 {
1076 Node* new_length = BuildAppendJSArray(PACKED_ELEMENTS, array_receiver,
1077 &args, &arg_index, &default_label);
1078 args.PopAndReturn(new_length);
1079 }
1080
1081 BIND(&double_push);
1082 {
1083 Node* new_length =
1084 BuildAppendJSArray(PACKED_DOUBLE_ELEMENTS, array_receiver, &args,
1085 &arg_index, &double_transition);
1086 args.PopAndReturn(new_length);
1087 }
1088
1089 // If the argument is not a double, then use a heavyweight SetProperty to
1090 // transition the array for only the single next element. If the argument is
1091 // a double, the failure is due to some other reason and we should fall back
1092 // on the most generic implementation for the rest of the array.
1093 BIND(&double_transition);
1094 {
1095 Node* arg = args.AtIndex(arg_index.value());
1096 GotoIfNumber(arg, &default_label);
1097 Node* length = LoadJSArrayLength(array_receiver);
1098 // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
1099 // calling into the runtime to do the elements transition is overkill.
1100 CallRuntime(Runtime::kSetProperty, context, array_receiver, length, arg,
1101 SmiConstant(LanguageMode::kStrict));
1102 Increment(&arg_index);
1103 // The runtime SetProperty call could have converted the array to dictionary
1104 // mode, which must be detected to abort the fast-path.
1105 Node* map = LoadMap(array_receiver);
1106 Node* bit_field2 = LoadMapBitField2(map);
1107 Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
1108 GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)),
1109 &default_label);
1110 Goto(&object_push);
1111 }
1112
1113 // Fallback that stores un-processed arguments using the full, heavyweight
1114 // SetProperty machinery.
1115 BIND(&default_label);
1116 {
1117 args.ForEach(
1118 [this, array_receiver, context](Node* arg) {
1119 Node* length = LoadJSArrayLength(array_receiver);
1120 CallRuntime(Runtime::kSetProperty, context, array_receiver, length,
1121 arg, SmiConstant(LanguageMode::kStrict));
1122 },
1123 arg_index.value());
1124 args.PopAndReturn(LoadJSArrayLength(array_receiver));
1125 }
1126
1127 BIND(&runtime);
1128 {
1129 Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
1130 MachineType::TaggedPointer());
1131 TailCallStub(CodeFactory::ArrayPush(isolate()), context, target,
1132 UndefinedConstant(), argc);
1133 }
1134 }
1135
1136 class ArrayPrototypeSliceCodeStubAssembler : public CodeStubAssembler {
1137 public:
ArrayPrototypeSliceCodeStubAssembler(compiler::CodeAssemblerState * state)1138 explicit ArrayPrototypeSliceCodeStubAssembler(
1139 compiler::CodeAssemblerState* state)
1140 : CodeStubAssembler(state) {}
1141
HandleFastSlice(TNode<Context> context,Node * array,Node * from,Node * count,Label * slow)1142 Node* HandleFastSlice(TNode<Context> context, Node* array, Node* from,
1143 Node* count, Label* slow) {
1144 VARIABLE(result, MachineRepresentation::kTagged);
1145 Label done(this);
1146
1147 GotoIf(TaggedIsNotSmi(from), slow);
1148 GotoIf(TaggedIsNotSmi(count), slow);
1149
1150 Label try_fast_arguments(this), try_simple_slice(this);
1151
1152 Node* map = LoadMap(array);
1153 GotoIfNot(IsJSArrayMap(map), &try_fast_arguments);
1154
1155 // Check prototype chain if receiver does not have packed elements
1156 GotoIfNot(IsPrototypeInitialArrayPrototype(context, map), slow);
1157
1158 GotoIf(IsNoElementsProtectorCellInvalid(), slow);
1159
1160 GotoIf(IsArraySpeciesProtectorCellInvalid(), slow);
1161
1162 // Bailout if receiver has slow elements.
1163 Node* elements_kind = LoadMapElementsKind(map);
1164 GotoIfNot(IsFastElementsKind(elements_kind), &try_simple_slice);
1165
1166 // Make sure that the length hasn't been changed by side-effect.
1167 Node* array_length = LoadJSArrayLength(array);
1168 GotoIf(TaggedIsNotSmi(array_length), slow);
1169 GotoIf(SmiAbove(SmiAdd(CAST(from), CAST(count)), CAST(array_length)), slow);
1170
1171 CSA_ASSERT(this, SmiGreaterThanOrEqual(CAST(from), SmiConstant(0)));
1172
1173 result.Bind(CallStub(CodeFactory::ExtractFastJSArray(isolate()), context,
1174 array, from, count));
1175 Goto(&done);
1176
1177 BIND(&try_fast_arguments);
1178
1179 Node* const native_context = LoadNativeContext(context);
1180 Node* const fast_aliasted_arguments_map = LoadContextElement(
1181 native_context, Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX);
1182 GotoIf(WordNotEqual(map, fast_aliasted_arguments_map), &try_simple_slice);
1183
1184 Node* sloppy_elements = LoadElements(array);
1185 TNode<Smi> sloppy_elements_length =
1186 LoadFixedArrayBaseLength(sloppy_elements);
1187 TNode<Smi> parameter_map_length =
1188 SmiSub(sloppy_elements_length,
1189 SmiConstant(SloppyArgumentsElements::kParameterMapStart));
1190 VARIABLE(index_out, MachineType::PointerRepresentation());
1191
1192 int max_fast_elements =
1193 (kMaxRegularHeapObjectSize - FixedArray::kHeaderSize - JSArray::kSize -
1194 AllocationMemento::kSize) /
1195 kPointerSize;
1196 GotoIf(SmiAboveOrEqual(CAST(count), SmiConstant(max_fast_elements)),
1197 &try_simple_slice);
1198
1199 GotoIf(SmiLessThan(CAST(from), SmiConstant(0)), slow);
1200
1201 TNode<Smi> end = SmiAdd(CAST(from), CAST(count));
1202
1203 Node* unmapped_elements = LoadFixedArrayElement(
1204 sloppy_elements, SloppyArgumentsElements::kArgumentsIndex);
1205 TNode<Smi> unmapped_elements_length =
1206 LoadFixedArrayBaseLength(unmapped_elements);
1207
1208 GotoIf(SmiAbove(end, unmapped_elements_length), slow);
1209
1210 Node* array_map = LoadJSArrayElementsMap(HOLEY_ELEMENTS, native_context);
1211 result.Bind(AllocateJSArray(HOLEY_ELEMENTS, array_map, count, count,
1212 nullptr, SMI_PARAMETERS));
1213
1214 index_out.Bind(IntPtrConstant(0));
1215 Node* result_elements = LoadElements(result.value());
1216 TNode<Smi> from_mapped = SmiMin(parameter_map_length, CAST(from));
1217 TNode<Smi> to = SmiMin(parameter_map_length, end);
1218 Node* arguments_context = LoadFixedArrayElement(
1219 sloppy_elements, SloppyArgumentsElements::kContextIndex);
1220 VariableList var_list({&index_out}, zone());
1221 BuildFastLoop(
1222 var_list, from_mapped, to,
1223 [this, result_elements, arguments_context, sloppy_elements,
1224 unmapped_elements, &index_out](Node* current) {
1225 Node* context_index = LoadFixedArrayElement(
1226 sloppy_elements, current,
1227 kPointerSize * SloppyArgumentsElements::kParameterMapStart,
1228 SMI_PARAMETERS);
1229 Label is_the_hole(this), done(this);
1230 GotoIf(IsTheHole(context_index), &is_the_hole);
1231 Node* mapped_argument =
1232 LoadContextElement(arguments_context, SmiUntag(context_index));
1233 StoreFixedArrayElement(result_elements, index_out.value(),
1234 mapped_argument, SKIP_WRITE_BARRIER);
1235 Goto(&done);
1236 BIND(&is_the_hole);
1237 Node* argument = LoadFixedArrayElement(unmapped_elements, current, 0,
1238 SMI_PARAMETERS);
1239 StoreFixedArrayElement(result_elements, index_out.value(), argument,
1240 SKIP_WRITE_BARRIER);
1241 Goto(&done);
1242 BIND(&done);
1243 index_out.Bind(IntPtrAdd(index_out.value(), IntPtrConstant(1)));
1244 },
1245 1, SMI_PARAMETERS, IndexAdvanceMode::kPost);
1246
1247 TNode<Smi> unmapped_from =
1248 SmiMin(SmiMax(parameter_map_length, CAST(from)), end);
1249
1250 BuildFastLoop(
1251 var_list, unmapped_from, end,
1252 [this, unmapped_elements, result_elements, &index_out](Node* current) {
1253 Node* argument = LoadFixedArrayElement(unmapped_elements, current, 0,
1254 SMI_PARAMETERS);
1255 StoreFixedArrayElement(result_elements, index_out.value(), argument,
1256 SKIP_WRITE_BARRIER);
1257 index_out.Bind(IntPtrAdd(index_out.value(), IntPtrConstant(1)));
1258 },
1259 1, SMI_PARAMETERS, IndexAdvanceMode::kPost);
1260
1261 Goto(&done);
1262
1263 BIND(&try_simple_slice);
1264 Node* simple_result = CallRuntime(Runtime::kTrySliceSimpleNonFastElements,
1265 context, array, from, count);
1266 GotoIfNumber(simple_result, slow);
1267 result.Bind(simple_result);
1268
1269 Goto(&done);
1270
1271 BIND(&done);
1272 return result.value();
1273 }
1274
CopyOneElement(TNode<Context> context,Node * o,Node * a,Node * p_k,Variable & n)1275 void CopyOneElement(TNode<Context> context, Node* o, Node* a, Node* p_k,
1276 Variable& n) {
1277 // b. Let kPresent be HasProperty(O, Pk).
1278 // c. ReturnIfAbrupt(kPresent).
1279 TNode<Oddball> k_present = HasProperty(o, p_k, context, kHasProperty);
1280
1281 // d. If kPresent is true, then
1282 Label done_element(this);
1283 GotoIf(IsFalse(k_present), &done_element);
1284
1285 // i. Let kValue be Get(O, Pk).
1286 // ii. ReturnIfAbrupt(kValue).
1287 Node* k_value = GetProperty(context, o, p_k);
1288
1289 // iii. Let status be CreateDataPropertyOrThrow(A, ToString(n), kValue).
1290 // iv. ReturnIfAbrupt(status).
1291 CallRuntime(Runtime::kCreateDataProperty, context, a, n.value(), k_value);
1292
1293 Goto(&done_element);
1294 BIND(&done_element);
1295 }
1296 };
1297
TF_BUILTIN(ArrayPrototypeSlice,ArrayPrototypeSliceCodeStubAssembler)1298 TF_BUILTIN(ArrayPrototypeSlice, ArrayPrototypeSliceCodeStubAssembler) {
1299 Node* const argc =
1300 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1301 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1302 Label slow(this, Label::kDeferred), fast_elements_kind(this);
1303
1304 CodeStubArguments args(this, argc);
1305 TNode<Object> receiver = args.GetReceiver();
1306
1307 TVARIABLE(JSReceiver, o);
1308 VARIABLE(len, MachineRepresentation::kTagged);
1309 Label length_done(this), generic_length(this), check_arguments_length(this),
1310 load_arguments_length(this);
1311
1312 GotoIf(TaggedIsSmi(receiver), &generic_length);
1313 GotoIfNot(IsJSArray(CAST(receiver)), &check_arguments_length);
1314
1315 TNode<JSArray> array_receiver = CAST(receiver);
1316 o = array_receiver;
1317 len.Bind(LoadJSArrayLength(array_receiver));
1318
1319 // Check for the array clone case. There can be no arguments to slice, the
1320 // array prototype chain must be intact and have no elements, the array has to
1321 // have fast elements.
1322 GotoIf(WordNotEqual(argc, IntPtrConstant(0)), &length_done);
1323
1324 Label clone(this);
1325 BranchIfFastJSArrayForCopy(receiver, context, &clone, &length_done);
1326 BIND(&clone);
1327
1328 args.PopAndReturn(
1329 CallStub(CodeFactory::CloneFastJSArray(isolate()), context, receiver));
1330
1331 BIND(&check_arguments_length);
1332
1333 Node* map = LoadMap(array_receiver);
1334 Node* native_context = LoadNativeContext(context);
1335 GotoIfContextElementEqual(map, native_context,
1336 Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX,
1337 &load_arguments_length);
1338 GotoIfContextElementEqual(map, native_context,
1339 Context::SLOW_ALIASED_ARGUMENTS_MAP_INDEX,
1340 &load_arguments_length);
1341 GotoIfContextElementEqual(map, native_context,
1342 Context::STRICT_ARGUMENTS_MAP_INDEX,
1343 &load_arguments_length);
1344 GotoIfContextElementEqual(map, native_context,
1345 Context::SLOPPY_ARGUMENTS_MAP_INDEX,
1346 &load_arguments_length);
1347
1348 Goto(&generic_length);
1349
1350 BIND(&load_arguments_length);
1351 Node* arguments_length =
1352 LoadObjectField(array_receiver, JSArgumentsObject::kLengthOffset);
1353 GotoIf(TaggedIsNotSmi(arguments_length), &generic_length);
1354 o = CAST(receiver);
1355 len.Bind(arguments_length);
1356 Goto(&length_done);
1357
1358 BIND(&generic_length);
1359 // 1. Let O be ToObject(this value).
1360 // 2. ReturnIfAbrupt(O).
1361 o = ToObject(context, receiver);
1362
1363 // 3. Let len be ToLength(Get(O, "length")).
1364 // 4. ReturnIfAbrupt(len).
1365 len.Bind(ToLength_Inline(
1366 context,
1367 GetProperty(context, o.value(), isolate()->factory()->length_string())));
1368 Goto(&length_done);
1369
1370 BIND(&length_done);
1371
1372 // 5. Let relativeStart be ToInteger(start).
1373 // 6. ReturnIfAbrupt(relativeStart).
1374 TNode<Object> arg0 = args.GetOptionalArgumentValue(0, SmiConstant(0));
1375 Node* relative_start = ToInteger_Inline(context, arg0);
1376
1377 // 7. If relativeStart < 0, let k be max((len + relativeStart),0);
1378 // else let k be min(relativeStart, len.value()).
1379 VARIABLE(k, MachineRepresentation::kTagged);
1380 Label relative_start_positive(this), relative_start_done(this);
1381 GotoIfNumberGreaterThanOrEqual(relative_start, SmiConstant(0),
1382 &relative_start_positive);
1383 k.Bind(NumberMax(NumberAdd(len.value(), relative_start), NumberConstant(0)));
1384 Goto(&relative_start_done);
1385 BIND(&relative_start_positive);
1386 k.Bind(NumberMin(relative_start, len.value()));
1387 Goto(&relative_start_done);
1388 BIND(&relative_start_done);
1389
1390 // 8. If end is undefined, let relativeEnd be len;
1391 // else let relativeEnd be ToInteger(end).
1392 // 9. ReturnIfAbrupt(relativeEnd).
1393 TNode<Object> end = args.GetOptionalArgumentValue(1, UndefinedConstant());
1394 Label end_undefined(this), end_done(this);
1395 VARIABLE(relative_end, MachineRepresentation::kTagged);
1396 GotoIf(WordEqual(end, UndefinedConstant()), &end_undefined);
1397 relative_end.Bind(ToInteger_Inline(context, end));
1398 Goto(&end_done);
1399 BIND(&end_undefined);
1400 relative_end.Bind(len.value());
1401 Goto(&end_done);
1402 BIND(&end_done);
1403
1404 // 10. If relativeEnd < 0, let final be max((len + relativeEnd),0);
1405 // else let final be min(relativeEnd, len).
1406 VARIABLE(final, MachineRepresentation::kTagged);
1407 Label relative_end_positive(this), relative_end_done(this);
1408 GotoIfNumberGreaterThanOrEqual(relative_end.value(), NumberConstant(0),
1409 &relative_end_positive);
1410 final.Bind(NumberMax(NumberAdd(len.value(), relative_end.value()),
1411 NumberConstant(0)));
1412 Goto(&relative_end_done);
1413 BIND(&relative_end_positive);
1414 final.Bind(NumberMin(relative_end.value(), len.value()));
1415 Goto(&relative_end_done);
1416 BIND(&relative_end_done);
1417
1418 // 11. Let count be max(final – k, 0).
1419 Node* count =
1420 NumberMax(NumberSub(final.value(), k.value()), NumberConstant(0));
1421
1422 // Handle FAST_ELEMENTS
1423 Label non_fast(this);
1424 Node* fast_result =
1425 HandleFastSlice(context, o.value(), k.value(), count, &non_fast);
1426 args.PopAndReturn(fast_result);
1427
1428 // 12. Let A be ArraySpeciesCreate(O, count).
1429 // 13. ReturnIfAbrupt(A).
1430 BIND(&non_fast);
1431
1432 Node* constructor =
1433 CallRuntime(Runtime::kArraySpeciesConstructor, context, o.value());
1434 Node* a = ConstructJS(CodeFactory::Construct(isolate()), context, constructor,
1435 count);
1436
1437 // 14. Let n be 0.
1438 VARIABLE(n, MachineRepresentation::kTagged);
1439 n.Bind(SmiConstant(0));
1440
1441 Label loop(this, {&k, &n});
1442 Label after_loop(this);
1443 Goto(&loop);
1444 BIND(&loop);
1445 {
1446 // 15. Repeat, while k < final
1447 GotoIfNumberGreaterThanOrEqual(k.value(), final.value(), &after_loop);
1448
1449 Node* p_k = k.value(); // ToString(context, k.value()) is no-op
1450
1451 CopyOneElement(context, o.value(), a, p_k, n);
1452
1453 // e. Increase k by 1.
1454 k.Bind(NumberInc(k.value()));
1455
1456 // f. Increase n by 1.
1457 n.Bind(NumberInc(n.value()));
1458
1459 Goto(&loop);
1460 }
1461
1462 BIND(&after_loop);
1463
1464 // 16. Let setStatus be Set(A, "length", n, true).
1465 // 17. ReturnIfAbrupt(setStatus).
1466 CallRuntime(Runtime::kSetProperty, context, a,
1467 HeapConstant(isolate()->factory()->length_string()), n.value(),
1468 SmiConstant(static_cast<int>(LanguageMode::kStrict)));
1469
1470 args.PopAndReturn(a);
1471 }
1472
TF_BUILTIN(ArrayPrototypeShift,CodeStubAssembler)1473 TF_BUILTIN(ArrayPrototypeShift, CodeStubAssembler) {
1474 TNode<Int32T> argc =
1475 UncheckedCast<Int32T>(Parameter(BuiltinDescriptor::kArgumentsCount));
1476 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1477 CSA_ASSERT(this, IsUndefined(Parameter(BuiltinDescriptor::kNewTarget)));
1478
1479 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
1480 TNode<Object> receiver = args.GetReceiver();
1481
1482 Label runtime(this, Label::kDeferred);
1483 Label fast(this);
1484
1485 // Only shift in this stub if
1486 // 1) the array has fast elements
1487 // 2) the length is writable,
1488 // 3) the elements backing store isn't copy-on-write,
1489 // 4) we aren't supposed to shrink the backing store,
1490 // 5) we aren't supposed to left-trim the backing store.
1491
1492 // 1) Check that the array has fast elements.
1493 BranchIfFastJSArray(receiver, context, &fast, &runtime);
1494
1495 BIND(&fast);
1496 {
1497 TNode<JSArray> array_receiver = CAST(receiver);
1498 CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array_receiver)));
1499 Node* length =
1500 LoadAndUntagObjectField(array_receiver, JSArray::kLengthOffset);
1501 Label return_undefined(this), fast_elements_tagged(this),
1502 fast_elements_smi(this);
1503 GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &return_undefined);
1504
1505 // 2) Ensure that the length is writable.
1506 EnsureArrayLengthWritable(LoadMap(array_receiver), &runtime);
1507
1508 // 3) Check that the elements backing store isn't copy-on-write.
1509 Node* elements = LoadElements(array_receiver);
1510 GotoIf(WordEqual(LoadMap(elements),
1511 LoadRoot(Heap::kFixedCOWArrayMapRootIndex)),
1512 &runtime);
1513
1514 Node* new_length = IntPtrSub(length, IntPtrConstant(1));
1515
1516 // 4) Check that we're not supposed to right-trim the backing store, as
1517 // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.
1518 Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
1519 GotoIf(IntPtrLessThan(
1520 IntPtrAdd(IntPtrAdd(new_length, new_length),
1521 IntPtrConstant(JSObject::kMinAddedElementsCapacity)),
1522 capacity),
1523 &runtime);
1524
1525 // 5) Check that we're not supposed to left-trim the backing store, as
1526 // implemented in elements.cc:FastElementsAccessor::MoveElements.
1527 GotoIf(IntPtrGreaterThan(new_length,
1528 IntPtrConstant(JSArray::kMaxCopyElements)),
1529 &runtime);
1530
1531 StoreObjectFieldNoWriteBarrier(array_receiver, JSArray::kLengthOffset,
1532 SmiTag(new_length));
1533
1534 Node* elements_kind = LoadMapElementsKind(LoadMap(array_receiver));
1535 GotoIf(
1536 Int32LessThanOrEqual(elements_kind, Int32Constant(HOLEY_SMI_ELEMENTS)),
1537 &fast_elements_smi);
1538 GotoIf(Int32LessThanOrEqual(elements_kind, Int32Constant(HOLEY_ELEMENTS)),
1539 &fast_elements_tagged);
1540
1541 // Fast double elements kind:
1542 {
1543 CSA_ASSERT(this,
1544 Int32LessThanOrEqual(elements_kind,
1545 Int32Constant(HOLEY_DOUBLE_ELEMENTS)));
1546
1547 VARIABLE(result, MachineRepresentation::kTagged, UndefinedConstant());
1548
1549 Label move_elements(this);
1550 result.Bind(AllocateHeapNumberWithValue(LoadFixedDoubleArrayElement(
1551 elements, IntPtrConstant(0), MachineType::Float64(), 0,
1552 INTPTR_PARAMETERS, &move_elements)));
1553 Goto(&move_elements);
1554 BIND(&move_elements);
1555
1556 int32_t header_size = FixedDoubleArray::kHeaderSize - kHeapObjectTag;
1557 Node* memmove =
1558 ExternalConstant(ExternalReference::libc_memmove_function());
1559 Node* start = IntPtrAdd(
1560 BitcastTaggedToWord(elements),
1561 ElementOffsetFromIndex(IntPtrConstant(0), HOLEY_DOUBLE_ELEMENTS,
1562 INTPTR_PARAMETERS, header_size));
1563 CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
1564 MachineType::Pointer(), MachineType::UintPtr(), memmove,
1565 start, IntPtrAdd(start, IntPtrConstant(kDoubleSize)),
1566 IntPtrMul(new_length, IntPtrConstant(kDoubleSize)));
1567 Node* offset = ElementOffsetFromIndex(new_length, HOLEY_DOUBLE_ELEMENTS,
1568 INTPTR_PARAMETERS, header_size);
1569 if (Is64()) {
1570 Node* double_hole = Int64Constant(kHoleNanInt64);
1571 StoreNoWriteBarrier(MachineRepresentation::kWord64, elements, offset,
1572 double_hole);
1573 } else {
1574 STATIC_ASSERT(kHoleNanLower32 == kHoleNanUpper32);
1575 Node* double_hole = Int32Constant(kHoleNanLower32);
1576 StoreNoWriteBarrier(MachineRepresentation::kWord32, elements, offset,
1577 double_hole);
1578 StoreNoWriteBarrier(MachineRepresentation::kWord32, elements,
1579 IntPtrAdd(offset, IntPtrConstant(kPointerSize)),
1580 double_hole);
1581 }
1582 args.PopAndReturn(result.value());
1583 }
1584
1585 BIND(&fast_elements_tagged);
1586 {
1587 Node* value = LoadFixedArrayElement(elements, 0);
1588 BuildFastLoop(IntPtrConstant(0), new_length,
1589 [&](Node* index) {
1590 StoreFixedArrayElement(
1591 elements, index,
1592 LoadFixedArrayElement(
1593 elements, IntPtrAdd(index, IntPtrConstant(1))));
1594 },
1595 1, ParameterMode::INTPTR_PARAMETERS,
1596 IndexAdvanceMode::kPost);
1597 StoreFixedArrayElement(elements, new_length, TheHoleConstant());
1598 GotoIf(WordEqual(value, TheHoleConstant()), &return_undefined);
1599 args.PopAndReturn(value);
1600 }
1601
1602 BIND(&fast_elements_smi);
1603 {
1604 Node* value = LoadFixedArrayElement(elements, 0);
1605 BuildFastLoop(IntPtrConstant(0), new_length,
1606 [&](Node* index) {
1607 StoreFixedArrayElement(
1608 elements, index,
1609 LoadFixedArrayElement(
1610 elements, IntPtrAdd(index, IntPtrConstant(1))),
1611 SKIP_WRITE_BARRIER);
1612 },
1613 1, ParameterMode::INTPTR_PARAMETERS,
1614 IndexAdvanceMode::kPost);
1615 StoreFixedArrayElement(elements, new_length, TheHoleConstant());
1616 GotoIf(WordEqual(value, TheHoleConstant()), &return_undefined);
1617 args.PopAndReturn(value);
1618 }
1619
1620 BIND(&return_undefined);
1621 { args.PopAndReturn(UndefinedConstant()); }
1622 }
1623
1624 BIND(&runtime);
1625 {
1626 Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
1627 MachineType::TaggedPointer());
1628 TailCallStub(CodeFactory::ArrayShift(isolate()), context, target,
1629 UndefinedConstant(), argc);
1630 }
1631 }
1632
TF_BUILTIN(ExtractFastJSArray,ArrayBuiltinsAssembler)1633 TF_BUILTIN(ExtractFastJSArray, ArrayBuiltinsAssembler) {
1634 ParameterMode mode = OptimalParameterMode();
1635 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1636 Node* array = Parameter(Descriptor::kSource);
1637 Node* begin = TaggedToParameter(Parameter(Descriptor::kBegin), mode);
1638 Node* count = TaggedToParameter(Parameter(Descriptor::kCount), mode);
1639
1640 CSA_ASSERT(this, IsJSArray(array));
1641 CSA_ASSERT(this, Word32BinaryNot(IsNoElementsProtectorCellInvalid()));
1642
1643 Return(ExtractFastJSArray(context, array, begin, count, mode));
1644 }
1645
TF_BUILTIN(CloneFastJSArray,ArrayBuiltinsAssembler)1646 TF_BUILTIN(CloneFastJSArray, ArrayBuiltinsAssembler) {
1647 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1648 Node* array = Parameter(Descriptor::kSource);
1649
1650 CSA_ASSERT(this, IsJSArray(array));
1651 CSA_ASSERT(this, Word32BinaryNot(IsNoElementsProtectorCellInvalid()));
1652
1653 ParameterMode mode = OptimalParameterMode();
1654 Return(CloneFastJSArray(context, array, mode));
1655 }
1656
TF_BUILTIN(ArrayFindLoopContinuation,ArrayBuiltinsAssembler)1657 TF_BUILTIN(ArrayFindLoopContinuation, ArrayBuiltinsAssembler) {
1658 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1659 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1660 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
1661 Node* this_arg = Parameter(Descriptor::kThisArg);
1662 Node* array = Parameter(Descriptor::kArray);
1663 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
1664 Node* initial_k = Parameter(Descriptor::kInitialK);
1665 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
1666 Node* to = Parameter(Descriptor::kTo);
1667
1668 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
1669 this_arg, array, object, initial_k,
1670 len, to);
1671
1672 GenerateIteratingArrayBuiltinLoopContinuation(
1673 &ArrayBuiltinsAssembler::FindProcessor,
1674 &ArrayBuiltinsAssembler::NullPostLoopAction,
1675 MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
1676 }
1677
1678 // Continuation that is called after an eager deoptimization from TF (ex. the
1679 // array changes during iteration).
TF_BUILTIN(ArrayFindLoopEagerDeoptContinuation,ArrayBuiltinsAssembler)1680 TF_BUILTIN(ArrayFindLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
1681 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1682 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1683 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
1684 Node* this_arg = Parameter(Descriptor::kThisArg);
1685 Node* initial_k = Parameter(Descriptor::kInitialK);
1686 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
1687
1688 Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver,
1689 callbackfn, this_arg, UndefinedConstant(), receiver,
1690 initial_k, len, UndefinedConstant()));
1691 }
1692
1693 // Continuation that is called after a lazy deoptimization from TF (ex. the
1694 // callback function is no longer callable).
TF_BUILTIN(ArrayFindLoopLazyDeoptContinuation,ArrayBuiltinsAssembler)1695 TF_BUILTIN(ArrayFindLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
1696 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1697 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1698 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
1699 Node* this_arg = Parameter(Descriptor::kThisArg);
1700 Node* initial_k = Parameter(Descriptor::kInitialK);
1701 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
1702
1703 Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver,
1704 callbackfn, this_arg, UndefinedConstant(), receiver,
1705 initial_k, len, UndefinedConstant()));
1706 }
1707
1708 // Continuation that is called after a lazy deoptimization from TF that happens
1709 // right after the callback and it's returned value must be handled before
1710 // iteration continues.
TF_BUILTIN(ArrayFindLoopAfterCallbackLazyDeoptContinuation,ArrayBuiltinsAssembler)1711 TF_BUILTIN(ArrayFindLoopAfterCallbackLazyDeoptContinuation,
1712 ArrayBuiltinsAssembler) {
1713 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1714 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1715 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
1716 Node* this_arg = Parameter(Descriptor::kThisArg);
1717 Node* initial_k = Parameter(Descriptor::kInitialK);
1718 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
1719 Node* found_value = Parameter(Descriptor::kFoundValue);
1720 Node* is_found = Parameter(Descriptor::kIsFound);
1721
1722 // This custom lazy deopt point is right after the callback. find() needs
1723 // to pick up at the next step, which is returning the element if the callback
1724 // value is truthy. Otherwise, continue the search by calling the
1725 // continuation.
1726 Label if_true(this), if_false(this);
1727 BranchIfToBooleanIsTrue(is_found, &if_true, &if_false);
1728 BIND(&if_true);
1729 Return(found_value);
1730 BIND(&if_false);
1731 Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver,
1732 callbackfn, this_arg, UndefinedConstant(), receiver,
1733 initial_k, len, UndefinedConstant()));
1734 }
1735
1736 // ES #sec-get-%typedarray%.prototype.find
TF_BUILTIN(ArrayPrototypeFind,ArrayBuiltinsAssembler)1737 TF_BUILTIN(ArrayPrototypeFind, ArrayBuiltinsAssembler) {
1738 TNode<IntPtrT> argc =
1739 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1740 CodeStubArguments args(this, argc);
1741 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1742 TNode<Object> receiver = args.GetReceiver();
1743 Node* callbackfn = args.GetOptionalArgumentValue(0);
1744 Node* this_arg = args.GetOptionalArgumentValue(1);
1745
1746 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
1747
1748 GenerateIteratingArrayBuiltinBody(
1749 "Array.prototype.find", &ArrayBuiltinsAssembler::FindResultGenerator,
1750 &ArrayBuiltinsAssembler::FindProcessor,
1751 &ArrayBuiltinsAssembler::NullPostLoopAction,
1752 Builtins::CallableFor(isolate(), Builtins::kArrayFindLoopContinuation),
1753 MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
1754 }
1755
TF_BUILTIN(ArrayFindIndexLoopContinuation,ArrayBuiltinsAssembler)1756 TF_BUILTIN(ArrayFindIndexLoopContinuation, ArrayBuiltinsAssembler) {
1757 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1758 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1759 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
1760 Node* this_arg = Parameter(Descriptor::kThisArg);
1761 Node* array = Parameter(Descriptor::kArray);
1762 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
1763 Node* initial_k = Parameter(Descriptor::kInitialK);
1764 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
1765 Node* to = Parameter(Descriptor::kTo);
1766
1767 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
1768 this_arg, array, object, initial_k,
1769 len, to);
1770
1771 GenerateIteratingArrayBuiltinLoopContinuation(
1772 &ArrayBuiltinsAssembler::FindIndexProcessor,
1773 &ArrayBuiltinsAssembler::NullPostLoopAction,
1774 MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
1775 }
1776
TF_BUILTIN(ArrayFindIndexLoopEagerDeoptContinuation,ArrayBuiltinsAssembler)1777 TF_BUILTIN(ArrayFindIndexLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
1778 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1779 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1780 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
1781 Node* this_arg = Parameter(Descriptor::kThisArg);
1782 Node* initial_k = Parameter(Descriptor::kInitialK);
1783 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
1784
1785 Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context,
1786 receiver, callbackfn, this_arg, SmiConstant(-1), receiver,
1787 initial_k, len, UndefinedConstant()));
1788 }
1789
TF_BUILTIN(ArrayFindIndexLoopLazyDeoptContinuation,ArrayBuiltinsAssembler)1790 TF_BUILTIN(ArrayFindIndexLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
1791 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1792 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1793 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
1794 Node* this_arg = Parameter(Descriptor::kThisArg);
1795 Node* initial_k = Parameter(Descriptor::kInitialK);
1796 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
1797
1798 Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context,
1799 receiver, callbackfn, this_arg, SmiConstant(-1), receiver,
1800 initial_k, len, UndefinedConstant()));
1801 }
1802
TF_BUILTIN(ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation,ArrayBuiltinsAssembler)1803 TF_BUILTIN(ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation,
1804 ArrayBuiltinsAssembler) {
1805 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
1806 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
1807 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
1808 Node* this_arg = Parameter(Descriptor::kThisArg);
1809 Node* initial_k = Parameter(Descriptor::kInitialK);
1810 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
1811 Node* found_value = Parameter(Descriptor::kFoundValue);
1812 Node* is_found = Parameter(Descriptor::kIsFound);
1813
1814 // This custom lazy deopt point is right after the callback. find() needs
1815 // to pick up at the next step, which is returning the element if the callback
1816 // value is truthy. Otherwise, continue the search by calling the
1817 // continuation.
1818 Label if_true(this), if_false(this);
1819 BranchIfToBooleanIsTrue(is_found, &if_true, &if_false);
1820 BIND(&if_true);
1821 Return(found_value);
1822 BIND(&if_false);
1823 Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context,
1824 receiver, callbackfn, this_arg, SmiConstant(-1), receiver,
1825 initial_k, len, UndefinedConstant()));
1826 }
1827
1828 // ES #sec-get-%typedarray%.prototype.findIndex
TF_BUILTIN(ArrayPrototypeFindIndex,ArrayBuiltinsAssembler)1829 TF_BUILTIN(ArrayPrototypeFindIndex, ArrayBuiltinsAssembler) {
1830 TNode<IntPtrT> argc =
1831 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1832 CodeStubArguments args(this, argc);
1833 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1834 TNode<Object> receiver = args.GetReceiver();
1835 Node* callbackfn = args.GetOptionalArgumentValue(0);
1836 Node* this_arg = args.GetOptionalArgumentValue(1);
1837
1838 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
1839
1840 GenerateIteratingArrayBuiltinBody(
1841 "Array.prototype.findIndex",
1842 &ArrayBuiltinsAssembler::FindIndexResultGenerator,
1843 &ArrayBuiltinsAssembler::FindIndexProcessor,
1844 &ArrayBuiltinsAssembler::NullPostLoopAction,
1845 Builtins::CallableFor(isolate(),
1846 Builtins::kArrayFindIndexLoopContinuation),
1847 MissingPropertyMode::kUseUndefined, ForEachDirection::kForward);
1848 }
1849
1850 class ArrayPopulatorAssembler : public CodeStubAssembler {
1851 public:
ArrayPopulatorAssembler(compiler::CodeAssemblerState * state)1852 explicit ArrayPopulatorAssembler(compiler::CodeAssemblerState* state)
1853 : CodeStubAssembler(state) {}
1854
ConstructArrayLike(TNode<Context> context,TNode<Object> receiver)1855 TNode<Object> ConstructArrayLike(TNode<Context> context,
1856 TNode<Object> receiver) {
1857 TVARIABLE(Object, array);
1858 Label is_constructor(this), is_not_constructor(this), done(this);
1859 GotoIf(TaggedIsSmi(receiver), &is_not_constructor);
1860 Branch(IsConstructor(CAST(receiver)), &is_constructor, &is_not_constructor);
1861
1862 BIND(&is_constructor);
1863 {
1864 array = CAST(
1865 ConstructJS(CodeFactory::Construct(isolate()), context, receiver));
1866 Goto(&done);
1867 }
1868
1869 BIND(&is_not_constructor);
1870 {
1871 Label allocate_js_array(this);
1872
1873 TNode<Map> array_map = CAST(LoadContextElement(
1874 context, Context::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX));
1875
1876 array = CAST(AllocateJSArray(PACKED_SMI_ELEMENTS, array_map,
1877 SmiConstant(0), SmiConstant(0), nullptr,
1878 ParameterMode::SMI_PARAMETERS));
1879 Goto(&done);
1880 }
1881
1882 BIND(&done);
1883 return array.value();
1884 }
1885
ConstructArrayLike(TNode<Context> context,TNode<Object> receiver,TNode<Number> length)1886 TNode<Object> ConstructArrayLike(TNode<Context> context,
1887 TNode<Object> receiver,
1888 TNode<Number> length) {
1889 TVARIABLE(Object, array);
1890 Label is_constructor(this), is_not_constructor(this), done(this);
1891 CSA_ASSERT(this, IsNumberNormalized(length));
1892 GotoIf(TaggedIsSmi(receiver), &is_not_constructor);
1893 Branch(IsConstructor(CAST(receiver)), &is_constructor, &is_not_constructor);
1894
1895 BIND(&is_constructor);
1896 {
1897 array = CAST(ConstructJS(CodeFactory::Construct(isolate()), context,
1898 receiver, length));
1899 Goto(&done);
1900 }
1901
1902 BIND(&is_not_constructor);
1903 {
1904 Label allocate_js_array(this);
1905
1906 Label next(this), runtime(this, Label::kDeferred);
1907 TNode<Smi> limit = SmiConstant(JSArray::kInitialMaxFastElementArray);
1908 CSA_ASSERT_BRANCH(this, [=](Label* ok, Label* not_ok) {
1909 BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual,
1910 length, SmiConstant(0), ok, not_ok);
1911 });
1912 // This check also transitively covers the case where length is too big
1913 // to be representable by a SMI and so is not usable with
1914 // AllocateJSArray.
1915 BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual, length,
1916 limit, &runtime, &next);
1917
1918 BIND(&runtime);
1919 {
1920 TNode<Context> native_context = LoadNativeContext(context);
1921 TNode<JSFunction> array_function = CAST(
1922 LoadContextElement(native_context, Context::ARRAY_FUNCTION_INDEX));
1923 array = CallRuntime(Runtime::kNewArray, context, array_function, length,
1924 array_function, UndefinedConstant());
1925 Goto(&done);
1926 }
1927
1928 BIND(&next);
1929 CSA_ASSERT(this, TaggedIsSmi(length));
1930
1931 TNode<Map> array_map = CAST(LoadContextElement(
1932 context, Context::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX));
1933
1934 // TODO(delphick): Consider using
1935 // AllocateUninitializedJSArrayWithElements to avoid initializing an
1936 // array and then writing over it.
1937 array = CAST(AllocateJSArray(PACKED_SMI_ELEMENTS, array_map, length,
1938 SmiConstant(0), nullptr,
1939 ParameterMode::SMI_PARAMETERS));
1940 Goto(&done);
1941 }
1942
1943 BIND(&done);
1944 return array.value();
1945 }
1946
GenerateSetLength(TNode<Context> context,TNode<Object> array,TNode<Number> length)1947 void GenerateSetLength(TNode<Context> context, TNode<Object> array,
1948 TNode<Number> length) {
1949 Label fast(this), runtime(this), done(this);
1950 // There's no need to set the length, if
1951 // 1) the array is a fast JS array and
1952 // 2) the new length is equal to the old length.
1953 // as the set is not observable. Otherwise fall back to the run-time.
1954
1955 // 1) Check that the array has fast elements.
1956 // TODO(delphick): Consider changing this since it does an an unnecessary
1957 // check for SMIs.
1958 // TODO(delphick): Also we could hoist this to after the array construction
1959 // and copy the args into array in the same way as the Array constructor.
1960 BranchIfFastJSArray(array, context, &fast, &runtime);
1961
1962 BIND(&fast);
1963 {
1964 TNode<JSArray> fast_array = CAST(array);
1965
1966 TNode<Smi> length_smi = CAST(length);
1967 TNode<Smi> old_length = LoadFastJSArrayLength(fast_array);
1968 CSA_ASSERT(this, TaggedIsPositiveSmi(old_length));
1969
1970 // 2) If the created array's length matches the required length, then
1971 // there's nothing else to do. Otherwise use the runtime to set the
1972 // property as that will insert holes into excess elements or shrink
1973 // the backing store as appropriate.
1974 Branch(SmiNotEqual(length_smi, old_length), &runtime, &done);
1975 }
1976
1977 BIND(&runtime);
1978 {
1979 CallRuntime(Runtime::kSetProperty, context, static_cast<Node*>(array),
1980 CodeStubAssembler::LengthStringConstant(), length,
1981 SmiConstant(LanguageMode::kStrict));
1982 Goto(&done);
1983 }
1984
1985 BIND(&done);
1986 }
1987 };
1988
1989 // ES #sec-array.from
TF_BUILTIN(ArrayFrom,ArrayPopulatorAssembler)1990 TF_BUILTIN(ArrayFrom, ArrayPopulatorAssembler) {
1991 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
1992 TNode<Int32T> argc =
1993 UncheckedCast<Int32T>(Parameter(BuiltinDescriptor::kArgumentsCount));
1994
1995 CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
1996
1997 TNode<Object> map_function = args.GetOptionalArgumentValue(1);
1998
1999 // If map_function is not undefined, then ensure it's callable else throw.
2000 {
2001 Label no_error(this), error(this);
2002 GotoIf(IsUndefined(map_function), &no_error);
2003 GotoIf(TaggedIsSmi(map_function), &error);
2004 Branch(IsCallable(CAST(map_function)), &no_error, &error);
2005
2006 BIND(&error);
2007 ThrowTypeError(context, MessageTemplate::kCalledNonCallable, map_function);
2008
2009 BIND(&no_error);
2010 }
2011
2012 Label iterable(this), not_iterable(this), finished(this), if_exception(this);
2013
2014 TNode<Object> this_arg = args.GetOptionalArgumentValue(2);
2015 TNode<Object> items = args.GetOptionalArgumentValue(0);
2016 // The spec doesn't require ToObject to be called directly on the iterable
2017 // branch, but it's part of GetMethod that is in the spec.
2018 TNode<JSReceiver> array_like = ToObject(context, items);
2019
2020 TVARIABLE(Object, array);
2021 TVARIABLE(Number, length);
2022
2023 // Determine whether items[Symbol.iterator] is defined:
2024 IteratorBuiltinsAssembler iterator_assembler(state());
2025 Node* iterator_method =
2026 iterator_assembler.GetIteratorMethod(context, array_like);
2027 Branch(IsNullOrUndefined(iterator_method), ¬_iterable, &iterable);
2028
2029 BIND(&iterable);
2030 {
2031 TVARIABLE(Number, index, SmiConstant(0));
2032 TVARIABLE(Object, var_exception);
2033 Label loop(this, &index), loop_done(this),
2034 on_exception(this, Label::kDeferred),
2035 index_overflow(this, Label::kDeferred);
2036
2037 // Check that the method is callable.
2038 {
2039 Label get_method_not_callable(this, Label::kDeferred), next(this);
2040 GotoIf(TaggedIsSmi(iterator_method), &get_method_not_callable);
2041 GotoIfNot(IsCallable(CAST(iterator_method)), &get_method_not_callable);
2042 Goto(&next);
2043
2044 BIND(&get_method_not_callable);
2045 ThrowTypeError(context, MessageTemplate::kCalledNonCallable,
2046 iterator_method);
2047
2048 BIND(&next);
2049 }
2050
2051 // Construct the output array with empty length.
2052 array = ConstructArrayLike(context, args.GetReceiver());
2053
2054 // Actually get the iterator and throw if the iterator method does not yield
2055 // one.
2056 IteratorRecord iterator_record =
2057 iterator_assembler.GetIterator(context, items, iterator_method);
2058
2059 TNode<Context> native_context = LoadNativeContext(context);
2060 TNode<Object> fast_iterator_result_map =
2061 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
2062
2063 Goto(&loop);
2064
2065 BIND(&loop);
2066 {
2067 // Loop while iterator is not done.
2068 TNode<Object> next = CAST(iterator_assembler.IteratorStep(
2069 context, iterator_record, &loop_done, fast_iterator_result_map));
2070 TVARIABLE(Object, value,
2071 CAST(iterator_assembler.IteratorValue(
2072 context, next, fast_iterator_result_map)));
2073
2074 // If a map_function is supplied then call it (using this_arg as
2075 // receiver), on the value returned from the iterator. Exceptions are
2076 // caught so the iterator can be closed.
2077 {
2078 Label next(this);
2079 GotoIf(IsUndefined(map_function), &next);
2080
2081 CSA_ASSERT(this, IsCallable(CAST(map_function)));
2082 Node* v = CallJS(CodeFactory::Call(isolate()), context, map_function,
2083 this_arg, value.value(), index.value());
2084 GotoIfException(v, &on_exception, &var_exception);
2085 value = CAST(v);
2086 Goto(&next);
2087 BIND(&next);
2088 }
2089
2090 // Store the result in the output object (catching any exceptions so the
2091 // iterator can be closed).
2092 Node* define_status =
2093 CallRuntime(Runtime::kCreateDataProperty, context, array.value(),
2094 index.value(), value.value());
2095 GotoIfException(define_status, &on_exception, &var_exception);
2096
2097 index = NumberInc(index.value());
2098
2099 // The spec requires that we throw an exception if index reaches 2^53-1,
2100 // but an empty loop would take >100 days to do this many iterations. To
2101 // actually run for that long would require an iterator that never set
2102 // done to true and a target array which somehow never ran out of memory,
2103 // e.g. a proxy that discarded the values. Ignoring this case just means
2104 // we would repeatedly call CreateDataProperty with index = 2^53.
2105 CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) {
2106 BranchIfNumberRelationalComparison(Operation::kLessThan, index.value(),
2107 NumberConstant(kMaxSafeInteger), ok,
2108 not_ok);
2109 });
2110 Goto(&loop);
2111 }
2112
2113 BIND(&loop_done);
2114 {
2115 length = index;
2116 Goto(&finished);
2117 }
2118
2119 BIND(&on_exception);
2120 {
2121 // Close the iterator, rethrowing either the passed exception or
2122 // exceptions thrown during the close.
2123 iterator_assembler.IteratorCloseOnException(context, iterator_record,
2124 &var_exception);
2125 }
2126 }
2127
2128 BIND(¬_iterable);
2129 {
2130 // Treat array_like as an array and try to get its length.
2131 length = ToLength_Inline(
2132 context, GetProperty(context, array_like, factory()->length_string()));
2133
2134 // Construct an array using the receiver as constructor with the same length
2135 // as the input array.
2136 array = ConstructArrayLike(context, args.GetReceiver(), length.value());
2137
2138 TVARIABLE(Number, index, SmiConstant(0));
2139
2140 // TODO(ishell): remove <Object, Object>
2141 GotoIf(WordEqual<Object, Object>(length.value(), SmiConstant(0)),
2142 &finished);
2143
2144 // Loop from 0 to length-1.
2145 {
2146 Label loop(this, &index);
2147 Goto(&loop);
2148 BIND(&loop);
2149 TVARIABLE(Object, value);
2150
2151 value = GetProperty(context, array_like, index.value());
2152
2153 // If a map_function is supplied then call it (using this_arg as
2154 // receiver), on the value retrieved from the array.
2155 {
2156 Label next(this);
2157 GotoIf(IsUndefined(map_function), &next);
2158
2159 CSA_ASSERT(this, IsCallable(CAST(map_function)));
2160 value = CAST(CallJS(CodeFactory::Call(isolate()), context, map_function,
2161 this_arg, value.value(), index.value()));
2162 Goto(&next);
2163 BIND(&next);
2164 }
2165
2166 // Store the result in the output object.
2167 CallRuntime(Runtime::kCreateDataProperty, context, array.value(),
2168 index.value(), value.value());
2169 index = NumberInc(index.value());
2170 BranchIfNumberRelationalComparison(Operation::kLessThan, index.value(),
2171 length.value(), &loop, &finished);
2172 }
2173 }
2174
2175 BIND(&finished);
2176
2177 // Finally set the length on the output and return it.
2178 GenerateSetLength(context, array.value(), length.value());
2179 args.PopAndReturn(array.value());
2180 }
2181
2182 // ES #sec-array.of
TF_BUILTIN(ArrayOf,ArrayPopulatorAssembler)2183 TF_BUILTIN(ArrayOf, ArrayPopulatorAssembler) {
2184 TNode<Int32T> argc =
2185 UncheckedCast<Int32T>(Parameter(BuiltinDescriptor::kArgumentsCount));
2186 TNode<Smi> length = SmiFromInt32(argc);
2187
2188 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2189
2190 CodeStubArguments args(this, length, nullptr, ParameterMode::SMI_PARAMETERS);
2191
2192 TNode<Object> array = ConstructArrayLike(context, args.GetReceiver(), length);
2193
2194 // TODO(delphick): Avoid using CreateDataProperty on the fast path.
2195 BuildFastLoop(SmiConstant(0), length,
2196 [=](Node* index) {
2197 CallRuntime(
2198 Runtime::kCreateDataProperty, context,
2199 static_cast<Node*>(array), index,
2200 args.AtIndex(index, ParameterMode::SMI_PARAMETERS));
2201 },
2202 1, ParameterMode::SMI_PARAMETERS, IndexAdvanceMode::kPost);
2203
2204 GenerateSetLength(context, array, length);
2205 args.PopAndReturn(array);
2206 }
2207
2208 // ES #sec-get-%typedarray%.prototype.find
TF_BUILTIN(TypedArrayPrototypeFind,ArrayBuiltinsAssembler)2209 TF_BUILTIN(TypedArrayPrototypeFind, ArrayBuiltinsAssembler) {
2210 TNode<IntPtrT> argc =
2211 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2212 CodeStubArguments args(this, argc);
2213 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2214 TNode<Object> receiver = args.GetReceiver();
2215 Node* callbackfn = args.GetOptionalArgumentValue(0);
2216 Node* this_arg = args.GetOptionalArgumentValue(1);
2217
2218 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
2219
2220 GenerateIteratingTypedArrayBuiltinBody(
2221 "%TypedArray%.prototype.find",
2222 &ArrayBuiltinsAssembler::FindResultGenerator,
2223 &ArrayBuiltinsAssembler::FindProcessor,
2224 &ArrayBuiltinsAssembler::NullPostLoopAction);
2225 }
2226
2227 // ES #sec-get-%typedarray%.prototype.findIndex
TF_BUILTIN(TypedArrayPrototypeFindIndex,ArrayBuiltinsAssembler)2228 TF_BUILTIN(TypedArrayPrototypeFindIndex, ArrayBuiltinsAssembler) {
2229 TNode<IntPtrT> argc =
2230 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2231 CodeStubArguments args(this, argc);
2232 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2233 TNode<Object> receiver = args.GetReceiver();
2234 Node* callbackfn = args.GetOptionalArgumentValue(0);
2235 Node* this_arg = args.GetOptionalArgumentValue(1);
2236
2237 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
2238
2239 GenerateIteratingTypedArrayBuiltinBody(
2240 "%TypedArray%.prototype.findIndex",
2241 &ArrayBuiltinsAssembler::FindIndexResultGenerator,
2242 &ArrayBuiltinsAssembler::FindIndexProcessor,
2243 &ArrayBuiltinsAssembler::NullPostLoopAction);
2244 }
2245
TF_BUILTIN(TypedArrayPrototypeForEach,ArrayBuiltinsAssembler)2246 TF_BUILTIN(TypedArrayPrototypeForEach, ArrayBuiltinsAssembler) {
2247 TNode<IntPtrT> argc =
2248 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2249 CodeStubArguments args(this, argc);
2250 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2251 TNode<Object> receiver = args.GetReceiver();
2252 Node* callbackfn = args.GetOptionalArgumentValue(0);
2253 Node* this_arg = args.GetOptionalArgumentValue(1);
2254
2255 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
2256
2257 GenerateIteratingTypedArrayBuiltinBody(
2258 "%TypedArray%.prototype.forEach",
2259 &ArrayBuiltinsAssembler::ForEachResultGenerator,
2260 &ArrayBuiltinsAssembler::ForEachProcessor,
2261 &ArrayBuiltinsAssembler::NullPostLoopAction);
2262 }
2263
TF_BUILTIN(ArraySomeLoopLazyDeoptContinuation,ArrayBuiltinsAssembler)2264 TF_BUILTIN(ArraySomeLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
2265 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2266 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2267 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2268 Node* this_arg = Parameter(Descriptor::kThisArg);
2269 Node* initial_k = Parameter(Descriptor::kInitialK);
2270 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2271 Node* result = Parameter(Descriptor::kResult);
2272
2273 // This custom lazy deopt point is right after the callback. every() needs
2274 // to pick up at the next step, which is either continuing to the next
2275 // array element or returning false if {result} is false.
2276 Label true_continue(this), false_continue(this);
2277
2278 // iii. If selected is true, then...
2279 BranchIfToBooleanIsTrue(result, &true_continue, &false_continue);
2280 BIND(&true_continue);
2281 { Return(TrueConstant()); }
2282 BIND(&false_continue);
2283 {
2284 // Increment k.
2285 initial_k = NumberInc(initial_k);
2286
2287 Return(CallBuiltin(Builtins::kArraySomeLoopContinuation, context, receiver,
2288 callbackfn, this_arg, FalseConstant(), receiver,
2289 initial_k, len, UndefinedConstant()));
2290 }
2291 }
2292
TF_BUILTIN(ArraySomeLoopEagerDeoptContinuation,ArrayBuiltinsAssembler)2293 TF_BUILTIN(ArraySomeLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
2294 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2295 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2296 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2297 Node* this_arg = Parameter(Descriptor::kThisArg);
2298 Node* initial_k = Parameter(Descriptor::kInitialK);
2299 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2300
2301 Return(CallBuiltin(Builtins::kArraySomeLoopContinuation, context, receiver,
2302 callbackfn, this_arg, FalseConstant(), receiver, initial_k,
2303 len, UndefinedConstant()));
2304 }
2305
TF_BUILTIN(ArraySomeLoopContinuation,ArrayBuiltinsAssembler)2306 TF_BUILTIN(ArraySomeLoopContinuation, ArrayBuiltinsAssembler) {
2307 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2308 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2309 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2310 Node* this_arg = Parameter(Descriptor::kThisArg);
2311 Node* array = Parameter(Descriptor::kArray);
2312 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
2313 Node* initial_k = Parameter(Descriptor::kInitialK);
2314 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2315 Node* to = Parameter(Descriptor::kTo);
2316
2317 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
2318 this_arg, array, object, initial_k,
2319 len, to);
2320
2321 GenerateIteratingArrayBuiltinLoopContinuation(
2322 &ArrayBuiltinsAssembler::SomeProcessor,
2323 &ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip);
2324 }
2325
TF_BUILTIN(ArraySome,ArrayBuiltinsAssembler)2326 TF_BUILTIN(ArraySome, ArrayBuiltinsAssembler) {
2327 TNode<IntPtrT> argc =
2328 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2329 CodeStubArguments args(this, argc);
2330 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2331 TNode<Object> receiver = args.GetReceiver();
2332 Node* callbackfn = args.GetOptionalArgumentValue(0);
2333 Node* this_arg = args.GetOptionalArgumentValue(1);
2334
2335 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
2336
2337 GenerateIteratingArrayBuiltinBody(
2338 "Array.prototype.some", &ArrayBuiltinsAssembler::SomeResultGenerator,
2339 &ArrayBuiltinsAssembler::SomeProcessor,
2340 &ArrayBuiltinsAssembler::NullPostLoopAction,
2341 Builtins::CallableFor(isolate(), Builtins::kArraySomeLoopContinuation),
2342 MissingPropertyMode::kSkip);
2343 }
2344
TF_BUILTIN(TypedArrayPrototypeSome,ArrayBuiltinsAssembler)2345 TF_BUILTIN(TypedArrayPrototypeSome, ArrayBuiltinsAssembler) {
2346 TNode<IntPtrT> argc =
2347 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2348 CodeStubArguments args(this, argc);
2349 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2350 TNode<Object> receiver = args.GetReceiver();
2351 Node* callbackfn = args.GetOptionalArgumentValue(0);
2352 Node* this_arg = args.GetOptionalArgumentValue(1);
2353
2354 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
2355
2356 GenerateIteratingTypedArrayBuiltinBody(
2357 "%TypedArray%.prototype.some",
2358 &ArrayBuiltinsAssembler::SomeResultGenerator,
2359 &ArrayBuiltinsAssembler::SomeProcessor,
2360 &ArrayBuiltinsAssembler::NullPostLoopAction);
2361 }
2362
TF_BUILTIN(ArrayEveryLoopLazyDeoptContinuation,ArrayBuiltinsAssembler)2363 TF_BUILTIN(ArrayEveryLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
2364 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2365 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2366 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2367 Node* this_arg = Parameter(Descriptor::kThisArg);
2368 Node* initial_k = Parameter(Descriptor::kInitialK);
2369 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2370 Node* result = Parameter(Descriptor::kResult);
2371
2372 // This custom lazy deopt point is right after the callback. every() needs
2373 // to pick up at the next step, which is either continuing to the next
2374 // array element or returning false if {result} is false.
2375 Label true_continue(this), false_continue(this);
2376
2377 // iii. If selected is true, then...
2378 BranchIfToBooleanIsTrue(result, &true_continue, &false_continue);
2379 BIND(&true_continue);
2380 {
2381 // Increment k.
2382 initial_k = NumberInc(initial_k);
2383
2384 Return(CallBuiltin(Builtins::kArrayEveryLoopContinuation, context, receiver,
2385 callbackfn, this_arg, TrueConstant(), receiver,
2386 initial_k, len, UndefinedConstant()));
2387 }
2388 BIND(&false_continue);
2389 { Return(FalseConstant()); }
2390 }
2391
TF_BUILTIN(ArrayEveryLoopEagerDeoptContinuation,ArrayBuiltinsAssembler)2392 TF_BUILTIN(ArrayEveryLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
2393 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2394 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2395 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2396 Node* this_arg = Parameter(Descriptor::kThisArg);
2397 Node* initial_k = Parameter(Descriptor::kInitialK);
2398 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2399
2400 Return(CallBuiltin(Builtins::kArrayEveryLoopContinuation, context, receiver,
2401 callbackfn, this_arg, TrueConstant(), receiver, initial_k,
2402 len, UndefinedConstant()));
2403 }
2404
TF_BUILTIN(ArrayEveryLoopContinuation,ArrayBuiltinsAssembler)2405 TF_BUILTIN(ArrayEveryLoopContinuation, ArrayBuiltinsAssembler) {
2406 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2407 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2408 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2409 Node* this_arg = Parameter(Descriptor::kThisArg);
2410 Node* array = Parameter(Descriptor::kArray);
2411 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
2412 Node* initial_k = Parameter(Descriptor::kInitialK);
2413 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2414 Node* to = Parameter(Descriptor::kTo);
2415
2416 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
2417 this_arg, array, object, initial_k,
2418 len, to);
2419
2420 GenerateIteratingArrayBuiltinLoopContinuation(
2421 &ArrayBuiltinsAssembler::EveryProcessor,
2422 &ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip);
2423 }
2424
TF_BUILTIN(ArrayEvery,ArrayBuiltinsAssembler)2425 TF_BUILTIN(ArrayEvery, ArrayBuiltinsAssembler) {
2426 TNode<IntPtrT> argc =
2427 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2428 CodeStubArguments args(this, argc);
2429 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2430 TNode<Object> receiver = args.GetReceiver();
2431 Node* callbackfn = args.GetOptionalArgumentValue(0);
2432 Node* this_arg = args.GetOptionalArgumentValue(1);
2433
2434 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
2435
2436 GenerateIteratingArrayBuiltinBody(
2437 "Array.prototype.every", &ArrayBuiltinsAssembler::EveryResultGenerator,
2438 &ArrayBuiltinsAssembler::EveryProcessor,
2439 &ArrayBuiltinsAssembler::NullPostLoopAction,
2440 Builtins::CallableFor(isolate(), Builtins::kArrayEveryLoopContinuation),
2441 MissingPropertyMode::kSkip);
2442 }
2443
TF_BUILTIN(TypedArrayPrototypeEvery,ArrayBuiltinsAssembler)2444 TF_BUILTIN(TypedArrayPrototypeEvery, ArrayBuiltinsAssembler) {
2445 TNode<IntPtrT> argc =
2446 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2447 CodeStubArguments args(this, argc);
2448 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2449 TNode<Object> receiver = args.GetReceiver();
2450 Node* callbackfn = args.GetOptionalArgumentValue(0);
2451 Node* this_arg = args.GetOptionalArgumentValue(1);
2452
2453 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
2454
2455 GenerateIteratingTypedArrayBuiltinBody(
2456 "%TypedArray%.prototype.every",
2457 &ArrayBuiltinsAssembler::EveryResultGenerator,
2458 &ArrayBuiltinsAssembler::EveryProcessor,
2459 &ArrayBuiltinsAssembler::NullPostLoopAction);
2460 }
2461
TF_BUILTIN(ArrayReduceLoopContinuation,ArrayBuiltinsAssembler)2462 TF_BUILTIN(ArrayReduceLoopContinuation, ArrayBuiltinsAssembler) {
2463 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2464 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2465 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2466 Node* this_arg = Parameter(Descriptor::kThisArg);
2467 Node* accumulator = Parameter(Descriptor::kAccumulator);
2468 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
2469 Node* initial_k = Parameter(Descriptor::kInitialK);
2470 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2471 Node* to = Parameter(Descriptor::kTo);
2472
2473 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
2474 this_arg, accumulator, object,
2475 initial_k, len, to);
2476
2477 GenerateIteratingArrayBuiltinLoopContinuation(
2478 &ArrayBuiltinsAssembler::ReduceProcessor,
2479 &ArrayBuiltinsAssembler::ReducePostLoopAction,
2480 MissingPropertyMode::kSkip);
2481 }
2482
TF_BUILTIN(ArrayReducePreLoopEagerDeoptContinuation,ArrayBuiltinsAssembler)2483 TF_BUILTIN(ArrayReducePreLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
2484 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2485 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2486 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2487 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2488
2489 // Simulate starting the loop at 0, but ensuring that the accumulator is
2490 // the hole. The continuation stub will search for the initial non-hole
2491 // element, rightly throwing an exception if not found.
2492 Return(CallBuiltin(Builtins::kArrayReduceLoopContinuation, context, receiver,
2493 callbackfn, UndefinedConstant(), TheHoleConstant(),
2494 receiver, SmiConstant(0), len, UndefinedConstant()));
2495 }
2496
TF_BUILTIN(ArrayReduceLoopEagerDeoptContinuation,ArrayBuiltinsAssembler)2497 TF_BUILTIN(ArrayReduceLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
2498 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2499 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2500 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2501 Node* accumulator = Parameter(Descriptor::kAccumulator);
2502 Node* initial_k = Parameter(Descriptor::kInitialK);
2503 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2504
2505 Return(CallBuiltin(Builtins::kArrayReduceLoopContinuation, context, receiver,
2506 callbackfn, UndefinedConstant(), accumulator, receiver,
2507 initial_k, len, UndefinedConstant()));
2508 }
2509
TF_BUILTIN(ArrayReduceLoopLazyDeoptContinuation,ArrayBuiltinsAssembler)2510 TF_BUILTIN(ArrayReduceLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
2511 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2512 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2513 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2514 Node* initial_k = Parameter(Descriptor::kInitialK);
2515 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2516 Node* result = Parameter(Descriptor::kResult);
2517
2518 Return(CallBuiltin(Builtins::kArrayReduceLoopContinuation, context, receiver,
2519 callbackfn, UndefinedConstant(), result, receiver,
2520 initial_k, len, UndefinedConstant()));
2521 }
2522
TF_BUILTIN(ArrayReduce,ArrayBuiltinsAssembler)2523 TF_BUILTIN(ArrayReduce, ArrayBuiltinsAssembler) {
2524 TNode<IntPtrT> argc =
2525 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2526 CodeStubArguments args(this, argc);
2527 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2528 TNode<Object> receiver = args.GetReceiver();
2529 Node* callbackfn = args.GetOptionalArgumentValue(0);
2530 Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant());
2531
2532 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value,
2533 argc);
2534
2535 GenerateIteratingArrayBuiltinBody(
2536 "Array.prototype.reduce", &ArrayBuiltinsAssembler::ReduceResultGenerator,
2537 &ArrayBuiltinsAssembler::ReduceProcessor,
2538 &ArrayBuiltinsAssembler::ReducePostLoopAction,
2539 Builtins::CallableFor(isolate(), Builtins::kArrayReduceLoopContinuation),
2540 MissingPropertyMode::kSkip);
2541 }
2542
TF_BUILTIN(TypedArrayPrototypeReduce,ArrayBuiltinsAssembler)2543 TF_BUILTIN(TypedArrayPrototypeReduce, ArrayBuiltinsAssembler) {
2544 TNode<IntPtrT> argc =
2545 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2546 CodeStubArguments args(this, argc);
2547 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2548 TNode<Object> receiver = args.GetReceiver();
2549 Node* callbackfn = args.GetOptionalArgumentValue(0);
2550 Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant());
2551
2552 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value,
2553 argc);
2554
2555 GenerateIteratingTypedArrayBuiltinBody(
2556 "%TypedArray%.prototype.reduce",
2557 &ArrayBuiltinsAssembler::ReduceResultGenerator,
2558 &ArrayBuiltinsAssembler::ReduceProcessor,
2559 &ArrayBuiltinsAssembler::ReducePostLoopAction);
2560 }
2561
TF_BUILTIN(ArrayReduceRightLoopContinuation,ArrayBuiltinsAssembler)2562 TF_BUILTIN(ArrayReduceRightLoopContinuation, ArrayBuiltinsAssembler) {
2563 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2564 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2565 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2566 Node* this_arg = Parameter(Descriptor::kThisArg);
2567 Node* accumulator = Parameter(Descriptor::kAccumulator);
2568 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
2569 Node* initial_k = Parameter(Descriptor::kInitialK);
2570 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2571 Node* to = Parameter(Descriptor::kTo);
2572
2573 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
2574 this_arg, accumulator, object,
2575 initial_k, len, to);
2576
2577 GenerateIteratingArrayBuiltinLoopContinuation(
2578 &ArrayBuiltinsAssembler::ReduceProcessor,
2579 &ArrayBuiltinsAssembler::ReducePostLoopAction, MissingPropertyMode::kSkip,
2580 ForEachDirection::kReverse);
2581 }
2582
TF_BUILTIN(ArrayReduceRightPreLoopEagerDeoptContinuation,ArrayBuiltinsAssembler)2583 TF_BUILTIN(ArrayReduceRightPreLoopEagerDeoptContinuation,
2584 ArrayBuiltinsAssembler) {
2585 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2586 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2587 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2588 TNode<Smi> len = CAST(Parameter(Descriptor::kLength));
2589
2590 // Simulate starting the loop at 0, but ensuring that the accumulator is
2591 // the hole. The continuation stub will search for the initial non-hole
2592 // element, rightly throwing an exception if not found.
2593 Return(CallBuiltin(Builtins::kArrayReduceRightLoopContinuation, context,
2594 receiver, callbackfn, UndefinedConstant(),
2595 TheHoleConstant(), receiver, SmiSub(len, SmiConstant(1)),
2596 len, UndefinedConstant()));
2597 }
2598
TF_BUILTIN(ArrayReduceRightLoopEagerDeoptContinuation,ArrayBuiltinsAssembler)2599 TF_BUILTIN(ArrayReduceRightLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
2600 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2601 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2602 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2603 Node* accumulator = Parameter(Descriptor::kAccumulator);
2604 Node* initial_k = Parameter(Descriptor::kInitialK);
2605 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2606
2607 Return(CallBuiltin(Builtins::kArrayReduceRightLoopContinuation, context,
2608 receiver, callbackfn, UndefinedConstant(), accumulator,
2609 receiver, initial_k, len, UndefinedConstant()));
2610 }
2611
TF_BUILTIN(ArrayReduceRightLoopLazyDeoptContinuation,ArrayBuiltinsAssembler)2612 TF_BUILTIN(ArrayReduceRightLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
2613 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2614 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2615 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2616 Node* initial_k = Parameter(Descriptor::kInitialK);
2617 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2618 Node* result = Parameter(Descriptor::kResult);
2619
2620 Return(CallBuiltin(Builtins::kArrayReduceRightLoopContinuation, context,
2621 receiver, callbackfn, UndefinedConstant(), result,
2622 receiver, initial_k, len, UndefinedConstant()));
2623 }
2624
TF_BUILTIN(ArrayReduceRight,ArrayBuiltinsAssembler)2625 TF_BUILTIN(ArrayReduceRight, ArrayBuiltinsAssembler) {
2626 TNode<IntPtrT> argc =
2627 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2628 CodeStubArguments args(this, argc);
2629 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2630 TNode<Object> receiver = args.GetReceiver();
2631 Node* callbackfn = args.GetOptionalArgumentValue(0);
2632 Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant());
2633
2634 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value,
2635 argc);
2636
2637 GenerateIteratingArrayBuiltinBody(
2638 "Array.prototype.reduceRight",
2639 &ArrayBuiltinsAssembler::ReduceResultGenerator,
2640 &ArrayBuiltinsAssembler::ReduceProcessor,
2641 &ArrayBuiltinsAssembler::ReducePostLoopAction,
2642 Builtins::CallableFor(isolate(),
2643 Builtins::kArrayReduceRightLoopContinuation),
2644 MissingPropertyMode::kSkip, ForEachDirection::kReverse);
2645 }
2646
TF_BUILTIN(TypedArrayPrototypeReduceRight,ArrayBuiltinsAssembler)2647 TF_BUILTIN(TypedArrayPrototypeReduceRight, ArrayBuiltinsAssembler) {
2648 TNode<IntPtrT> argc =
2649 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2650 CodeStubArguments args(this, argc);
2651 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2652 TNode<Object> receiver = args.GetReceiver();
2653 Node* callbackfn = args.GetOptionalArgumentValue(0);
2654 Node* initial_value = args.GetOptionalArgumentValue(1, TheHoleConstant());
2655
2656 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, initial_value,
2657 argc);
2658
2659 GenerateIteratingTypedArrayBuiltinBody(
2660 "%TypedArray%.prototype.reduceRight",
2661 &ArrayBuiltinsAssembler::ReduceResultGenerator,
2662 &ArrayBuiltinsAssembler::ReduceProcessor,
2663 &ArrayBuiltinsAssembler::ReducePostLoopAction,
2664 ForEachDirection::kReverse);
2665 }
2666
TF_BUILTIN(ArrayFilterLoopContinuation,ArrayBuiltinsAssembler)2667 TF_BUILTIN(ArrayFilterLoopContinuation, ArrayBuiltinsAssembler) {
2668 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2669 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2670 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2671 Node* this_arg = Parameter(Descriptor::kThisArg);
2672 Node* array = Parameter(Descriptor::kArray);
2673 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
2674 Node* initial_k = Parameter(Descriptor::kInitialK);
2675 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2676 Node* to = Parameter(Descriptor::kTo);
2677
2678 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
2679 this_arg, array, object, initial_k,
2680 len, to);
2681
2682 GenerateIteratingArrayBuiltinLoopContinuation(
2683 &ArrayBuiltinsAssembler::FilterProcessor,
2684 &ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip);
2685 }
2686
TF_BUILTIN(ArrayFilterLoopEagerDeoptContinuation,ArrayBuiltinsAssembler)2687 TF_BUILTIN(ArrayFilterLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
2688 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2689 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2690 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2691 Node* this_arg = Parameter(Descriptor::kThisArg);
2692 Node* array = Parameter(Descriptor::kArray);
2693 Node* initial_k = Parameter(Descriptor::kInitialK);
2694 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2695 Node* to = Parameter(Descriptor::kTo);
2696
2697 Return(CallBuiltin(Builtins::kArrayFilterLoopContinuation, context, receiver,
2698 callbackfn, this_arg, array, receiver, initial_k, len,
2699 to));
2700 }
2701
TF_BUILTIN(ArrayFilterLoopLazyDeoptContinuation,ArrayBuiltinsAssembler)2702 TF_BUILTIN(ArrayFilterLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
2703 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2704 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2705 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2706 Node* this_arg = Parameter(Descriptor::kThisArg);
2707 Node* array = Parameter(Descriptor::kArray);
2708 Node* initial_k = Parameter(Descriptor::kInitialK);
2709 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2710 Node* value_k = Parameter(Descriptor::kValueK);
2711 Node* result = Parameter(Descriptor::kResult);
2712
2713 VARIABLE(to, MachineRepresentation::kTagged, Parameter(Descriptor::kTo));
2714
2715 // This custom lazy deopt point is right after the callback. filter() needs
2716 // to pick up at the next step, which is setting the callback result in
2717 // the output array. After incrementing k and to, we can glide into the loop
2718 // continuation builtin.
2719
2720 Label true_continue(this, &to), false_continue(this);
2721
2722 // iii. If selected is true, then...
2723 BranchIfToBooleanIsTrue(result, &true_continue, &false_continue);
2724 BIND(&true_continue);
2725 {
2726 // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue).
2727 CallRuntime(Runtime::kCreateDataProperty, context, array, to.value(),
2728 value_k);
2729 // 2. Increase to by 1.
2730 to.Bind(NumberInc(to.value()));
2731 Goto(&false_continue);
2732 }
2733 BIND(&false_continue);
2734
2735 // Increment k.
2736 initial_k = NumberInc(initial_k);
2737
2738 Return(CallBuiltin(Builtins::kArrayFilterLoopContinuation, context, receiver,
2739 callbackfn, this_arg, array, receiver, initial_k, len,
2740 to.value()));
2741 }
2742
TF_BUILTIN(ArrayFilter,ArrayBuiltinsAssembler)2743 TF_BUILTIN(ArrayFilter, ArrayBuiltinsAssembler) {
2744 TNode<IntPtrT> argc =
2745 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2746 CodeStubArguments args(this, argc);
2747 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2748 TNode<Object> receiver = args.GetReceiver();
2749 Node* callbackfn = args.GetOptionalArgumentValue(0);
2750 Node* this_arg = args.GetOptionalArgumentValue(1);
2751
2752 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
2753
2754 GenerateIteratingArrayBuiltinBody(
2755 "Array.prototype.filter", &ArrayBuiltinsAssembler::FilterResultGenerator,
2756 &ArrayBuiltinsAssembler::FilterProcessor,
2757 &ArrayBuiltinsAssembler::NullPostLoopAction,
2758 Builtins::CallableFor(isolate(), Builtins::kArrayFilterLoopContinuation),
2759 MissingPropertyMode::kSkip);
2760 }
2761
TF_BUILTIN(ArrayMapLoopContinuation,ArrayBuiltinsAssembler)2762 TF_BUILTIN(ArrayMapLoopContinuation, ArrayBuiltinsAssembler) {
2763 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2764 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2765 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2766 Node* this_arg = Parameter(Descriptor::kThisArg);
2767 Node* array = Parameter(Descriptor::kArray);
2768 TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject));
2769 Node* initial_k = Parameter(Descriptor::kInitialK);
2770 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2771 Node* to = Parameter(Descriptor::kTo);
2772
2773 InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn,
2774 this_arg, array, object, initial_k,
2775 len, to);
2776
2777 GenerateIteratingArrayBuiltinLoopContinuation(
2778 &ArrayBuiltinsAssembler::SpecCompliantMapProcessor,
2779 &ArrayBuiltinsAssembler::NullPostLoopAction, MissingPropertyMode::kSkip);
2780 }
2781
TF_BUILTIN(ArrayMapLoopEagerDeoptContinuation,ArrayBuiltinsAssembler)2782 TF_BUILTIN(ArrayMapLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) {
2783 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2784 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2785 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2786 Node* this_arg = Parameter(Descriptor::kThisArg);
2787 Node* array = Parameter(Descriptor::kArray);
2788 Node* initial_k = Parameter(Descriptor::kInitialK);
2789 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2790
2791 Return(CallBuiltin(Builtins::kArrayMapLoopContinuation, context, receiver,
2792 callbackfn, this_arg, array, receiver, initial_k, len,
2793 UndefinedConstant()));
2794 }
2795
TF_BUILTIN(ArrayMapLoopLazyDeoptContinuation,ArrayBuiltinsAssembler)2796 TF_BUILTIN(ArrayMapLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) {
2797 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2798 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
2799 Node* callbackfn = Parameter(Descriptor::kCallbackFn);
2800 Node* this_arg = Parameter(Descriptor::kThisArg);
2801 Node* array = Parameter(Descriptor::kArray);
2802 Node* initial_k = Parameter(Descriptor::kInitialK);
2803 TNode<Number> len = CAST(Parameter(Descriptor::kLength));
2804 Node* result = Parameter(Descriptor::kResult);
2805
2806 // This custom lazy deopt point is right after the callback. map() needs
2807 // to pick up at the next step, which is setting the callback result in
2808 // the output array. After incrementing k, we can glide into the loop
2809 // continuation builtin.
2810
2811 // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
2812 CallRuntime(Runtime::kCreateDataProperty, context, array, initial_k, result);
2813 // Then we have to increment k before going on.
2814 initial_k = NumberInc(initial_k);
2815
2816 Return(CallBuiltin(Builtins::kArrayMapLoopContinuation, context, receiver,
2817 callbackfn, this_arg, array, receiver, initial_k, len,
2818 UndefinedConstant()));
2819 }
2820
TF_BUILTIN(ArrayMap,ArrayBuiltinsAssembler)2821 TF_BUILTIN(ArrayMap, ArrayBuiltinsAssembler) {
2822 TNode<IntPtrT> argc =
2823 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2824 CodeStubArguments args(this, argc);
2825 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2826 TNode<Object> receiver = args.GetReceiver();
2827 Node* callbackfn = args.GetOptionalArgumentValue(0);
2828 Node* this_arg = args.GetOptionalArgumentValue(1);
2829
2830 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
2831
2832 GenerateIteratingArrayBuiltinBody(
2833 "Array.prototype.map", &ArrayBuiltinsAssembler::MapResultGenerator,
2834 &ArrayBuiltinsAssembler::FastMapProcessor,
2835 &ArrayBuiltinsAssembler::NullPostLoopAction,
2836 Builtins::CallableFor(isolate(), Builtins::kArrayMapLoopContinuation),
2837 MissingPropertyMode::kSkip);
2838 }
2839
TF_BUILTIN(TypedArrayPrototypeMap,ArrayBuiltinsAssembler)2840 TF_BUILTIN(TypedArrayPrototypeMap, ArrayBuiltinsAssembler) {
2841 TNode<IntPtrT> argc =
2842 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2843 CodeStubArguments args(this, argc);
2844 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2845 TNode<Object> receiver = args.GetReceiver();
2846 Node* callbackfn = args.GetOptionalArgumentValue(0);
2847 Node* this_arg = args.GetOptionalArgumentValue(1);
2848
2849 InitIteratingArrayBuiltinBody(context, receiver, callbackfn, this_arg, argc);
2850
2851 GenerateIteratingTypedArrayBuiltinBody(
2852 "%TypedArray%.prototype.map",
2853 &ArrayBuiltinsAssembler::TypedArrayMapResultGenerator,
2854 &ArrayBuiltinsAssembler::TypedArrayMapProcessor,
2855 &ArrayBuiltinsAssembler::NullPostLoopAction);
2856 }
2857
TF_BUILTIN(ArrayIsArray,CodeStubAssembler)2858 TF_BUILTIN(ArrayIsArray, CodeStubAssembler) {
2859 TNode<Object> object = CAST(Parameter(Descriptor::kArg));
2860 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
2861
2862 Label call_runtime(this), return_true(this), return_false(this);
2863
2864 GotoIf(TaggedIsSmi(object), &return_false);
2865 TNode<Int32T> instance_type = LoadInstanceType(CAST(object));
2866
2867 GotoIf(InstanceTypeEqual(instance_type, JS_ARRAY_TYPE), &return_true);
2868
2869 // TODO(verwaest): Handle proxies in-place.
2870 Branch(InstanceTypeEqual(instance_type, JS_PROXY_TYPE), &call_runtime,
2871 &return_false);
2872
2873 BIND(&return_true);
2874 Return(TrueConstant());
2875
2876 BIND(&return_false);
2877 Return(FalseConstant());
2878
2879 BIND(&call_runtime);
2880 Return(CallRuntime(Runtime::kArrayIsArray, context, object));
2881 }
2882
2883 class ArrayIncludesIndexofAssembler : public CodeStubAssembler {
2884 public:
ArrayIncludesIndexofAssembler(compiler::CodeAssemblerState * state)2885 explicit ArrayIncludesIndexofAssembler(compiler::CodeAssemblerState* state)
2886 : CodeStubAssembler(state) {}
2887
2888 enum SearchVariant { kIncludes, kIndexOf };
2889
2890 void Generate(SearchVariant variant);
2891 void GenerateSmiOrObject(SearchVariant variant, Node* context, Node* elements,
2892 Node* search_element, Node* array_length,
2893 Node* from_index);
2894 void GeneratePackedDoubles(SearchVariant variant, Node* elements,
2895 Node* search_element, Node* array_length,
2896 Node* from_index);
2897 void GenerateHoleyDoubles(SearchVariant variant, Node* elements,
2898 Node* search_element, Node* array_length,
2899 Node* from_index);
2900 };
2901
Generate(SearchVariant variant)2902 void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant) {
2903 const int kSearchElementArg = 0;
2904 const int kFromIndexArg = 1;
2905
2906 TNode<IntPtrT> argc =
2907 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
2908 CodeStubArguments args(this, argc);
2909
2910 TNode<Object> receiver = args.GetReceiver();
2911 TNode<Object> search_element =
2912 args.GetOptionalArgumentValue(kSearchElementArg);
2913 TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
2914
2915 Node* intptr_zero = IntPtrConstant(0);
2916
2917 Label init_index(this), return_not_found(this), call_runtime(this);
2918
2919 // Take slow path if not a JSArray, if retrieving elements requires
2920 // traversing prototype, or if access checks are required.
2921 BranchIfFastJSArray(receiver, context, &init_index, &call_runtime);
2922
2923 BIND(&init_index);
2924 VARIABLE(index_var, MachineType::PointerRepresentation(), intptr_zero);
2925 TNode<JSArray> array = CAST(receiver);
2926
2927 // JSArray length is always a positive Smi for fast arrays.
2928 CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array)));
2929 Node* array_length = LoadFastJSArrayLength(array);
2930 Node* array_length_untagged = SmiUntag(array_length);
2931
2932 {
2933 // Initialize fromIndex.
2934 Label is_smi(this), is_nonsmi(this), done(this);
2935
2936 // If no fromIndex was passed, default to 0.
2937 GotoIf(IntPtrLessThanOrEqual(argc, IntPtrConstant(kFromIndexArg)), &done);
2938
2939 Node* start_from = args.AtIndex(kFromIndexArg);
2940 // Handle Smis and undefined here and everything else in runtime.
2941 // We must be very careful with side effects from the ToInteger conversion,
2942 // as the side effects might render previously checked assumptions about
2943 // the receiver being a fast JSArray and its length invalid.
2944 Branch(TaggedIsSmi(start_from), &is_smi, &is_nonsmi);
2945
2946 BIND(&is_nonsmi);
2947 {
2948 GotoIfNot(IsUndefined(start_from), &call_runtime);
2949 Goto(&done);
2950 }
2951 BIND(&is_smi);
2952 {
2953 Node* intptr_start_from = SmiUntag(start_from);
2954 index_var.Bind(intptr_start_from);
2955
2956 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done);
2957 // The fromIndex is negative: add it to the array's length.
2958 index_var.Bind(IntPtrAdd(array_length_untagged, index_var.value()));
2959 // Clamp negative results at zero.
2960 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done);
2961 index_var.Bind(intptr_zero);
2962 Goto(&done);
2963 }
2964 BIND(&done);
2965 }
2966
2967 // Fail early if startIndex >= array.length.
2968 GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length_untagged),
2969 &return_not_found);
2970
2971 Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this);
2972
2973 Node* elements_kind = LoadMapElementsKind(LoadMap(array));
2974 Node* elements = LoadElements(array);
2975 STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
2976 STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
2977 STATIC_ASSERT(PACKED_ELEMENTS == 2);
2978 STATIC_ASSERT(HOLEY_ELEMENTS == 3);
2979 GotoIf(Uint32LessThanOrEqual(elements_kind, Int32Constant(HOLEY_ELEMENTS)),
2980 &if_smiorobjects);
2981 GotoIf(Word32Equal(elements_kind, Int32Constant(PACKED_DOUBLE_ELEMENTS)),
2982 &if_packed_doubles);
2983 GotoIf(Word32Equal(elements_kind, Int32Constant(HOLEY_DOUBLE_ELEMENTS)),
2984 &if_holey_doubles);
2985 Goto(&return_not_found);
2986
2987 BIND(&if_smiorobjects);
2988 {
2989 Callable callable =
2990 (variant == kIncludes)
2991 ? Builtins::CallableFor(isolate(),
2992 Builtins::kArrayIncludesSmiOrObject)
2993 : Builtins::CallableFor(isolate(),
2994 Builtins::kArrayIndexOfSmiOrObject);
2995 Node* result = CallStub(callable, context, elements, search_element,
2996 array_length, SmiTag(index_var.value()));
2997 args.PopAndReturn(result);
2998 }
2999
3000 BIND(&if_packed_doubles);
3001 {
3002 Callable callable =
3003 (variant == kIncludes)
3004 ? Builtins::CallableFor(isolate(),
3005 Builtins::kArrayIncludesPackedDoubles)
3006 : Builtins::CallableFor(isolate(),
3007 Builtins::kArrayIndexOfPackedDoubles);
3008 Node* result = CallStub(callable, context, elements, search_element,
3009 array_length, SmiTag(index_var.value()));
3010 args.PopAndReturn(result);
3011 }
3012
3013 BIND(&if_holey_doubles);
3014 {
3015 Callable callable =
3016 (variant == kIncludes)
3017 ? Builtins::CallableFor(isolate(),
3018 Builtins::kArrayIncludesHoleyDoubles)
3019 : Builtins::CallableFor(isolate(),
3020 Builtins::kArrayIndexOfHoleyDoubles);
3021 Node* result = CallStub(callable, context, elements, search_element,
3022 array_length, SmiTag(index_var.value()));
3023 args.PopAndReturn(result);
3024 }
3025
3026 BIND(&return_not_found);
3027 if (variant == kIncludes) {
3028 args.PopAndReturn(FalseConstant());
3029 } else {
3030 args.PopAndReturn(NumberConstant(-1));
3031 }
3032
3033 BIND(&call_runtime);
3034 {
3035 Node* start_from =
3036 args.GetOptionalArgumentValue(kFromIndexArg, UndefinedConstant());
3037 Runtime::FunctionId function = variant == kIncludes
3038 ? Runtime::kArrayIncludes_Slow
3039 : Runtime::kArrayIndexOf;
3040 args.PopAndReturn(
3041 CallRuntime(function, context, array, search_element, start_from));
3042 }
3043 }
3044
GenerateSmiOrObject(SearchVariant variant,Node * context,Node * elements,Node * search_element,Node * array_length,Node * from_index)3045 void ArrayIncludesIndexofAssembler::GenerateSmiOrObject(
3046 SearchVariant variant, Node* context, Node* elements, Node* search_element,
3047 Node* array_length, Node* from_index) {
3048 VARIABLE(index_var, MachineType::PointerRepresentation(),
3049 SmiUntag(from_index));
3050 VARIABLE(search_num, MachineRepresentation::kFloat64);
3051 Node* array_length_untagged = SmiUntag(array_length);
3052
3053 Label ident_loop(this, &index_var), heap_num_loop(this, &search_num),
3054 string_loop(this), bigint_loop(this, &index_var),
3055 undef_loop(this, &index_var), not_smi(this), not_heap_num(this),
3056 return_found(this), return_not_found(this);
3057
3058 GotoIfNot(TaggedIsSmi(search_element), ¬_smi);
3059 search_num.Bind(SmiToFloat64(search_element));
3060 Goto(&heap_num_loop);
3061
3062 BIND(¬_smi);
3063 if (variant == kIncludes) {
3064 GotoIf(IsUndefined(search_element), &undef_loop);
3065 }
3066 Node* map = LoadMap(search_element);
3067 GotoIfNot(IsHeapNumberMap(map), ¬_heap_num);
3068 search_num.Bind(LoadHeapNumberValue(search_element));
3069 Goto(&heap_num_loop);
3070
3071 BIND(¬_heap_num);
3072 Node* search_type = LoadMapInstanceType(map);
3073 GotoIf(IsStringInstanceType(search_type), &string_loop);
3074 GotoIf(IsBigIntInstanceType(search_type), &bigint_loop);
3075 Goto(&ident_loop);
3076
3077 BIND(&ident_loop);
3078 {
3079 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3080 &return_not_found);
3081 Node* element_k = LoadFixedArrayElement(elements, index_var.value());
3082 GotoIf(WordEqual(element_k, search_element), &return_found);
3083
3084 Increment(&index_var);
3085 Goto(&ident_loop);
3086 }
3087
3088 if (variant == kIncludes) {
3089 BIND(&undef_loop);
3090
3091 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3092 &return_not_found);
3093 Node* element_k = LoadFixedArrayElement(elements, index_var.value());
3094 GotoIf(IsUndefined(element_k), &return_found);
3095 GotoIf(IsTheHole(element_k), &return_found);
3096
3097 Increment(&index_var);
3098 Goto(&undef_loop);
3099 }
3100
3101 BIND(&heap_num_loop);
3102 {
3103 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var);
3104 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
3105 BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop);
3106
3107 BIND(¬_nan_loop);
3108 {
3109 Label continue_loop(this), not_smi(this);
3110 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3111 &return_not_found);
3112 Node* element_k = LoadFixedArrayElement(elements, index_var.value());
3113 GotoIfNot(TaggedIsSmi(element_k), ¬_smi);
3114 Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)),
3115 &return_found, &continue_loop);
3116
3117 BIND(¬_smi);
3118 GotoIfNot(IsHeapNumber(element_k), &continue_loop);
3119 Branch(Float64Equal(search_num.value(), LoadHeapNumberValue(element_k)),
3120 &return_found, &continue_loop);
3121
3122 BIND(&continue_loop);
3123 Increment(&index_var);
3124 Goto(¬_nan_loop);
3125 }
3126
3127 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
3128 if (variant == kIncludes) {
3129 BIND(&nan_loop);
3130 Label continue_loop(this);
3131 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3132 &return_not_found);
3133 Node* element_k = LoadFixedArrayElement(elements, index_var.value());
3134 GotoIf(TaggedIsSmi(element_k), &continue_loop);
3135 GotoIfNot(IsHeapNumber(CAST(element_k)), &continue_loop);
3136 BranchIfFloat64IsNaN(LoadHeapNumberValue(element_k), &return_found,
3137 &continue_loop);
3138
3139 BIND(&continue_loop);
3140 Increment(&index_var);
3141 Goto(&nan_loop);
3142 }
3143 }
3144
3145 BIND(&string_loop);
3146 {
3147 TNode<String> search_element_string = CAST(search_element);
3148 Label continue_loop(this), next_iteration(this, &index_var),
3149 slow_compare(this), runtime(this, Label::kDeferred);
3150 TNode<IntPtrT> search_length =
3151 LoadStringLengthAsWord(search_element_string);
3152 Goto(&next_iteration);
3153 BIND(&next_iteration);
3154 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3155 &return_not_found);
3156 Node* element_k = LoadFixedArrayElement(elements, index_var.value());
3157 GotoIf(TaggedIsSmi(element_k), &continue_loop);
3158 GotoIf(WordEqual(search_element_string, element_k), &return_found);
3159 Node* element_k_type = LoadInstanceType(element_k);
3160 GotoIfNot(IsStringInstanceType(element_k_type), &continue_loop);
3161 Branch(WordEqual(search_length, LoadStringLengthAsWord(element_k)),
3162 &slow_compare, &continue_loop);
3163
3164 BIND(&slow_compare);
3165 StringBuiltinsAssembler string_asm(state());
3166 string_asm.StringEqual_Core(context, search_element_string, search_type,
3167 element_k, element_k_type, search_length,
3168 &return_found, &continue_loop, &runtime);
3169 BIND(&runtime);
3170 TNode<Object> result = CallRuntime(Runtime::kStringEqual, context,
3171 search_element_string, element_k);
3172 Branch(WordEqual(result, TrueConstant()), &return_found, &continue_loop);
3173
3174 BIND(&continue_loop);
3175 Increment(&index_var);
3176 Goto(&next_iteration);
3177 }
3178
3179 BIND(&bigint_loop);
3180 {
3181 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3182 &return_not_found);
3183
3184 Node* element_k = LoadFixedArrayElement(elements, index_var.value());
3185 Label continue_loop(this);
3186 GotoIf(TaggedIsSmi(element_k), &continue_loop);
3187 GotoIfNot(IsBigInt(CAST(element_k)), &continue_loop);
3188 TNode<Object> result = CallRuntime(Runtime::kBigIntEqualToBigInt, context,
3189 search_element, element_k);
3190 Branch(WordEqual(result, TrueConstant()), &return_found, &continue_loop);
3191
3192 BIND(&continue_loop);
3193 Increment(&index_var);
3194 Goto(&bigint_loop);
3195 }
3196 BIND(&return_found);
3197 if (variant == kIncludes) {
3198 Return(TrueConstant());
3199 } else {
3200 Return(SmiTag(index_var.value()));
3201 }
3202
3203 BIND(&return_not_found);
3204 if (variant == kIncludes) {
3205 Return(FalseConstant());
3206 } else {
3207 Return(NumberConstant(-1));
3208 }
3209 }
3210
GeneratePackedDoubles(SearchVariant variant,Node * elements,Node * search_element,Node * array_length,Node * from_index)3211 void ArrayIncludesIndexofAssembler::GeneratePackedDoubles(SearchVariant variant,
3212 Node* elements,
3213 Node* search_element,
3214 Node* array_length,
3215 Node* from_index) {
3216 VARIABLE(index_var, MachineType::PointerRepresentation(),
3217 SmiUntag(from_index));
3218 Node* array_length_untagged = SmiUntag(array_length);
3219
3220 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var),
3221 hole_loop(this, &index_var), search_notnan(this), return_found(this),
3222 return_not_found(this);
3223 VARIABLE(search_num, MachineRepresentation::kFloat64);
3224 search_num.Bind(Float64Constant(0));
3225
3226 GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
3227 search_num.Bind(SmiToFloat64(search_element));
3228 Goto(¬_nan_loop);
3229
3230 BIND(&search_notnan);
3231 GotoIfNot(IsHeapNumber(search_element), &return_not_found);
3232
3233 search_num.Bind(LoadHeapNumberValue(search_element));
3234
3235 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
3236 BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop);
3237
3238 BIND(¬_nan_loop);
3239 {
3240 Label continue_loop(this);
3241 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3242 &return_not_found);
3243 Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(),
3244 MachineType::Float64());
3245 Branch(Float64Equal(element_k, search_num.value()), &return_found,
3246 &continue_loop);
3247 BIND(&continue_loop);
3248 Increment(&index_var);
3249 Goto(¬_nan_loop);
3250 }
3251
3252 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
3253 if (variant == kIncludes) {
3254 BIND(&nan_loop);
3255 Label continue_loop(this);
3256 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3257 &return_not_found);
3258 Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(),
3259 MachineType::Float64());
3260 BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop);
3261 BIND(&continue_loop);
3262 Increment(&index_var);
3263 Goto(&nan_loop);
3264 }
3265
3266 BIND(&return_found);
3267 if (variant == kIncludes) {
3268 Return(TrueConstant());
3269 } else {
3270 Return(SmiTag(index_var.value()));
3271 }
3272
3273 BIND(&return_not_found);
3274 if (variant == kIncludes) {
3275 Return(FalseConstant());
3276 } else {
3277 Return(NumberConstant(-1));
3278 }
3279 }
3280
GenerateHoleyDoubles(SearchVariant variant,Node * elements,Node * search_element,Node * array_length,Node * from_index)3281 void ArrayIncludesIndexofAssembler::GenerateHoleyDoubles(SearchVariant variant,
3282 Node* elements,
3283 Node* search_element,
3284 Node* array_length,
3285 Node* from_index) {
3286 VARIABLE(index_var, MachineType::PointerRepresentation(),
3287 SmiUntag(from_index));
3288 Node* array_length_untagged = SmiUntag(array_length);
3289
3290 Label nan_loop(this, &index_var), not_nan_loop(this, &index_var),
3291 hole_loop(this, &index_var), search_notnan(this), return_found(this),
3292 return_not_found(this);
3293 VARIABLE(search_num, MachineRepresentation::kFloat64);
3294 search_num.Bind(Float64Constant(0));
3295
3296 GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
3297 search_num.Bind(SmiToFloat64(search_element));
3298 Goto(¬_nan_loop);
3299
3300 BIND(&search_notnan);
3301 if (variant == kIncludes) {
3302 GotoIf(IsUndefined(search_element), &hole_loop);
3303 }
3304 GotoIfNot(IsHeapNumber(search_element), &return_not_found);
3305
3306 search_num.Bind(LoadHeapNumberValue(search_element));
3307
3308 Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found;
3309 BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop);
3310
3311 BIND(¬_nan_loop);
3312 {
3313 Label continue_loop(this);
3314 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3315 &return_not_found);
3316
3317 // No need for hole checking here; the following Float64Equal will
3318 // return 'not equal' for holes anyway.
3319 Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(),
3320 MachineType::Float64());
3321
3322 Branch(Float64Equal(element_k, search_num.value()), &return_found,
3323 &continue_loop);
3324 BIND(&continue_loop);
3325 Increment(&index_var);
3326 Goto(¬_nan_loop);
3327 }
3328
3329 // Array.p.includes uses SameValueZero comparisons, where NaN == NaN.
3330 if (variant == kIncludes) {
3331 BIND(&nan_loop);
3332 Label continue_loop(this);
3333 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3334 &return_not_found);
3335
3336 // Load double value or continue if it's the hole NaN.
3337 Node* element_k = LoadFixedDoubleArrayElement(
3338 elements, index_var.value(), MachineType::Float64(), 0,
3339 INTPTR_PARAMETERS, &continue_loop);
3340
3341 BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop);
3342 BIND(&continue_loop);
3343 Increment(&index_var);
3344 Goto(&nan_loop);
3345 }
3346
3347 // Array.p.includes treats the hole as undefined.
3348 if (variant == kIncludes) {
3349 BIND(&hole_loop);
3350 GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged),
3351 &return_not_found);
3352
3353 // Check if the element is a double hole, but don't load it.
3354 LoadFixedDoubleArrayElement(elements, index_var.value(),
3355 MachineType::None(), 0, INTPTR_PARAMETERS,
3356 &return_found);
3357
3358 Increment(&index_var);
3359 Goto(&hole_loop);
3360 }
3361
3362 BIND(&return_found);
3363 if (variant == kIncludes) {
3364 Return(TrueConstant());
3365 } else {
3366 Return(SmiTag(index_var.value()));
3367 }
3368
3369 BIND(&return_not_found);
3370 if (variant == kIncludes) {
3371 Return(FalseConstant());
3372 } else {
3373 Return(NumberConstant(-1));
3374 }
3375 }
3376
TF_BUILTIN(ArrayIncludes,ArrayIncludesIndexofAssembler)3377 TF_BUILTIN(ArrayIncludes, ArrayIncludesIndexofAssembler) {
3378 Generate(kIncludes);
3379 }
3380
TF_BUILTIN(ArrayIncludesSmiOrObject,ArrayIncludesIndexofAssembler)3381 TF_BUILTIN(ArrayIncludesSmiOrObject, ArrayIncludesIndexofAssembler) {
3382 Node* context = Parameter(Descriptor::kContext);
3383 Node* elements = Parameter(Descriptor::kElements);
3384 Node* search_element = Parameter(Descriptor::kSearchElement);
3385 Node* array_length = Parameter(Descriptor::kLength);
3386 Node* from_index = Parameter(Descriptor::kFromIndex);
3387
3388 GenerateSmiOrObject(kIncludes, context, elements, search_element,
3389 array_length, from_index);
3390 }
3391
TF_BUILTIN(ArrayIncludesPackedDoubles,ArrayIncludesIndexofAssembler)3392 TF_BUILTIN(ArrayIncludesPackedDoubles, ArrayIncludesIndexofAssembler) {
3393 Node* elements = Parameter(Descriptor::kElements);
3394 Node* search_element = Parameter(Descriptor::kSearchElement);
3395 Node* array_length = Parameter(Descriptor::kLength);
3396 Node* from_index = Parameter(Descriptor::kFromIndex);
3397
3398 GeneratePackedDoubles(kIncludes, elements, search_element, array_length,
3399 from_index);
3400 }
3401
TF_BUILTIN(ArrayIncludesHoleyDoubles,ArrayIncludesIndexofAssembler)3402 TF_BUILTIN(ArrayIncludesHoleyDoubles, ArrayIncludesIndexofAssembler) {
3403 Node* elements = Parameter(Descriptor::kElements);
3404 Node* search_element = Parameter(Descriptor::kSearchElement);
3405 Node* array_length = Parameter(Descriptor::kLength);
3406 Node* from_index = Parameter(Descriptor::kFromIndex);
3407
3408 GenerateHoleyDoubles(kIncludes, elements, search_element, array_length,
3409 from_index);
3410 }
3411
TF_BUILTIN(ArrayIndexOf,ArrayIncludesIndexofAssembler)3412 TF_BUILTIN(ArrayIndexOf, ArrayIncludesIndexofAssembler) { Generate(kIndexOf); }
3413
TF_BUILTIN(ArrayIndexOfSmiOrObject,ArrayIncludesIndexofAssembler)3414 TF_BUILTIN(ArrayIndexOfSmiOrObject, ArrayIncludesIndexofAssembler) {
3415 Node* context = Parameter(Descriptor::kContext);
3416 Node* elements = Parameter(Descriptor::kElements);
3417 Node* search_element = Parameter(Descriptor::kSearchElement);
3418 Node* array_length = Parameter(Descriptor::kLength);
3419 Node* from_index = Parameter(Descriptor::kFromIndex);
3420
3421 GenerateSmiOrObject(kIndexOf, context, elements, search_element, array_length,
3422 from_index);
3423 }
3424
TF_BUILTIN(ArrayIndexOfPackedDoubles,ArrayIncludesIndexofAssembler)3425 TF_BUILTIN(ArrayIndexOfPackedDoubles, ArrayIncludesIndexofAssembler) {
3426 Node* elements = Parameter(Descriptor::kElements);
3427 Node* search_element = Parameter(Descriptor::kSearchElement);
3428 Node* array_length = Parameter(Descriptor::kLength);
3429 Node* from_index = Parameter(Descriptor::kFromIndex);
3430
3431 GeneratePackedDoubles(kIndexOf, elements, search_element, array_length,
3432 from_index);
3433 }
3434
TF_BUILTIN(ArrayIndexOfHoleyDoubles,ArrayIncludesIndexofAssembler)3435 TF_BUILTIN(ArrayIndexOfHoleyDoubles, ArrayIncludesIndexofAssembler) {
3436 Node* elements = Parameter(Descriptor::kElements);
3437 Node* search_element = Parameter(Descriptor::kSearchElement);
3438 Node* array_length = Parameter(Descriptor::kLength);
3439 Node* from_index = Parameter(Descriptor::kFromIndex);
3440
3441 GenerateHoleyDoubles(kIndexOf, elements, search_element, array_length,
3442 from_index);
3443 }
3444
3445 // ES #sec-array.prototype.values
TF_BUILTIN(ArrayPrototypeValues,CodeStubAssembler)3446 TF_BUILTIN(ArrayPrototypeValues, CodeStubAssembler) {
3447 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3448 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
3449 Return(CreateArrayIterator(context, ToObject(context, receiver),
3450 IterationKind::kValues));
3451 }
3452
3453 // ES #sec-array.prototype.entries
TF_BUILTIN(ArrayPrototypeEntries,CodeStubAssembler)3454 TF_BUILTIN(ArrayPrototypeEntries, CodeStubAssembler) {
3455 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3456 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
3457 Return(CreateArrayIterator(context, ToObject(context, receiver),
3458 IterationKind::kEntries));
3459 }
3460
3461 // ES #sec-array.prototype.keys
TF_BUILTIN(ArrayPrototypeKeys,CodeStubAssembler)3462 TF_BUILTIN(ArrayPrototypeKeys, CodeStubAssembler) {
3463 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3464 TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
3465 Return(CreateArrayIterator(context, ToObject(context, receiver),
3466 IterationKind::kKeys));
3467 }
3468
3469 // ES #sec-%arrayiteratorprototype%.next
TF_BUILTIN(ArrayIteratorPrototypeNext,CodeStubAssembler)3470 TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) {
3471 const char* method_name = "Array Iterator.prototype.next";
3472
3473 TNode<Context> context = CAST(Parameter(Descriptor::kContext));
3474 Node* iterator = Parameter(Descriptor::kReceiver);
3475
3476 VARIABLE(var_value, MachineRepresentation::kTagged);
3477 VARIABLE(var_done, MachineRepresentation::kTagged);
3478
3479 // Required, or else `throw_bad_receiver` fails a DCHECK due to these
3480 // variables not being bound along all paths, despite not being used.
3481 var_done.Bind(TrueConstant());
3482 var_value.Bind(UndefinedConstant());
3483
3484 Label throw_bad_receiver(this, Label::kDeferred);
3485 Label set_done(this);
3486 Label allocate_entry_if_needed(this);
3487 Label allocate_iterator_result(this);
3488
3489 // If O does not have all of the internal slots of an Array Iterator Instance
3490 // (22.1.5.3), throw a TypeError exception
3491 GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver);
3492 GotoIfNot(IsJSArrayIterator(CAST(iterator)), &throw_bad_receiver);
3493
3494 // Let a be O.[[IteratedObject]].
3495 Node* array =
3496 LoadObjectField(iterator, JSArrayIterator::kIteratedObjectOffset);
3497
3498 // Let index be O.[[ArrayIteratorNextIndex]].
3499 Node* index = LoadObjectField(iterator, JSArrayIterator::kNextIndexOffset);
3500 Node* array_map = LoadMap(array);
3501
3502 Label if_detached(this, Label::kDeferred);
3503
3504 Label if_typedarray(this), if_other(this, Label::kDeferred), if_array(this),
3505 if_generic(this, Label::kDeferred);
3506
3507 Node* array_type = LoadInstanceType(array);
3508 GotoIf(InstanceTypeEqual(array_type, JS_ARRAY_TYPE), &if_array);
3509 Branch(InstanceTypeEqual(array_type, JS_TYPED_ARRAY_TYPE), &if_typedarray,
3510 &if_other);
3511
3512 BIND(&if_array);
3513 {
3514 // We can only handle fast elements here.
3515 Node* elements_kind = LoadMapElementsKind(array_map);
3516 GotoIfNot(IsFastElementsKind(elements_kind), &if_other);
3517
3518 TNode<Smi> length = CAST(LoadJSArrayLength(array));
3519
3520 GotoIfNot(SmiBelow(CAST(index), length), &set_done);
3521
3522 var_value.Bind(index);
3523 TNode<Smi> one = SmiConstant(1);
3524 StoreObjectFieldNoWriteBarrier(iterator, JSArrayIterator::kNextIndexOffset,
3525 SmiAdd(CAST(index), one));
3526 var_done.Bind(FalseConstant());
3527
3528 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
3529 iterator, JSArrayIterator::kKindOffset),
3530 Int32Constant(static_cast<int>(IterationKind::kKeys))),
3531 &allocate_iterator_result);
3532
3533 Node* elements = LoadElements(array);
3534 Label if_packed(this), if_holey(this), if_packed_double(this),
3535 if_holey_double(this), if_unknown_kind(this, Label::kDeferred);
3536 int32_t kinds[] = {// Handled by if_packed.
3537 PACKED_SMI_ELEMENTS, PACKED_ELEMENTS,
3538 // Handled by if_holey.
3539 HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS,
3540 // Handled by if_packed_double.
3541 PACKED_DOUBLE_ELEMENTS,
3542 // Handled by if_holey_double.
3543 HOLEY_DOUBLE_ELEMENTS};
3544 Label* labels[] = {// PACKED_{SMI,}_ELEMENTS
3545 &if_packed, &if_packed,
3546 // HOLEY_{SMI,}_ELEMENTS
3547 &if_holey, &if_holey,
3548 // PACKED_DOUBLE_ELEMENTS
3549 &if_packed_double,
3550 // HOLEY_DOUBLE_ELEMENTS
3551 &if_holey_double};
3552 Switch(elements_kind, &if_unknown_kind, kinds, labels, arraysize(kinds));
3553
3554 BIND(&if_packed);
3555 {
3556 var_value.Bind(LoadFixedArrayElement(elements, index, 0, SMI_PARAMETERS));
3557 Goto(&allocate_entry_if_needed);
3558 }
3559
3560 BIND(&if_holey);
3561 {
3562 Node* element = LoadFixedArrayElement(elements, index, 0, SMI_PARAMETERS);
3563 var_value.Bind(element);
3564 GotoIfNot(WordEqual(element, TheHoleConstant()),
3565 &allocate_entry_if_needed);
3566 GotoIf(IsNoElementsProtectorCellInvalid(), &if_generic);
3567 var_value.Bind(UndefinedConstant());
3568 Goto(&allocate_entry_if_needed);
3569 }
3570
3571 BIND(&if_packed_double);
3572 {
3573 Node* value = LoadFixedDoubleArrayElement(
3574 elements, index, MachineType::Float64(), 0, SMI_PARAMETERS);
3575 var_value.Bind(AllocateHeapNumberWithValue(value));
3576 Goto(&allocate_entry_if_needed);
3577 }
3578
3579 BIND(&if_holey_double);
3580 {
3581 Label if_hole(this, Label::kDeferred);
3582 Node* value = LoadFixedDoubleArrayElement(
3583 elements, index, MachineType::Float64(), 0, SMI_PARAMETERS, &if_hole);
3584 var_value.Bind(AllocateHeapNumberWithValue(value));
3585 Goto(&allocate_entry_if_needed);
3586 BIND(&if_hole);
3587 GotoIf(IsNoElementsProtectorCellInvalid(), &if_generic);
3588 var_value.Bind(UndefinedConstant());
3589 Goto(&allocate_entry_if_needed);
3590 }
3591
3592 BIND(&if_unknown_kind);
3593 Unreachable();
3594 }
3595
3596 BIND(&if_other);
3597 {
3598 // If a is undefined, return CreateIterResultObject(undefined, true)
3599 GotoIf(IsUndefined(array), &allocate_iterator_result);
3600
3601 Node* length =
3602 CallBuiltin(Builtins::kToLength, context,
3603 GetProperty(context, array, factory()->length_string()));
3604
3605 GotoIfNumberGreaterThanOrEqual(index, length, &set_done);
3606
3607 var_value.Bind(index);
3608 StoreObjectField(iterator, JSArrayIterator::kNextIndexOffset,
3609 NumberInc(index));
3610 var_done.Bind(FalseConstant());
3611
3612 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
3613 iterator, JSArrayIterator::kKindOffset),
3614 Int32Constant(static_cast<int>(IterationKind::kKeys))),
3615 &allocate_iterator_result);
3616 Goto(&if_generic);
3617 }
3618
3619 BIND(&if_generic);
3620 {
3621 var_value.Bind(GetProperty(context, array, index));
3622 Goto(&allocate_entry_if_needed);
3623 }
3624
3625 BIND(&if_typedarray);
3626 {
3627 Node* buffer = LoadObjectField(array, JSTypedArray::kBufferOffset);
3628 GotoIf(IsDetachedBuffer(buffer), &if_detached);
3629
3630 TNode<Smi> length =
3631 CAST(LoadObjectField(array, JSTypedArray::kLengthOffset));
3632
3633 GotoIfNot(SmiBelow(CAST(index), length), &set_done);
3634
3635 var_value.Bind(index);
3636 TNode<Smi> one = SmiConstant(1);
3637 StoreObjectFieldNoWriteBarrier(iterator, JSArrayIterator::kNextIndexOffset,
3638 SmiAdd(CAST(index), one));
3639 var_done.Bind(FalseConstant());
3640
3641 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
3642 iterator, JSArrayIterator::kKindOffset),
3643 Int32Constant(static_cast<int>(IterationKind::kKeys))),
3644 &allocate_iterator_result);
3645
3646 Node* elements_kind = LoadMapElementsKind(array_map);
3647 Node* elements = LoadElements(array);
3648 Node* base_ptr =
3649 LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset);
3650 Node* external_ptr =
3651 LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset,
3652 MachineType::Pointer());
3653 Node* data_ptr = IntPtrAdd(BitcastTaggedToWord(base_ptr), external_ptr);
3654
3655 Label if_unknown_type(this, Label::kDeferred);
3656 int32_t elements_kinds[] = {
3657 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) TYPE##_ELEMENTS,
3658 TYPED_ARRAYS(TYPED_ARRAY_CASE)
3659 #undef TYPED_ARRAY_CASE
3660 };
3661
3662 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
3663 Label if_##type##array(this);
3664 TYPED_ARRAYS(TYPED_ARRAY_CASE)
3665 #undef TYPED_ARRAY_CASE
3666
3667 Label* elements_kind_labels[] = {
3668 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) &if_##type##array,
3669 TYPED_ARRAYS(TYPED_ARRAY_CASE)
3670 #undef TYPED_ARRAY_CASE
3671 };
3672 STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels));
3673
3674 Switch(elements_kind, &if_unknown_type, elements_kinds,
3675 elements_kind_labels, arraysize(elements_kinds));
3676
3677 BIND(&if_unknown_type);
3678 Unreachable();
3679
3680 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
3681 BIND(&if_##type##array); \
3682 { \
3683 var_value.Bind(LoadFixedTypedArrayElementAsTagged( \
3684 data_ptr, index, TYPE##_ELEMENTS, SMI_PARAMETERS)); \
3685 Goto(&allocate_entry_if_needed); \
3686 }
3687 TYPED_ARRAYS(TYPED_ARRAY_CASE)
3688 #undef TYPED_ARRAY_CASE
3689
3690 BIND(&if_detached);
3691 ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
3692 }
3693
3694 BIND(&set_done);
3695 {
3696 StoreObjectFieldNoWriteBarrier(
3697 iterator, JSArrayIterator::kIteratedObjectOffset, UndefinedConstant());
3698 Goto(&allocate_iterator_result);
3699 }
3700
3701 BIND(&allocate_entry_if_needed);
3702 {
3703 GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField(
3704 iterator, JSArrayIterator::kKindOffset),
3705 Int32Constant(static_cast<int>(IterationKind::kValues))),
3706 &allocate_iterator_result);
3707
3708 Node* elements = AllocateFixedArray(PACKED_ELEMENTS, IntPtrConstant(2));
3709 StoreFixedArrayElement(elements, 0, index, SKIP_WRITE_BARRIER);
3710 StoreFixedArrayElement(elements, 1, var_value.value(), SKIP_WRITE_BARRIER);
3711
3712 Node* entry = Allocate(JSArray::kSize);
3713 Node* map = LoadContextElement(LoadNativeContext(context),
3714 Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX);
3715
3716 StoreMapNoWriteBarrier(entry, map);
3717 StoreObjectFieldRoot(entry, JSArray::kPropertiesOrHashOffset,
3718 Heap::kEmptyFixedArrayRootIndex);
3719 StoreObjectFieldNoWriteBarrier(entry, JSArray::kElementsOffset, elements);
3720 StoreObjectFieldNoWriteBarrier(entry, JSArray::kLengthOffset,
3721 SmiConstant(2));
3722
3723 var_value.Bind(entry);
3724 Goto(&allocate_iterator_result);
3725 }
3726
3727 BIND(&allocate_iterator_result);
3728 {
3729 Node* result = Allocate(JSIteratorResult::kSize);
3730 Node* map = LoadContextElement(LoadNativeContext(context),
3731 Context::ITERATOR_RESULT_MAP_INDEX);
3732 StoreMapNoWriteBarrier(result, map);
3733 StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOrHashOffset,
3734 Heap::kEmptyFixedArrayRootIndex);
3735 StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset,
3736 Heap::kEmptyFixedArrayRootIndex);
3737 StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset,
3738 var_value.value());
3739 StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset,
3740 var_done.value());
3741 Return(result);
3742 }
3743
3744 BIND(&throw_bad_receiver);
3745 {
3746 // The {receiver} is not a valid JSArrayIterator.
3747 ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
3748 StringConstant(method_name), iterator);
3749 }
3750 }
3751
3752 namespace {
3753
3754 class ArrayFlattenAssembler : public CodeStubAssembler {
3755 public:
ArrayFlattenAssembler(compiler::CodeAssemblerState * state)3756 explicit ArrayFlattenAssembler(compiler::CodeAssemblerState* state)
3757 : CodeStubAssembler(state) {}
3758
3759 // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
FlattenIntoArray(Node * context,Node * target,Node * source,Node * source_length,Node * start,Node * depth,Node * mapper_function=nullptr,Node * this_arg=nullptr)3760 Node* FlattenIntoArray(Node* context, Node* target, Node* source,
3761 Node* source_length, Node* start, Node* depth,
3762 Node* mapper_function = nullptr,
3763 Node* this_arg = nullptr) {
3764 CSA_ASSERT(this, IsJSReceiver(target));
3765 CSA_ASSERT(this, IsJSReceiver(source));
3766 CSA_ASSERT(this, IsNumberPositive(source_length));
3767 CSA_ASSERT(this, IsNumberPositive(start));
3768 CSA_ASSERT(this, IsNumber(depth));
3769
3770 // 1. Let targetIndex be start.
3771 VARIABLE(var_target_index, MachineRepresentation::kTagged, start);
3772
3773 // 2. Let sourceIndex be 0.
3774 VARIABLE(var_source_index, MachineRepresentation::kTagged, SmiConstant(0));
3775
3776 // 3. Repeat...
3777 Label loop(this, {&var_target_index, &var_source_index}), done_loop(this);
3778 Goto(&loop);
3779 BIND(&loop);
3780 {
3781 Node* const source_index = var_source_index.value();
3782 Node* const target_index = var_target_index.value();
3783
3784 // ...while sourceIndex < sourceLen
3785 GotoIfNumberGreaterThanOrEqual(source_index, source_length, &done_loop);
3786
3787 // a. Let P be ! ToString(sourceIndex).
3788 // b. Let exists be ? HasProperty(source, P).
3789 CSA_ASSERT(this,
3790 SmiGreaterThanOrEqual(CAST(source_index), SmiConstant(0)));
3791 Node* const exists =
3792 HasProperty(source, source_index, context, kHasProperty);
3793
3794 // c. If exists is true, then
3795 Label next(this);
3796 GotoIfNot(IsTrue(exists), &next);
3797 {
3798 // i. Let element be ? Get(source, P).
3799 Node* element = GetProperty(context, source, source_index);
3800
3801 // ii. If mapperFunction is present, then
3802 if (mapper_function != nullptr) {
3803 CSA_ASSERT(this, Word32Or(IsUndefined(mapper_function),
3804 IsCallable(mapper_function)));
3805 DCHECK_NOT_NULL(this_arg);
3806
3807 // 1. Set element to ? Call(mapperFunction, thisArg , « element,
3808 // sourceIndex, source »).
3809 element =
3810 CallJS(CodeFactory::Call(isolate()), context, mapper_function,
3811 this_arg, element, source_index, source);
3812 }
3813
3814 // iii. Let shouldFlatten be false.
3815 Label if_flatten_array(this), if_flatten_proxy(this, Label::kDeferred),
3816 if_noflatten(this);
3817 // iv. If depth > 0, then
3818 GotoIfNumberGreaterThanOrEqual(SmiConstant(0), depth, &if_noflatten);
3819 // 1. Set shouldFlatten to ? IsArray(element).
3820 GotoIf(TaggedIsSmi(element), &if_noflatten);
3821 GotoIf(IsJSArray(element), &if_flatten_array);
3822 GotoIfNot(IsJSProxy(element), &if_noflatten);
3823 Branch(IsTrue(CallRuntime(Runtime::kArrayIsArray, context, element)),
3824 &if_flatten_proxy, &if_noflatten);
3825
3826 BIND(&if_flatten_array);
3827 {
3828 CSA_ASSERT(this, IsJSArray(element));
3829
3830 // 1. Let elementLen be ? ToLength(? Get(element, "length")).
3831 Node* const element_length =
3832 LoadObjectField(element, JSArray::kLengthOffset);
3833
3834 // 2. Set targetIndex to ? FlattenIntoArray(target, element,
3835 // elementLen, targetIndex,
3836 // depth - 1).
3837 var_target_index.Bind(
3838 CallBuiltin(Builtins::kFlattenIntoArray, context, target, element,
3839 element_length, target_index, NumberDec(depth)));
3840 Goto(&next);
3841 }
3842
3843 BIND(&if_flatten_proxy);
3844 {
3845 CSA_ASSERT(this, IsJSProxy(element));
3846
3847 // 1. Let elementLen be ? ToLength(? Get(element, "length")).
3848 Node* const element_length = ToLength_Inline(
3849 context, GetProperty(context, element, LengthStringConstant()));
3850
3851 // 2. Set targetIndex to ? FlattenIntoArray(target, element,
3852 // elementLen, targetIndex,
3853 // depth - 1).
3854 var_target_index.Bind(
3855 CallBuiltin(Builtins::kFlattenIntoArray, context, target, element,
3856 element_length, target_index, NumberDec(depth)));
3857 Goto(&next);
3858 }
3859
3860 BIND(&if_noflatten);
3861 {
3862 // 1. If targetIndex >= 2^53-1, throw a TypeError exception.
3863 Label throw_error(this, Label::kDeferred);
3864 GotoIfNumberGreaterThanOrEqual(
3865 target_index, NumberConstant(kMaxSafeInteger), &throw_error);
3866
3867 // 2. Perform ? CreateDataPropertyOrThrow(target,
3868 // ! ToString(targetIndex),
3869 // element).
3870 CallRuntime(Runtime::kCreateDataProperty, context, target,
3871 target_index, element);
3872
3873 // 3. Increase targetIndex by 1.
3874 var_target_index.Bind(NumberInc(target_index));
3875 Goto(&next);
3876
3877 BIND(&throw_error);
3878 ThrowTypeError(context, MessageTemplate::kFlattenPastSafeLength,
3879 source_length, target_index);
3880 }
3881 }
3882 BIND(&next);
3883
3884 // d. Increase sourceIndex by 1.
3885 var_source_index.Bind(NumberInc(source_index));
3886 Goto(&loop);
3887 }
3888
3889 BIND(&done_loop);
3890 return var_target_index.value();
3891 }
3892 };
3893
3894 } // namespace
3895
3896 // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
TF_BUILTIN(FlattenIntoArray,ArrayFlattenAssembler)3897 TF_BUILTIN(FlattenIntoArray, ArrayFlattenAssembler) {
3898 Node* const context = Parameter(Descriptor::kContext);
3899 Node* const target = Parameter(Descriptor::kTarget);
3900 Node* const source = Parameter(Descriptor::kSource);
3901 Node* const source_length = Parameter(Descriptor::kSourceLength);
3902 Node* const start = Parameter(Descriptor::kStart);
3903 Node* const depth = Parameter(Descriptor::kDepth);
3904
3905 Return(
3906 FlattenIntoArray(context, target, source, source_length, start, depth));
3907 }
3908
3909 // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
TF_BUILTIN(FlatMapIntoArray,ArrayFlattenAssembler)3910 TF_BUILTIN(FlatMapIntoArray, ArrayFlattenAssembler) {
3911 Node* const context = Parameter(Descriptor::kContext);
3912 Node* const target = Parameter(Descriptor::kTarget);
3913 Node* const source = Parameter(Descriptor::kSource);
3914 Node* const source_length = Parameter(Descriptor::kSourceLength);
3915 Node* const start = Parameter(Descriptor::kStart);
3916 Node* const depth = Parameter(Descriptor::kDepth);
3917 Node* const mapper_function = Parameter(Descriptor::kMapperFunction);
3918 Node* const this_arg = Parameter(Descriptor::kThisArg);
3919
3920 Return(FlattenIntoArray(context, target, source, source_length, start, depth,
3921 mapper_function, this_arg));
3922 }
3923
3924 // https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatten
TF_BUILTIN(ArrayPrototypeFlatten,CodeStubAssembler)3925 TF_BUILTIN(ArrayPrototypeFlatten, CodeStubAssembler) {
3926 Node* const argc =
3927 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
3928 CodeStubArguments args(this, argc);
3929 Node* const context = Parameter(BuiltinDescriptor::kContext);
3930 Node* const receiver = args.GetReceiver();
3931 Node* const depth = args.GetOptionalArgumentValue(0);
3932
3933 // 1. Let O be ? ToObject(this value).
3934 Node* const o = ToObject(context, receiver);
3935
3936 // 2. Let sourceLen be ? ToLength(? Get(O, "length")).
3937 Node* const source_length =
3938 ToLength_Inline(context, GetProperty(context, o, LengthStringConstant()));
3939
3940 // 3. Let depthNum be 1.
3941 VARIABLE(var_depth_num, MachineRepresentation::kTagged, SmiConstant(1));
3942
3943 // 4. If depth is not undefined, then
3944 Label done(this);
3945 GotoIf(IsUndefined(depth), &done);
3946 {
3947 // a. Set depthNum to ? ToInteger(depth).
3948 var_depth_num.Bind(ToInteger_Inline(context, depth));
3949 Goto(&done);
3950 }
3951 BIND(&done);
3952
3953 // 5. Let A be ? ArraySpeciesCreate(O, 0).
3954 Node* const constructor =
3955 CallRuntime(Runtime::kArraySpeciesConstructor, context, o);
3956 Node* const a = ConstructJS(CodeFactory::Construct(isolate()), context,
3957 constructor, SmiConstant(0));
3958
3959 // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
3960 CallBuiltin(Builtins::kFlattenIntoArray, context, a, o, source_length,
3961 SmiConstant(0), var_depth_num.value());
3962
3963 // 7. Return A.
3964 args.PopAndReturn(a);
3965 }
3966
3967 // https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap
TF_BUILTIN(ArrayPrototypeFlatMap,CodeStubAssembler)3968 TF_BUILTIN(ArrayPrototypeFlatMap, CodeStubAssembler) {
3969 Node* const argc =
3970 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
3971 CodeStubArguments args(this, argc);
3972 Node* const context = Parameter(BuiltinDescriptor::kContext);
3973 Node* const receiver = args.GetReceiver();
3974 Node* const mapper_function = args.GetOptionalArgumentValue(0);
3975
3976 // 1. Let O be ? ToObject(this value).
3977 Node* const o = ToObject(context, receiver);
3978
3979 // 2. Let sourceLen be ? ToLength(? Get(O, "length")).
3980 Node* const source_length =
3981 ToLength_Inline(context, GetProperty(context, o, LengthStringConstant()));
3982
3983 // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception.
3984 Label if_not_callable(this, Label::kDeferred);
3985 GotoIf(TaggedIsSmi(mapper_function), &if_not_callable);
3986 GotoIfNot(IsCallable(mapper_function), &if_not_callable);
3987
3988 // 4. If thisArg is present, let T be thisArg; else let T be undefined.
3989 Node* const t = args.GetOptionalArgumentValue(1);
3990
3991 // 5. Let A be ? ArraySpeciesCreate(O, 0).
3992 Node* const constructor =
3993 CallRuntime(Runtime::kArraySpeciesConstructor, context, o);
3994 Node* const a = ConstructJS(CodeFactory::Construct(isolate()), context,
3995 constructor, SmiConstant(0));
3996
3997 // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T).
3998 CallBuiltin(Builtins::kFlatMapIntoArray, context, a, o, source_length,
3999 SmiConstant(0), SmiConstant(1), mapper_function, t);
4000
4001 // 7. Return A.
4002 args.PopAndReturn(a);
4003
4004 BIND(&if_not_callable);
4005 { ThrowTypeError(context, MessageTemplate::kMapperFunctionNonCallable); }
4006 }
4007
4008 } // namespace internal
4009 } // namespace v8
4010