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