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