diff options
Diffstat (limited to 'deps/v8/src/builtins/builtins-array-gen.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-array-gen.cc | 1567 |
1 files changed, 907 insertions, 660 deletions
diff --git a/deps/v8/src/builtins/builtins-array-gen.cc b/deps/v8/src/builtins/builtins-array-gen.cc index 52a6222882..cca395b2ce 100644 --- a/deps/v8/src/builtins/builtins-array-gen.cc +++ b/deps/v8/src/builtins/builtins-array-gen.cc @@ -8,8 +8,8 @@ #include "src/builtins/builtins-utils-gen.h" #include "src/builtins/builtins.h" #include "src/code-stub-assembler.h" -#include "src/factory-inl.h" #include "src/frame-constants.h" +#include "src/heap/factory-inl.h" #include "src/builtins/builtins-array-gen.h" @@ -151,7 +151,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { BIND(&fast); { GotoIf(SmiNotEqual(LoadJSArrayLength(a()), to_.value()), &runtime); - kind = EnsureArrayPushable(a(), &runtime); + kind = EnsureArrayPushable(LoadMap(a()), &runtime); GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS), &object_push_pre); @@ -214,8 +214,9 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { CSA_ASSERT(this, SmiLessThanOrEqual( len_, LoadObjectField(a, JSTypedArray::kLengthOffset))); - fast_typed_array_target_ = Word32Equal(LoadInstanceType(LoadElements(o_)), - LoadInstanceType(LoadElements(a))); + fast_typed_array_target_ = + Word32Equal(LoadInstanceType(LoadElements(original_array)), + LoadInstanceType(LoadElements(a))); a_.Bind(a); } @@ -413,15 +414,15 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { // 3. Let len be ToLength(Get(O, "length")). // 4. ReturnIfAbrupt(len). - VARIABLE(merged_length, MachineRepresentation::kTagged); + TVARIABLE(Number, merged_length); Label has_length(this, &merged_length), not_js_array(this); GotoIf(DoesntHaveInstanceType(o(), JS_ARRAY_TYPE), ¬_js_array); - merged_length.Bind(LoadJSArrayLength(o())); + merged_length = LoadJSArrayLength(CAST(o())); Goto(&has_length); BIND(¬_js_array); Node* len_property = GetProperty(context(), o(), isolate()->factory()->length_string()); - merged_length.Bind(ToLength_Inline(context(), len_property)); + merged_length = ToLength_Inline(context(), len_property); Goto(&has_length); BIND(&has_length); len_ = merged_length.value(); @@ -466,7 +467,8 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { void ArrayBuiltinsAssembler::InitIteratingArrayBuiltinLoopContinuation( TNode<Context> context, TNode<Object> receiver, Node* callbackfn, - Node* this_arg, Node* a, Node* o, Node* initial_k, Node* len, Node* to) { + Node* this_arg, Node* a, TNode<JSReceiver> o, Node* initial_k, + TNode<Number> len, Node* to) { context_ = context; this_arg_ = this_arg; callbackfn_ = callbackfn; @@ -489,14 +491,17 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { throw_detached(this, Label::kDeferred); GotoIf(TaggedIsSmi(receiver_), &throw_not_typed_array); - GotoIfNot(HasInstanceType(receiver_, JS_TYPED_ARRAY_TYPE), + GotoIfNot(HasInstanceType(CAST(receiver_), JS_TYPED_ARRAY_TYPE), &throw_not_typed_array); - o_ = receiver_; - Node* array_buffer = LoadObjectField(o_, JSTypedArray::kBufferOffset); + TNode<JSTypedArray> typed_array = CAST(receiver_); + o_ = typed_array; + + Node* array_buffer = + LoadObjectField(typed_array, JSTypedArray::kBufferOffset); GotoIf(IsDetachedBuffer(array_buffer), &throw_detached); - len_ = LoadObjectField(o_, JSTypedArray::kLengthOffset); + len_ = LoadObjectField<Smi>(typed_array, JSTypedArray::kLengthOffset); Label throw_not_callable(this, Label::kDeferred); Label distinguish_types(this); @@ -540,7 +545,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { } else { k_.Bind(NumberDec(len())); } - Node* instance_type = LoadInstanceType(LoadElements(o_)); + Node* instance_type = LoadInstanceType(LoadElements(typed_array)); Switch(instance_type, &unexpected_instance_type, instance_types.data(), label_ptrs.data(), labels.size()); @@ -552,7 +557,8 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { // TODO(tebbi): Silently cancelling the loop on buffer detachment is a // spec violation. Should go to &throw_detached and throw a TypeError // instead. - VisitAllTypedArrayElements(array_buffer, processor, &done, direction); + VisitAllTypedArrayElements(array_buffer, processor, &done, direction, + typed_array); Goto(&done); // No exception, return success BIND(&done); @@ -638,12 +644,12 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { void ArrayBuiltinsAssembler::VisitAllTypedArrayElements( Node* array_buffer, const CallResultProcessor& processor, Label* detached, - ForEachDirection direction) { + ForEachDirection direction, TNode<JSTypedArray> typed_array) { VariableList list({&a_, &k_, &to_}, zone()); FastLoopBody body = [&](Node* index) { GotoIf(IsDetachedBuffer(array_buffer), detached); - Node* elements = LoadElements(o_); + Node* elements = LoadElements(typed_array); Node* base_ptr = LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset); Node* external_ptr = @@ -671,13 +677,13 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { void ArrayBuiltinsAssembler::VisitAllFastElementsOneKind( ElementsKind kind, const CallResultProcessor& processor, Label* array_changed, ParameterMode mode, ForEachDirection direction, - MissingPropertyMode missing_property_mode) { + MissingPropertyMode missing_property_mode, TNode<Smi> length) { Comment("begin VisitAllFastElementsOneKind"); VARIABLE(original_map, MachineRepresentation::kTagged); original_map.Bind(LoadMap(o())); VariableList list({&original_map, &a_, &k_, &to_}, zone()); Node* start = IntPtrOrSmiConstant(0, mode); - Node* end = TaggedToParameter(len(), mode); + Node* end = TaggedToParameter(length, mode); IndexAdvanceMode advance_mode = direction == ForEachDirection::kReverse ? IndexAdvanceMode::kPre : IndexAdvanceMode::kPost; @@ -695,13 +701,14 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { Node* o_map = LoadMap(o()); GotoIf(WordNotEqual(o_map, original_map.value()), array_changed); + TNode<JSArray> o_array = CAST(o()); // Check if o's length has changed during the callback and if the // index is now out of range of the new length. - GotoIf(SmiGreaterThanOrEqual(k_.value(), LoadJSArrayLength(o())), + GotoIf(SmiGreaterThanOrEqual(k_.value(), LoadJSArrayLength(o_array)), array_changed); // Re-load the elements array. If may have been resized. - Node* elements = LoadElements(o()); + Node* elements = LoadElements(o_array); // Fast case: load the element directly from the elements FixedArray // and call the callback if the element is not the hole. @@ -757,6 +764,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { &switch_on_elements_kind, slow); BIND(&switch_on_elements_kind); + TNode<Smi> smi_len = CAST(len()); // Select by ElementsKind Node* o_map = LoadMap(o()); Node* bit_field2 = LoadMapBitField2(o_map); @@ -768,7 +776,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { BIND(&fast_elements); { VisitAllFastElementsOneKind(PACKED_ELEMENTS, processor, slow, mode, - direction, missing_property_mode); + direction, missing_property_mode, smi_len); action(this); @@ -783,7 +791,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { BIND(&fast_double_elements); { VisitAllFastElementsOneKind(PACKED_DOUBLE_ELEMENTS, processor, slow, mode, - direction, missing_property_mode); + direction, missing_property_mode, smi_len); action(this); @@ -807,7 +815,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map), &runtime); - Node* species_protector = SpeciesProtectorConstant(); + Node* species_protector = ArraySpeciesProtectorConstant(); Node* value = LoadObjectField(species_protector, PropertyCell::kValueOffset); TNode<Smi> const protector_invalid = @@ -841,8 +849,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { } // Perform ArraySpeciesCreate (ES6 #sec-arrayspeciescreate). - void ArrayBuiltinsAssembler::GenerateArraySpeciesCreate( - SloppyTNode<Smi> len) { + void ArrayBuiltinsAssembler::GenerateArraySpeciesCreate(TNode<Number> len) { Label runtime(this, Label::kDeferred), done(this); Node* const original_map = LoadMap(o()); @@ -853,7 +860,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) { GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map), &runtime); - Node* species_protector = SpeciesProtectorConstant(); + Node* species_protector = ArraySpeciesProtectorConstant(); Node* value = LoadObjectField(species_protector, PropertyCell::kValueOffset); Node* const protector_invalid = SmiConstant(Isolate::kProtectorInvalid); @@ -1019,7 +1026,7 @@ TF_BUILTIN(ArrayPrototypePush, CodeStubAssembler) { { array_receiver = CAST(receiver); arg_index = IntPtrConstant(0); - kind = EnsureArrayPushable(array_receiver, &runtime); + kind = EnsureArrayPushable(LoadMap(array_receiver), &runtime); GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS), &object_push_pre); @@ -1146,7 +1153,7 @@ class ArrayPrototypeSliceCodeStubAssembler : public CodeStubAssembler { GotoIf(IsNoElementsProtectorCellInvalid(), slow); - GotoIf(IsSpeciesProtectorCellInvalid(), slow); + GotoIf(IsArraySpeciesProtectorCellInvalid(), slow); // Bailout if receiver has slow elements. Node* elements_kind = LoadMapElementsKind(map); @@ -1647,9 +1654,9 @@ TF_BUILTIN(ArrayFindLoopContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); - Node* object = Parameter(Descriptor::kObject); + TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* to = Parameter(Descriptor::kTo); InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, @@ -1670,7 +1677,7 @@ TF_BUILTIN(ArrayFindLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver, callbackfn, this_arg, UndefinedConstant(), receiver, @@ -1685,7 +1692,7 @@ TF_BUILTIN(ArrayFindLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Return(CallBuiltin(Builtins::kArrayFindLoopContinuation, context, receiver, callbackfn, this_arg, UndefinedConstant(), receiver, @@ -1702,7 +1709,7 @@ TF_BUILTIN(ArrayFindLoopAfterCallbackLazyDeoptContinuation, Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* found_value = Parameter(Descriptor::kFoundValue); Node* is_found = Parameter(Descriptor::kIsFound); @@ -1748,9 +1755,9 @@ TF_BUILTIN(ArrayFindIndexLoopContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); - Node* object = Parameter(Descriptor::kObject); + TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* to = Parameter(Descriptor::kTo); InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, @@ -1769,7 +1776,7 @@ TF_BUILTIN(ArrayFindIndexLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context, receiver, callbackfn, this_arg, SmiConstant(-1), receiver, @@ -1782,7 +1789,7 @@ TF_BUILTIN(ArrayFindIndexLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Return(CallBuiltin(Builtins::kArrayFindIndexLoopContinuation, context, receiver, callbackfn, this_arg, SmiConstant(-1), receiver, @@ -1796,7 +1803,7 @@ TF_BUILTIN(ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation, Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* found_value = Parameter(Descriptor::kFoundValue); Node* is_found = Parameter(Descriptor::kIsFound); @@ -1938,13 +1945,10 @@ class ArrayPopulatorAssembler : public CodeStubAssembler { void GenerateSetLength(TNode<Context> context, TNode<Object> array, TNode<Number> length) { Label fast(this), runtime(this), done(this); - // TODO(delphick): We should be able to skip the fast set altogether, if the - // length already equals the expected length, which it always is now on the - // fast path. - // Only set the length in this stub if - // 1) the array has fast elements, - // 2) the length is writable, - // 3) the new length is equal to the old length. + // There's no need to set the length, if + // 1) the array is a fast JS array and + // 2) the new length is equal to the old length. + // as the set is not observable. Otherwise fall back to the run-time. // 1) Check that the array has fast elements. // TODO(delphick): Consider changing this since it does an an unnecessary @@ -1961,20 +1965,11 @@ class ArrayPopulatorAssembler : public CodeStubAssembler { TNode<Smi> old_length = LoadFastJSArrayLength(fast_array); CSA_ASSERT(this, TaggedIsPositiveSmi(old_length)); - // 2) Ensure that the length is writable. - // TODO(delphick): This check may be redundant due to the - // BranchIfFastJSArray above. - EnsureArrayLengthWritable(LoadMap(fast_array), &runtime); - - // 3) If the created array's length does not match the required length, - // then use the runtime to set the property as that will insert holes - // into excess elements or shrink the backing store as appropriate. - GotoIf(SmiNotEqual(length_smi, old_length), &runtime); - - StoreObjectFieldNoWriteBarrier(fast_array, JSArray::kLengthOffset, - length_smi); - - Goto(&done); + // 2) If the created array's length matches the required length, then + // there's nothing else to do. Otherwise use the runtime to set the + // property as that will insert holes into excess elements or shrink + // the backing store as appropriate. + Branch(SmiNotEqual(length_smi, old_length), &runtime, &done); } BIND(&runtime); @@ -2128,14 +2123,11 @@ TF_BUILTIN(ArrayFrom, ArrayPopulatorAssembler) { } } - // Since there's no iterator, items cannot be a Fast JS Array. BIND(¬_iterable); { - CSA_ASSERT(this, Word32BinaryNot(IsFastJSArray(array_like, context))); - // Treat array_like as an array and try to get its length. - length = CAST(ToLength_Inline( - context, GetProperty(context, array_like, factory()->length_string()))); + length = ToLength_Inline( + context, GetProperty(context, array_like, factory()->length_string())); // Construct an array using the receiver as constructor with the same length // as the input array. @@ -2257,9 +2249,9 @@ TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); - Node* object = Parameter(Descriptor::kObject); + TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* to = Parameter(Descriptor::kTo); InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, @@ -2277,7 +2269,7 @@ TF_BUILTIN(ArrayForEachLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Return(CallBuiltin(Builtins::kArrayForEachLoopContinuation, context, receiver, callbackfn, this_arg, UndefinedConstant(), receiver, @@ -2290,7 +2282,7 @@ TF_BUILTIN(ArrayForEachLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Return(CallBuiltin(Builtins::kArrayForEachLoopContinuation, context, receiver, callbackfn, this_arg, UndefinedConstant(), receiver, @@ -2345,7 +2337,7 @@ TF_BUILTIN(ArraySomeLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* result = Parameter(Descriptor::kResult); // This custom lazy deopt point is right after the callback. every() needs @@ -2374,7 +2366,7 @@ TF_BUILTIN(ArraySomeLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Return(CallBuiltin(Builtins::kArraySomeLoopContinuation, context, receiver, callbackfn, this_arg, FalseConstant(), receiver, initial_k, @@ -2387,9 +2379,9 @@ TF_BUILTIN(ArraySomeLoopContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); - Node* object = Parameter(Descriptor::kObject); + TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* to = Parameter(Descriptor::kTo); InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, @@ -2448,7 +2440,7 @@ TF_BUILTIN(ArrayEveryLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* result = Parameter(Descriptor::kResult); // This custom lazy deopt point is right after the callback. every() needs @@ -2477,7 +2469,7 @@ TF_BUILTIN(ArrayEveryLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Return(CallBuiltin(Builtins::kArrayEveryLoopContinuation, context, receiver, callbackfn, this_arg, TrueConstant(), receiver, initial_k, @@ -2490,9 +2482,9 @@ TF_BUILTIN(ArrayEveryLoopContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); - Node* object = Parameter(Descriptor::kObject); + TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* to = Parameter(Descriptor::kTo); InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, @@ -2551,9 +2543,9 @@ TF_BUILTIN(ArrayReduceLoopContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* accumulator = Parameter(Descriptor::kAccumulator); - Node* object = Parameter(Descriptor::kObject); + TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* to = Parameter(Descriptor::kTo); InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, @@ -2570,16 +2562,14 @@ TF_BUILTIN(ArrayReducePreLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { TNode<Context> context = CAST(Parameter(Descriptor::kContext)); TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); - Callable stub( - Builtins::CallableFor(isolate(), Builtins::kArrayReduceLoopContinuation)); // Simulate starting the loop at 0, but ensuring that the accumulator is // the hole. The continuation stub will search for the initial non-hole // element, rightly throwing an exception if not found. - Return(CallStub(stub, context, receiver, callbackfn, UndefinedConstant(), - TheHoleConstant(), receiver, SmiConstant(0), len, - UndefinedConstant())); + Return(CallBuiltin(Builtins::kArrayReduceLoopContinuation, context, receiver, + callbackfn, UndefinedConstant(), TheHoleConstant(), + receiver, SmiConstant(0), len, UndefinedConstant())); } TF_BUILTIN(ArrayReduceLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { @@ -2588,12 +2578,11 @@ TF_BUILTIN(ArrayReduceLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* accumulator = Parameter(Descriptor::kAccumulator); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); - Callable stub( - Builtins::CallableFor(isolate(), Builtins::kArrayReduceLoopContinuation)); - Return(CallStub(stub, context, receiver, callbackfn, UndefinedConstant(), - accumulator, receiver, initial_k, len, UndefinedConstant())); + Return(CallBuiltin(Builtins::kArrayReduceLoopContinuation, context, receiver, + callbackfn, UndefinedConstant(), accumulator, receiver, + initial_k, len, UndefinedConstant())); } TF_BUILTIN(ArrayReduceLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { @@ -2601,13 +2590,12 @@ TF_BUILTIN(ArrayReduceLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* result = Parameter(Descriptor::kResult); - Callable stub( - Builtins::CallableFor(isolate(), Builtins::kArrayReduceLoopContinuation)); - Return(CallStub(stub, context, receiver, callbackfn, UndefinedConstant(), - result, receiver, initial_k, len, UndefinedConstant())); + Return(CallBuiltin(Builtins::kArrayReduceLoopContinuation, context, receiver, + callbackfn, UndefinedConstant(), result, receiver, + initial_k, len, UndefinedConstant())); } TF_BUILTIN(ArrayReduce, ArrayBuiltinsAssembler) { @@ -2657,9 +2645,9 @@ TF_BUILTIN(ArrayReduceRightLoopContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* accumulator = Parameter(Descriptor::kAccumulator); - Node* object = Parameter(Descriptor::kObject); + TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* to = Parameter(Descriptor::kTo); InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, @@ -2677,16 +2665,15 @@ TF_BUILTIN(ArrayReduceRightPreLoopEagerDeoptContinuation, TNode<Context> context = CAST(Parameter(Descriptor::kContext)); TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); Node* callbackfn = Parameter(Descriptor::kCallbackFn); - Node* len = Parameter(Descriptor::kLength); + TNode<Smi> len = CAST(Parameter(Descriptor::kLength)); - Callable stub(Builtins::CallableFor( - isolate(), Builtins::kArrayReduceRightLoopContinuation)); // Simulate starting the loop at 0, but ensuring that the accumulator is // the hole. The continuation stub will search for the initial non-hole // element, rightly throwing an exception if not found. - Return(CallStub(stub, context, receiver, callbackfn, UndefinedConstant(), - TheHoleConstant(), receiver, SmiConstant(0), len, - UndefinedConstant())); + Return(CallBuiltin(Builtins::kArrayReduceRightLoopContinuation, context, + receiver, callbackfn, UndefinedConstant(), + TheHoleConstant(), receiver, SmiSub(len, SmiConstant(1)), + len, UndefinedConstant())); } TF_BUILTIN(ArrayReduceRightLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { @@ -2695,12 +2682,11 @@ TF_BUILTIN(ArrayReduceRightLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* accumulator = Parameter(Descriptor::kAccumulator); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); - Callable stub(Builtins::CallableFor( - isolate(), Builtins::kArrayReduceRightLoopContinuation)); - Return(CallStub(stub, context, receiver, callbackfn, UndefinedConstant(), - accumulator, receiver, initial_k, len, UndefinedConstant())); + Return(CallBuiltin(Builtins::kArrayReduceRightLoopContinuation, context, + receiver, callbackfn, UndefinedConstant(), accumulator, + receiver, initial_k, len, UndefinedConstant())); } TF_BUILTIN(ArrayReduceRightLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { @@ -2708,13 +2694,12 @@ TF_BUILTIN(ArrayReduceRightLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* result = Parameter(Descriptor::kResult); - Callable stub(Builtins::CallableFor( - isolate(), Builtins::kArrayReduceRightLoopContinuation)); - Return(CallStub(stub, context, receiver, callbackfn, UndefinedConstant(), - result, receiver, initial_k, len, UndefinedConstant())); + Return(CallBuiltin(Builtins::kArrayReduceRightLoopContinuation, context, + receiver, callbackfn, UndefinedConstant(), result, + receiver, initial_k, len, UndefinedConstant())); } TF_BUILTIN(ArrayReduceRight, ArrayBuiltinsAssembler) { @@ -2767,9 +2752,9 @@ TF_BUILTIN(ArrayFilterLoopContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); - Node* object = Parameter(Descriptor::kObject); + TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* to = Parameter(Descriptor::kTo); InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, @@ -2788,7 +2773,7 @@ TF_BUILTIN(ArrayFilterLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* to = Parameter(Descriptor::kTo); Return(CallBuiltin(Builtins::kArrayFilterLoopContinuation, context, receiver, @@ -2803,7 +2788,7 @@ TF_BUILTIN(ArrayFilterLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* value_k = Parameter(Descriptor::kValueK); Node* result = Parameter(Descriptor::kResult); @@ -2864,9 +2849,9 @@ TF_BUILTIN(ArrayMapLoopContinuation, ArrayBuiltinsAssembler) { Node* callbackfn = Parameter(Descriptor::kCallbackFn); Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); - Node* object = Parameter(Descriptor::kObject); + TNode<JSReceiver> object = CAST(Parameter(Descriptor::kObject)); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* to = Parameter(Descriptor::kTo); InitIteratingArrayBuiltinLoopContinuation(context, receiver, callbackfn, @@ -2885,7 +2870,7 @@ TF_BUILTIN(ArrayMapLoopEagerDeoptContinuation, ArrayBuiltinsAssembler) { Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Return(CallBuiltin(Builtins::kArrayMapLoopContinuation, context, receiver, callbackfn, this_arg, array, receiver, initial_k, len, @@ -2899,7 +2884,7 @@ TF_BUILTIN(ArrayMapLoopLazyDeoptContinuation, ArrayBuiltinsAssembler) { Node* this_arg = Parameter(Descriptor::kThisArg); Node* array = Parameter(Descriptor::kArray); Node* initial_k = Parameter(Descriptor::kInitialK); - Node* len = Parameter(Descriptor::kLength); + TNode<Number> len = CAST(Parameter(Descriptor::kLength)); Node* result = Parameter(Descriptor::kResult); // This custom lazy deopt point is right after the callback. map() needs @@ -2965,7 +2950,7 @@ TF_BUILTIN(ArrayIsArray, CodeStubAssembler) { Label call_runtime(this), return_true(this), return_false(this); GotoIf(TaggedIsSmi(object), &return_false); - TNode<Word32T> instance_type = LoadInstanceType(CAST(object)); + TNode<Int32T> instance_type = LoadInstanceType(CAST(object)); GotoIf(InstanceTypeEqual(instance_type, JS_ARRAY_TYPE), &return_true); @@ -2991,6 +2976,15 @@ class ArrayIncludesIndexofAssembler : public CodeStubAssembler { enum SearchVariant { kIncludes, kIndexOf }; void Generate(SearchVariant variant); + void GenerateSmiOrObject(SearchVariant variant, Node* context, Node* elements, + Node* search_element, Node* array_length, + Node* from_index); + void GeneratePackedDoubles(SearchVariant variant, Node* elements, + Node* search_element, Node* array_length, + Node* from_index); + void GenerateHoleyDoubles(SearchVariant variant, Node* elements, + Node* search_element, Node* array_length, + Node* from_index); }; void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant) { @@ -3008,8 +3002,7 @@ void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant) { Node* intptr_zero = IntPtrConstant(0); - Label init_index(this), return_found(this), return_not_found(this), - call_runtime(this); + Label init_index(this), return_not_found(this), call_runtime(this); // Take slow path if not a JSArray, if retrieving elements requires // traversing prototype, or if access checks are required. @@ -3021,7 +3014,8 @@ void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant) { // JSArray length is always a positive Smi for fast arrays. CSA_ASSERT(this, TaggedIsPositiveSmi(LoadJSArrayLength(array))); - Node* array_length = SmiUntag(LoadFastJSArrayLength(array)); + Node* array_length = LoadFastJSArrayLength(array); + Node* array_length_untagged = SmiUntag(array_length); { // Initialize fromIndex. @@ -3049,7 +3043,7 @@ void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant) { GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done); // The fromIndex is negative: add it to the array's length. - index_var.Bind(IntPtrAdd(array_length, index_var.value())); + index_var.Bind(IntPtrAdd(array_length_untagged, index_var.value())); // Clamp negative results at zero. GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), intptr_zero), &done); index_var.Bind(intptr_zero); @@ -3059,7 +3053,7 @@ void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant) { } // Fail early if startIndex >= array.length. - GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length), + GotoIf(IntPtrGreaterThanOrEqual(index_var.value(), array_length_untagged), &return_not_found); Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this); @@ -3080,179 +3074,139 @@ void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant) { BIND(&if_smiorobjects); { - VARIABLE(search_num, MachineRepresentation::kFloat64); - Label ident_loop(this, &index_var), heap_num_loop(this, &search_num), - string_loop(this), bigint_loop(this, &index_var), - undef_loop(this, &index_var), not_smi(this), not_heap_num(this); - - GotoIfNot(TaggedIsSmi(search_element), ¬_smi); - search_num.Bind(SmiToFloat64(CAST(search_element))); - Goto(&heap_num_loop); - - BIND(¬_smi); - if (variant == kIncludes) { - GotoIf(IsUndefined(search_element), &undef_loop); - } - Node* map = LoadMap(CAST(search_element)); - GotoIfNot(IsHeapNumberMap(map), ¬_heap_num); - search_num.Bind(LoadHeapNumberValue(CAST(search_element))); - Goto(&heap_num_loop); - - BIND(¬_heap_num); - Node* search_type = LoadMapInstanceType(map); - GotoIf(IsStringInstanceType(search_type), &string_loop); - GotoIf(IsBigIntInstanceType(search_type), &bigint_loop); - Goto(&ident_loop); - - BIND(&ident_loop); - { - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), - &return_not_found); - Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - GotoIf(WordEqual(element_k, search_element), &return_found); + Callable callable = + (variant == kIncludes) + ? Builtins::CallableFor(isolate(), + Builtins::kArrayIncludesSmiOrObject) + : Builtins::CallableFor(isolate(), + Builtins::kArrayIndexOfSmiOrObject); + Node* result = CallStub(callable, context, elements, search_element, + array_length, SmiTag(index_var.value())); + args.PopAndReturn(result); + } - Increment(&index_var); - Goto(&ident_loop); - } + BIND(&if_packed_doubles); + { + Callable callable = + (variant == kIncludes) + ? Builtins::CallableFor(isolate(), + Builtins::kArrayIncludesPackedDoubles) + : Builtins::CallableFor(isolate(), + Builtins::kArrayIndexOfPackedDoubles); + Node* result = CallStub(callable, context, elements, search_element, + array_length, SmiTag(index_var.value())); + args.PopAndReturn(result); + } - if (variant == kIncludes) { - BIND(&undef_loop); + BIND(&if_holey_doubles); + { + Callable callable = + (variant == kIncludes) + ? Builtins::CallableFor(isolate(), + Builtins::kArrayIncludesHoleyDoubles) + : Builtins::CallableFor(isolate(), + Builtins::kArrayIndexOfHoleyDoubles); + Node* result = CallStub(callable, context, elements, search_element, + array_length, SmiTag(index_var.value())); + args.PopAndReturn(result); + } - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), - &return_not_found); - Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - GotoIf(IsUndefined(element_k), &return_found); - GotoIf(IsTheHole(element_k), &return_found); + BIND(&return_not_found); + if (variant == kIncludes) { + args.PopAndReturn(FalseConstant()); + } else { + args.PopAndReturn(NumberConstant(-1)); + } - Increment(&index_var); - Goto(&undef_loop); - } + BIND(&call_runtime); + { + Node* start_from = + args.GetOptionalArgumentValue(kFromIndexArg, UndefinedConstant()); + Runtime::FunctionId function = variant == kIncludes + ? Runtime::kArrayIncludes_Slow + : Runtime::kArrayIndexOf; + args.PopAndReturn( + CallRuntime(function, context, array, search_element, start_from)); + } +} - BIND(&heap_num_loop); - { - Label nan_loop(this, &index_var), not_nan_loop(this, &index_var); - Label* nan_handling = - variant == kIncludes ? &nan_loop : &return_not_found; - BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); +void ArrayIncludesIndexofAssembler::GenerateSmiOrObject( + SearchVariant variant, Node* context, Node* elements, Node* search_element, + Node* array_length, Node* from_index) { + VARIABLE(index_var, MachineType::PointerRepresentation(), + SmiUntag(from_index)); + VARIABLE(search_num, MachineRepresentation::kFloat64); + Node* array_length_untagged = SmiUntag(array_length); - BIND(¬_nan_loop); - { - Label continue_loop(this), not_smi(this); - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), - &return_not_found); - Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - GotoIfNot(TaggedIsSmi(element_k), ¬_smi); - Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)), - &return_found, &continue_loop); - - BIND(¬_smi); - GotoIfNot(IsHeapNumber(element_k), &continue_loop); - Branch(Float64Equal(search_num.value(), LoadHeapNumberValue(element_k)), - &return_found, &continue_loop); - - BIND(&continue_loop); - Increment(&index_var); - Goto(¬_nan_loop); - } + Label ident_loop(this, &index_var), heap_num_loop(this, &search_num), + string_loop(this), bigint_loop(this, &index_var), + undef_loop(this, &index_var), not_smi(this), not_heap_num(this), + return_found(this), return_not_found(this); - // Array.p.includes uses SameValueZero comparisons, where NaN == NaN. - if (variant == kIncludes) { - BIND(&nan_loop); - Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), - &return_not_found); - Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - GotoIf(TaggedIsSmi(element_k), &continue_loop); - GotoIfNot(IsHeapNumber(element_k), &continue_loop); - BranchIfFloat64IsNaN(LoadHeapNumberValue(element_k), &return_found, - &continue_loop); - - BIND(&continue_loop); - Increment(&index_var); - Goto(&nan_loop); - } - } + GotoIfNot(TaggedIsSmi(search_element), ¬_smi); + search_num.Bind(SmiToFloat64(search_element)); + Goto(&heap_num_loop); - BIND(&string_loop); - { - TNode<String> search_element_string = CAST(search_element); - Label continue_loop(this), next_iteration(this, &index_var), - slow_compare(this), runtime(this, Label::kDeferred); - TNode<IntPtrT> search_length = - LoadStringLengthAsWord(search_element_string); - Goto(&next_iteration); - BIND(&next_iteration); - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), - &return_not_found); - Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - GotoIf(TaggedIsSmi(element_k), &continue_loop); - GotoIf(WordEqual(search_element_string, element_k), &return_found); - Node* element_k_type = LoadInstanceType(element_k); - GotoIfNot(IsStringInstanceType(element_k_type), &continue_loop); - Branch(WordEqual(search_length, LoadStringLengthAsWord(element_k)), - &slow_compare, &continue_loop); - - BIND(&slow_compare); - StringBuiltinsAssembler string_asm(state()); - string_asm.StringEqual_Core(context, search_element_string, search_type, - element_k, element_k_type, search_length, - &return_found, &continue_loop, &runtime); - BIND(&runtime); - TNode<Object> result = CallRuntime(Runtime::kStringEqual, context, - search_element_string, element_k); - Branch(WordEqual(result, TrueConstant()), &return_found, &continue_loop); + BIND(¬_smi); + if (variant == kIncludes) { + GotoIf(IsUndefined(search_element), &undef_loop); + } + Node* map = LoadMap(search_element); + GotoIfNot(IsHeapNumberMap(map), ¬_heap_num); + search_num.Bind(LoadHeapNumberValue(search_element)); + Goto(&heap_num_loop); + + BIND(¬_heap_num); + Node* search_type = LoadMapInstanceType(map); + GotoIf(IsStringInstanceType(search_type), &string_loop); + GotoIf(IsBigIntInstanceType(search_type), &bigint_loop); + Goto(&ident_loop); + + BIND(&ident_loop); + { + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), + &return_not_found); + Node* element_k = LoadFixedArrayElement(elements, index_var.value()); + GotoIf(WordEqual(element_k, search_element), &return_found); - BIND(&continue_loop); - Increment(&index_var); - Goto(&next_iteration); - } + Increment(&index_var); + Goto(&ident_loop); + } - BIND(&bigint_loop); - { - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), - &return_not_found); + if (variant == kIncludes) { + BIND(&undef_loop); - Node* element_k = LoadFixedArrayElement(elements, index_var.value()); - Label continue_loop(this); - GotoIf(TaggedIsSmi(element_k), &continue_loop); - GotoIfNot(IsBigInt(element_k), &continue_loop); - TNode<Object> result = CallRuntime(Runtime::kBigIntEqualToBigInt, context, - search_element, element_k); - Branch(WordEqual(result, TrueConstant()), &return_found, &continue_loop); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), + &return_not_found); + Node* element_k = LoadFixedArrayElement(elements, index_var.value()); + GotoIf(IsUndefined(element_k), &return_found); + GotoIf(IsTheHole(element_k), &return_found); - BIND(&continue_loop); - Increment(&index_var); - Goto(&bigint_loop); - } + Increment(&index_var); + Goto(&undef_loop); } - BIND(&if_packed_doubles); + BIND(&heap_num_loop); { - Label nan_loop(this, &index_var), not_nan_loop(this, &index_var), - hole_loop(this, &index_var), search_notnan(this); - VARIABLE(search_num, MachineRepresentation::kFloat64); - - GotoIfNot(TaggedIsSmi(search_element), &search_notnan); - search_num.Bind(SmiToFloat64(CAST(search_element))); - Goto(¬_nan_loop); - - BIND(&search_notnan); - GotoIfNot(IsHeapNumber(search_element), &return_not_found); - - search_num.Bind(LoadHeapNumberValue(CAST(search_element))); - + Label nan_loop(this, &index_var), not_nan_loop(this, &index_var); Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found; BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); BIND(¬_nan_loop); { - Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + Label continue_loop(this), not_smi(this); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), &return_not_found); - Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), - MachineType::Float64()); - Branch(Float64Equal(element_k, search_num.value()), &return_found, - &continue_loop); + Node* element_k = LoadFixedArrayElement(elements, index_var.value()); + GotoIfNot(TaggedIsSmi(element_k), ¬_smi); + Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)), + &return_found, &continue_loop); + + BIND(¬_smi); + GotoIfNot(IsHeapNumber(element_k), &continue_loop); + Branch(Float64Equal(search_num.value(), LoadHeapNumberValue(element_k)), + &return_found, &continue_loop); + BIND(&continue_loop); Increment(&index_var); Goto(¬_nan_loop); @@ -3262,112 +3216,249 @@ void ArrayIncludesIndexofAssembler::Generate(SearchVariant variant) { if (variant == kIncludes) { BIND(&nan_loop); Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), &return_not_found); - Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), - MachineType::Float64()); - BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop); + Node* element_k = LoadFixedArrayElement(elements, index_var.value()); + GotoIf(TaggedIsSmi(element_k), &continue_loop); + GotoIfNot(IsHeapNumber(element_k), &continue_loop); + BranchIfFloat64IsNaN(LoadHeapNumberValue(element_k), &return_found, + &continue_loop); + BIND(&continue_loop); Increment(&index_var); Goto(&nan_loop); } } - BIND(&if_holey_doubles); + BIND(&string_loop); { - Label nan_loop(this, &index_var), not_nan_loop(this, &index_var), - hole_loop(this, &index_var), search_notnan(this); - VARIABLE(search_num, MachineRepresentation::kFloat64); - - GotoIfNot(TaggedIsSmi(search_element), &search_notnan); - search_num.Bind(SmiToFloat64(CAST(search_element))); - Goto(¬_nan_loop); - - BIND(&search_notnan); - if (variant == kIncludes) { - GotoIf(IsUndefined(search_element), &hole_loop); - } - GotoIfNot(IsHeapNumber(search_element), &return_not_found); + TNode<String> search_element_string = CAST(search_element); + Label continue_loop(this), next_iteration(this, &index_var), + slow_compare(this), runtime(this, Label::kDeferred); + TNode<IntPtrT> search_length = + LoadStringLengthAsWord(search_element_string); + Goto(&next_iteration); + BIND(&next_iteration); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), + &return_not_found); + Node* element_k = LoadFixedArrayElement(elements, index_var.value()); + GotoIf(TaggedIsSmi(element_k), &continue_loop); + GotoIf(WordEqual(search_element_string, element_k), &return_found); + Node* element_k_type = LoadInstanceType(element_k); + GotoIfNot(IsStringInstanceType(element_k_type), &continue_loop); + Branch(WordEqual(search_length, LoadStringLengthAsWord(element_k)), + &slow_compare, &continue_loop); + + BIND(&slow_compare); + StringBuiltinsAssembler string_asm(state()); + string_asm.StringEqual_Core(context, search_element_string, search_type, + element_k, element_k_type, search_length, + &return_found, &continue_loop, &runtime); + BIND(&runtime); + TNode<Object> result = CallRuntime(Runtime::kStringEqual, context, + search_element_string, element_k); + Branch(WordEqual(result, TrueConstant()), &return_found, &continue_loop); - search_num.Bind(LoadHeapNumberValue(CAST(search_element))); + BIND(&continue_loop); + Increment(&index_var); + Goto(&next_iteration); + } - Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found; - BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); + BIND(&bigint_loop); + { + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), + &return_not_found); + + Node* element_k = LoadFixedArrayElement(elements, index_var.value()); + Label continue_loop(this); + GotoIf(TaggedIsSmi(element_k), &continue_loop); + GotoIfNot(IsBigInt(element_k), &continue_loop); + TNode<Object> result = CallRuntime(Runtime::kBigIntEqualToBigInt, context, + search_element, element_k); + Branch(WordEqual(result, TrueConstant()), &return_found, &continue_loop); + + BIND(&continue_loop); + Increment(&index_var); + Goto(&bigint_loop); + } + BIND(&return_found); + if (variant == kIncludes) { + Return(TrueConstant()); + } else { + Return(SmiTag(index_var.value())); + } - BIND(¬_nan_loop); - { - Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), - &return_not_found); + BIND(&return_not_found); + if (variant == kIncludes) { + Return(FalseConstant()); + } else { + Return(NumberConstant(-1)); + } +} - // No need for hole checking here; the following Float64Equal will - // return 'not equal' for holes anyway. - Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), - MachineType::Float64()); +void ArrayIncludesIndexofAssembler::GeneratePackedDoubles(SearchVariant variant, + Node* elements, + Node* search_element, + Node* array_length, + Node* from_index) { + VARIABLE(index_var, MachineType::PointerRepresentation(), + SmiUntag(from_index)); + Node* array_length_untagged = SmiUntag(array_length); - Branch(Float64Equal(element_k, search_num.value()), &return_found, - &continue_loop); - BIND(&continue_loop); - Increment(&index_var); - Goto(¬_nan_loop); - } + Label nan_loop(this, &index_var), not_nan_loop(this, &index_var), + hole_loop(this, &index_var), search_notnan(this), return_found(this), + return_not_found(this); + VARIABLE(search_num, MachineRepresentation::kFloat64); + search_num.Bind(Float64Constant(0)); - // Array.p.includes uses SameValueZero comparisons, where NaN == NaN. - if (variant == kIncludes) { - BIND(&nan_loop); - Label continue_loop(this); - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), - &return_not_found); + GotoIfNot(TaggedIsSmi(search_element), &search_notnan); + search_num.Bind(SmiToFloat64(search_element)); + Goto(¬_nan_loop); - // Load double value or continue if it's the hole NaN. - Node* element_k = LoadFixedDoubleArrayElement( - elements, index_var.value(), MachineType::Float64(), 0, - INTPTR_PARAMETERS, &continue_loop); + BIND(&search_notnan); + GotoIfNot(IsHeapNumber(search_element), &return_not_found); - BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop); - BIND(&continue_loop); - Increment(&index_var); - Goto(&nan_loop); - } + search_num.Bind(LoadHeapNumberValue(search_element)); - // Array.p.includes treats the hole as undefined. - if (variant == kIncludes) { - BIND(&hole_loop); - GotoIfNot(UintPtrLessThan(index_var.value(), array_length), - &return_not_found); + Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found; + BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); - // Check if the element is a double hole, but don't load it. - LoadFixedDoubleArrayElement(elements, index_var.value(), - MachineType::None(), 0, INTPTR_PARAMETERS, - &return_found); + BIND(¬_nan_loop); + { + Label continue_loop(this); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), + &return_not_found); + Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), + MachineType::Float64()); + Branch(Float64Equal(element_k, search_num.value()), &return_found, + &continue_loop); + BIND(&continue_loop); + Increment(&index_var); + Goto(¬_nan_loop); + } - Increment(&index_var); - Goto(&hole_loop); - } + // Array.p.includes uses SameValueZero comparisons, where NaN == NaN. + if (variant == kIncludes) { + BIND(&nan_loop); + Label continue_loop(this); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), + &return_not_found); + Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), + MachineType::Float64()); + BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop); + BIND(&continue_loop); + Increment(&index_var); + Goto(&nan_loop); } BIND(&return_found); if (variant == kIncludes) { - args.PopAndReturn(TrueConstant()); + Return(TrueConstant()); } else { - args.PopAndReturn(SmiTag(index_var.value())); + Return(SmiTag(index_var.value())); } BIND(&return_not_found); if (variant == kIncludes) { - args.PopAndReturn(FalseConstant()); + Return(FalseConstant()); } else { - args.PopAndReturn(NumberConstant(-1)); + Return(NumberConstant(-1)); } +} - BIND(&call_runtime); +void ArrayIncludesIndexofAssembler::GenerateHoleyDoubles(SearchVariant variant, + Node* elements, + Node* search_element, + Node* array_length, + Node* from_index) { + VARIABLE(index_var, MachineType::PointerRepresentation(), + SmiUntag(from_index)); + Node* array_length_untagged = SmiUntag(array_length); + + Label nan_loop(this, &index_var), not_nan_loop(this, &index_var), + hole_loop(this, &index_var), search_notnan(this), return_found(this), + return_not_found(this); + VARIABLE(search_num, MachineRepresentation::kFloat64); + search_num.Bind(Float64Constant(0)); + + GotoIfNot(TaggedIsSmi(search_element), &search_notnan); + search_num.Bind(SmiToFloat64(search_element)); + Goto(¬_nan_loop); + + BIND(&search_notnan); + if (variant == kIncludes) { + GotoIf(IsUndefined(search_element), &hole_loop); + } + GotoIfNot(IsHeapNumber(search_element), &return_not_found); + + search_num.Bind(LoadHeapNumberValue(search_element)); + + Label* nan_handling = variant == kIncludes ? &nan_loop : &return_not_found; + BranchIfFloat64IsNaN(search_num.value(), nan_handling, ¬_nan_loop); + + BIND(¬_nan_loop); { - Node* start_from = args.GetOptionalArgumentValue(kFromIndexArg); - Runtime::FunctionId function = variant == kIncludes - ? Runtime::kArrayIncludes_Slow - : Runtime::kArrayIndexOf; - args.PopAndReturn( - CallRuntime(function, context, array, search_element, start_from)); + Label continue_loop(this); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), + &return_not_found); + + // No need for hole checking here; the following Float64Equal will + // return 'not equal' for holes anyway. + Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(), + MachineType::Float64()); + + Branch(Float64Equal(element_k, search_num.value()), &return_found, + &continue_loop); + BIND(&continue_loop); + Increment(&index_var); + Goto(¬_nan_loop); + } + + // Array.p.includes uses SameValueZero comparisons, where NaN == NaN. + if (variant == kIncludes) { + BIND(&nan_loop); + Label continue_loop(this); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), + &return_not_found); + + // Load double value or continue if it's the hole NaN. + Node* element_k = LoadFixedDoubleArrayElement( + elements, index_var.value(), MachineType::Float64(), 0, + INTPTR_PARAMETERS, &continue_loop); + + BranchIfFloat64IsNaN(element_k, &return_found, &continue_loop); + BIND(&continue_loop); + Increment(&index_var); + Goto(&nan_loop); + } + + // Array.p.includes treats the hole as undefined. + if (variant == kIncludes) { + BIND(&hole_loop); + GotoIfNot(UintPtrLessThan(index_var.value(), array_length_untagged), + &return_not_found); + + // Check if the element is a double hole, but don't load it. + LoadFixedDoubleArrayElement(elements, index_var.value(), + MachineType::None(), 0, INTPTR_PARAMETERS, + &return_found); + + Increment(&index_var); + Goto(&hole_loop); + } + + BIND(&return_found); + if (variant == kIncludes) { + Return(TrueConstant()); + } else { + Return(SmiTag(index_var.value())); + } + + BIND(&return_not_found); + if (variant == kIncludes) { + Return(FalseConstant()); + } else { + Return(NumberConstant(-1)); } } @@ -3375,69 +3466,95 @@ TF_BUILTIN(ArrayIncludes, ArrayIncludesIndexofAssembler) { Generate(kIncludes); } -TF_BUILTIN(ArrayIndexOf, ArrayIncludesIndexofAssembler) { Generate(kIndexOf); } +TF_BUILTIN(ArrayIncludesSmiOrObject, ArrayIncludesIndexofAssembler) { + Node* context = Parameter(Descriptor::kContext); + Node* elements = Parameter(Descriptor::kElements); + Node* search_element = Parameter(Descriptor::kSearchElement); + Node* array_length = Parameter(Descriptor::kLength); + Node* from_index = Parameter(Descriptor::kFromIndex); -class ArrayPrototypeIterationAssembler : public CodeStubAssembler { - public: - explicit ArrayPrototypeIterationAssembler(compiler::CodeAssemblerState* state) - : CodeStubAssembler(state) {} + GenerateSmiOrObject(kIncludes, context, elements, search_element, + array_length, from_index); +} + +TF_BUILTIN(ArrayIncludesPackedDoubles, ArrayIncludesIndexofAssembler) { + Node* elements = Parameter(Descriptor::kElements); + Node* search_element = Parameter(Descriptor::kSearchElement); + Node* array_length = Parameter(Descriptor::kLength); + Node* from_index = Parameter(Descriptor::kFromIndex); + + GeneratePackedDoubles(kIncludes, elements, search_element, array_length, + from_index); +} - protected: - void Generate_ArrayPrototypeIterationMethod(TNode<Context> context, - TNode<Object> receiver, - IterationKind iteration_kind) { - VARIABLE(var_array, MachineRepresentation::kTagged); - VARIABLE(var_map, MachineRepresentation::kTagged); - VARIABLE(var_type, MachineRepresentation::kWord32); +TF_BUILTIN(ArrayIncludesHoleyDoubles, ArrayIncludesIndexofAssembler) { + Node* elements = Parameter(Descriptor::kElements); + Node* search_element = Parameter(Descriptor::kSearchElement); + Node* array_length = Parameter(Descriptor::kLength); + Node* from_index = Parameter(Descriptor::kFromIndex); - Label if_isnotobject(this, Label::kDeferred); - Label create_array_iterator(this); + GenerateHoleyDoubles(kIncludes, elements, search_element, array_length, + from_index); +} - GotoIf(TaggedIsSmi(receiver), &if_isnotobject); +TF_BUILTIN(ArrayIndexOf, ArrayIncludesIndexofAssembler) { Generate(kIndexOf); } - TNode<HeapObject> object_receiver = CAST(receiver); - var_array.Bind(object_receiver); - var_map.Bind(LoadMap(object_receiver)); - var_type.Bind(LoadMapInstanceType(var_map.value())); - Branch(IsJSReceiverInstanceType(var_type.value()), &create_array_iterator, - &if_isnotobject); +TF_BUILTIN(ArrayIndexOfSmiOrObject, ArrayIncludesIndexofAssembler) { + Node* context = Parameter(Descriptor::kContext); + Node* elements = Parameter(Descriptor::kElements); + Node* search_element = Parameter(Descriptor::kSearchElement); + Node* array_length = Parameter(Descriptor::kLength); + Node* from_index = Parameter(Descriptor::kFromIndex); - BIND(&if_isnotobject); - { - TNode<JSReceiver> result = ToObject(context, receiver); - var_array.Bind(result); - var_map.Bind(LoadMap(result)); - var_type.Bind(LoadMapInstanceType(var_map.value())); - Goto(&create_array_iterator); - } + GenerateSmiOrObject(kIndexOf, context, elements, search_element, array_length, + from_index); +} - BIND(&create_array_iterator); - Return(CreateArrayIterator(var_array.value(), var_map.value(), - var_type.value(), context, iteration_kind)); - } -}; +TF_BUILTIN(ArrayIndexOfPackedDoubles, ArrayIncludesIndexofAssembler) { + Node* elements = Parameter(Descriptor::kElements); + Node* search_element = Parameter(Descriptor::kSearchElement); + Node* array_length = Parameter(Descriptor::kLength); + Node* from_index = Parameter(Descriptor::kFromIndex); + + GeneratePackedDoubles(kIndexOf, elements, search_element, array_length, + from_index); +} + +TF_BUILTIN(ArrayIndexOfHoleyDoubles, ArrayIncludesIndexofAssembler) { + Node* elements = Parameter(Descriptor::kElements); + Node* search_element = Parameter(Descriptor::kSearchElement); + Node* array_length = Parameter(Descriptor::kLength); + Node* from_index = Parameter(Descriptor::kFromIndex); -TF_BUILTIN(ArrayPrototypeValues, ArrayPrototypeIterationAssembler) { + GenerateHoleyDoubles(kIndexOf, elements, search_element, array_length, + from_index); +} + +// ES #sec-array.prototype.values +TF_BUILTIN(ArrayPrototypeValues, CodeStubAssembler) { TNode<Context> context = CAST(Parameter(Descriptor::kContext)); TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - Generate_ArrayPrototypeIterationMethod(context, receiver, - IterationKind::kValues); + Return(CreateArrayIterator(context, ToObject(context, receiver), + IterationKind::kValues)); } -TF_BUILTIN(ArrayPrototypeEntries, ArrayPrototypeIterationAssembler) { +// ES #sec-array.prototype.entries +TF_BUILTIN(ArrayPrototypeEntries, CodeStubAssembler) { TNode<Context> context = CAST(Parameter(Descriptor::kContext)); TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - Generate_ArrayPrototypeIterationMethod(context, receiver, - IterationKind::kEntries); + Return(CreateArrayIterator(context, ToObject(context, receiver), + IterationKind::kEntries)); } -TF_BUILTIN(ArrayPrototypeKeys, ArrayPrototypeIterationAssembler) { +// ES #sec-array.prototype.keys +TF_BUILTIN(ArrayPrototypeKeys, CodeStubAssembler) { TNode<Context> context = CAST(Parameter(Descriptor::kContext)); TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); - Generate_ArrayPrototypeIterationMethod(context, receiver, - IterationKind::kKeys); + Return(CreateArrayIterator(context, ToObject(context, receiver), + IterationKind::kKeys)); } +// ES #sec-%arrayiteratorprototype%.next TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) { const char* method_name = "Array Iterator.prototype.next"; @@ -3454,16 +3571,13 @@ TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) { Label throw_bad_receiver(this, Label::kDeferred); Label set_done(this); - Label allocate_key_result(this); Label allocate_entry_if_needed(this); Label allocate_iterator_result(this); - Label generic_values(this); // If O does not have all of the internal slots of an Array Iterator Instance // (22.1.5.3), throw a TypeError exception GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver); - TNode<Int32T> instance_type = LoadInstanceType(iterator); - GotoIf(IsArrayIteratorInstanceType(instance_type), &throw_bad_receiver); + GotoIfNot(IsJSArrayIterator(iterator), &throw_bad_receiver); // Let a be O.[[IteratedObject]]. Node* array = @@ -3471,19 +3585,23 @@ TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) { // Let index be O.[[ArrayIteratorNextIndex]]. Node* index = LoadObjectField(iterator, JSArrayIterator::kNextIndexOffset); - Node* orig_map = - LoadObjectField(iterator, JSArrayIterator::kIteratedObjectMapOffset); Node* array_map = LoadMap(array); - Label if_isfastarray(this), if_isnotfastarray(this), - if_isdetached(this, Label::kDeferred); + Label if_detached(this, Label::kDeferred); + + Label if_typedarray(this), if_other(this, Label::kDeferred), if_array(this), + if_generic(this, Label::kDeferred); - Branch(WordEqual(orig_map, array_map), &if_isfastarray, &if_isnotfastarray); + Node* array_type = LoadInstanceType(array); + GotoIf(InstanceTypeEqual(array_type, JS_ARRAY_TYPE), &if_array); + Branch(InstanceTypeEqual(array_type, JS_TYPED_ARRAY_TYPE), &if_typedarray, + &if_other); - BIND(&if_isfastarray); + BIND(&if_array); { - CSA_ASSERT( - this, InstanceTypeEqual(LoadMapInstanceType(array_map), JS_ARRAY_TYPE)); + // We can only handle fast elements here. + Node* elements_kind = LoadMapElementsKind(array_map); + GotoIfNot(IsFastElementsKind(elements_kind), &if_other); Node* length = LoadJSArrayLength(array); @@ -3492,295 +3610,178 @@ TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) { GotoIfNot(SmiBelow(index, length), &set_done); + var_value.Bind(index); Node* one = SmiConstant(1); StoreObjectFieldNoWriteBarrier(iterator, JSArrayIterator::kNextIndexOffset, SmiAdd(index, one)); - var_done.Bind(FalseConstant()); - Node* elements = LoadElements(array); - static int32_t kInstanceType[] = { - JS_FAST_ARRAY_KEY_ITERATOR_TYPE, - JS_FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_FAST_HOLEY_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_FAST_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_FAST_HOLEY_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_FAST_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_FAST_HOLEY_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE, - JS_FAST_HOLEY_SMI_ARRAY_VALUE_ITERATOR_TYPE, - JS_FAST_ARRAY_VALUE_ITERATOR_TYPE, - JS_FAST_HOLEY_ARRAY_VALUE_ITERATOR_TYPE, - JS_FAST_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE, - JS_FAST_HOLEY_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE, - }; - - Label packed_object_values(this), holey_object_values(this), - packed_double_values(this), holey_double_values(this); - Label* kInstanceTypeHandlers[] = { - &allocate_key_result, &packed_object_values, &holey_object_values, - &packed_object_values, &holey_object_values, &packed_double_values, - &holey_double_values, &packed_object_values, &holey_object_values, - &packed_object_values, &holey_object_values, &packed_double_values, - &holey_double_values}; - - Switch(instance_type, &throw_bad_receiver, kInstanceType, - kInstanceTypeHandlers, arraysize(kInstanceType)); + GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField( + iterator, JSArrayIterator::kKindOffset), + Int32Constant(static_cast<int>(IterationKind::kKeys))), + &allocate_iterator_result); - BIND(&packed_object_values); + Node* elements = LoadElements(array); + Label if_packed(this), if_holey(this), if_packed_double(this), + if_holey_double(this), if_unknown_kind(this, Label::kDeferred); + int32_t kinds[] = {// Handled by if_packed. + PACKED_SMI_ELEMENTS, PACKED_ELEMENTS, + // Handled by if_holey. + HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS, + // Handled by if_packed_double. + PACKED_DOUBLE_ELEMENTS, + // Handled by if_holey_double. + HOLEY_DOUBLE_ELEMENTS}; + Label* labels[] = {// PACKED_{SMI,}_ELEMENTS + &if_packed, &if_packed, + // HOLEY_{SMI,}_ELEMENTS + &if_holey, &if_holey, + // PACKED_DOUBLE_ELEMENTS + &if_packed_double, + // HOLEY_DOUBLE_ELEMENTS + &if_holey_double}; + Switch(elements_kind, &if_unknown_kind, kinds, labels, arraysize(kinds)); + + BIND(&if_packed); { var_value.Bind(LoadFixedArrayElement(elements, index, 0, SMI_PARAMETERS)); Goto(&allocate_entry_if_needed); } - BIND(&packed_double_values); + BIND(&if_holey); { - Node* value = LoadFixedDoubleArrayElement( - elements, index, MachineType::Float64(), 0, SMI_PARAMETERS); - var_value.Bind(AllocateHeapNumberWithValue(value)); + Node* element = LoadFixedArrayElement(elements, index, 0, SMI_PARAMETERS); + var_value.Bind(element); + GotoIfNot(WordEqual(element, TheHoleConstant()), + &allocate_entry_if_needed); + GotoIf(IsNoElementsProtectorCellInvalid(), &if_generic); + var_value.Bind(UndefinedConstant()); Goto(&allocate_entry_if_needed); } - BIND(&holey_object_values); + BIND(&if_packed_double); { - // Check the no_elements_protector cell, and take the slow path if it's - // invalid. - GotoIf(IsNoElementsProtectorCellInvalid(), &generic_values); - - var_value.Bind(UndefinedConstant()); - Node* value = LoadFixedArrayElement(elements, index, 0, SMI_PARAMETERS); - GotoIf(WordEqual(value, TheHoleConstant()), &allocate_entry_if_needed); - var_value.Bind(value); + Node* value = LoadFixedDoubleArrayElement( + elements, index, MachineType::Float64(), 0, SMI_PARAMETERS); + var_value.Bind(AllocateHeapNumberWithValue(value)); Goto(&allocate_entry_if_needed); } - BIND(&holey_double_values); + BIND(&if_holey_double); { - // Check the no_elements_protector cell, and take the slow path if it's - // invalid. - GotoIf(IsNoElementsProtectorCellInvalid(), &generic_values); - - var_value.Bind(UndefinedConstant()); + Label if_hole(this, Label::kDeferred); Node* value = LoadFixedDoubleArrayElement( - elements, index, MachineType::Float64(), 0, SMI_PARAMETERS, - &allocate_entry_if_needed); + elements, index, MachineType::Float64(), 0, SMI_PARAMETERS, &if_hole); var_value.Bind(AllocateHeapNumberWithValue(value)); Goto(&allocate_entry_if_needed); + BIND(&if_hole); + GotoIf(IsNoElementsProtectorCellInvalid(), &if_generic); + var_value.Bind(UndefinedConstant()); + Goto(&allocate_entry_if_needed); } + + BIND(&if_unknown_kind); + Unreachable(); } - BIND(&if_isnotfastarray); + BIND(&if_other); { - Label if_istypedarray(this), if_isgeneric(this); - // If a is undefined, return CreateIterResultObject(undefined, true) GotoIf(IsUndefined(array), &allocate_iterator_result); - Node* array_type = LoadInstanceType(array); - Branch(InstanceTypeEqual(array_type, JS_TYPED_ARRAY_TYPE), &if_istypedarray, - &if_isgeneric); - - BIND(&if_isgeneric); - { - Label if_wasfastarray(this); + Node* length = + CallBuiltin(Builtins::kToLength, context, + GetProperty(context, array, factory()->length_string())); - Node* length = nullptr; - { - VARIABLE(var_length, MachineRepresentation::kTagged); - Label if_isarray(this), if_isnotarray(this), done(this); - Branch(InstanceTypeEqual(array_type, JS_ARRAY_TYPE), &if_isarray, - &if_isnotarray); + GotoIfNumberGreaterThanOrEqual(index, length, &set_done); - BIND(&if_isarray); - { - var_length.Bind(LoadJSArrayLength(array)); + var_value.Bind(index); + StoreObjectField(iterator, JSArrayIterator::kNextIndexOffset, + NumberInc(index)); + var_done.Bind(FalseConstant()); - // Invalidate protector cell if needed - Branch(WordNotEqual(orig_map, UndefinedConstant()), &if_wasfastarray, - &done); + GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField( + iterator, JSArrayIterator::kKindOffset), + Int32Constant(static_cast<int>(IterationKind::kKeys))), + &allocate_iterator_result); + Goto(&if_generic); + } - BIND(&if_wasfastarray); - { - Label if_invalid(this, Label::kDeferred); - // A fast array iterator transitioned to a slow iterator during - // iteration. Invalidate fast_array_iteration_protector cell to - // prevent potential deopt loops. - StoreObjectFieldNoWriteBarrier( - iterator, JSArrayIterator::kIteratedObjectMapOffset, - UndefinedConstant()); - GotoIf(Uint32LessThanOrEqual( - instance_type, - Int32Constant(JS_GENERIC_ARRAY_KEY_ITERATOR_TYPE)), - &done); - - Node* invalid = SmiConstant(Isolate::kProtectorInvalid); - Node* cell = LoadRoot(Heap::kFastArrayIterationProtectorRootIndex); - StoreObjectFieldNoWriteBarrier(cell, Cell::kValueOffset, invalid); - Goto(&done); - } - } + BIND(&if_generic); + { + var_value.Bind(GetProperty(context, array, index)); + Goto(&allocate_entry_if_needed); + } - BIND(&if_isnotarray); - { - Node* length = - GetProperty(context, array, factory()->length_string()); - var_length.Bind(ToLength_Inline(context, length)); - Goto(&done); - } + BIND(&if_typedarray); + { + Node* buffer = LoadObjectField(array, JSTypedArray::kBufferOffset); + GotoIf(IsDetachedBuffer(buffer), &if_detached); - BIND(&done); - length = var_length.value(); - } + Node* length = LoadObjectField(array, JSTypedArray::kLengthOffset); - GotoIfNumberGreaterThanOrEqual(index, length, &set_done); + CSA_ASSERT(this, TaggedIsSmi(length)); + CSA_ASSERT(this, TaggedIsSmi(index)); - StoreObjectField(iterator, JSArrayIterator::kNextIndexOffset, - NumberInc(index)); - var_done.Bind(FalseConstant()); + GotoIfNot(SmiBelow(index, length), &set_done); - Branch( - Uint32LessThanOrEqual( - instance_type, Int32Constant(JS_GENERIC_ARRAY_KEY_ITERATOR_TYPE)), - &allocate_key_result, &generic_values); + var_value.Bind(index); + Node* one = SmiConstant(1); + StoreObjectFieldNoWriteBarrier(iterator, JSArrayIterator::kNextIndexOffset, + SmiAdd(index, one)); + var_done.Bind(FalseConstant()); - BIND(&generic_values); - { - var_value.Bind(GetProperty(context, array, index)); - Goto(&allocate_entry_if_needed); - } - } + GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField( + iterator, JSArrayIterator::kKindOffset), + Int32Constant(static_cast<int>(IterationKind::kKeys))), + &allocate_iterator_result); - BIND(&if_istypedarray); - { - Node* buffer = LoadObjectField(array, JSTypedArray::kBufferOffset); - GotoIf(IsDetachedBuffer(buffer), &if_isdetached); + Node* elements_kind = LoadMapElementsKind(array_map); + Node* elements = LoadElements(array); + Node* base_ptr = + LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset); + Node* external_ptr = + LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset, + MachineType::Pointer()); + Node* data_ptr = IntPtrAdd(BitcastTaggedToWord(base_ptr), external_ptr); + + Label if_unknown_type(this, Label::kDeferred); + int32_t elements_kinds[] = { +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) TYPE##_ELEMENTS, + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + }; - Node* length = LoadObjectField(array, JSTypedArray::kLengthOffset); +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + Label if_##type##array(this); + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE - CSA_ASSERT(this, TaggedIsSmi(length)); - CSA_ASSERT(this, TaggedIsSmi(index)); + Label* elements_kind_labels[] = { +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) &if_##type##array, + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + }; + STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels)); - GotoIfNot(SmiBelow(index, length), &set_done); + Switch(elements_kind, &if_unknown_type, elements_kinds, + elements_kind_labels, arraysize(elements_kinds)); - Node* one = SmiConstant(1); - StoreObjectFieldNoWriteBarrier( - iterator, JSArrayIterator::kNextIndexOffset, SmiAdd(index, one)); - var_done.Bind(FalseConstant()); + BIND(&if_unknown_type); + Unreachable(); - Node* elements = LoadElements(array); - Node* base_ptr = - LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset); - Node* external_ptr = - LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset, - MachineType::Pointer()); - Node* data_ptr = IntPtrAdd(BitcastTaggedToWord(base_ptr), external_ptr); +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + BIND(&if_##type##array); \ + { \ + var_value.Bind(LoadFixedTypedArrayElementAsTagged( \ + data_ptr, index, TYPE##_ELEMENTS, SMI_PARAMETERS)); \ + Goto(&allocate_entry_if_needed); \ + } + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE - static int32_t kInstanceType[] = { - JS_TYPED_ARRAY_KEY_ITERATOR_TYPE, - JS_UINT8_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_UINT8_CLAMPED_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_INT8_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_UINT16_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_INT16_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_UINT32_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_INT32_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_FLOAT32_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_FLOAT64_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_BIGUINT64_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_BIGINT64_ARRAY_KEY_VALUE_ITERATOR_TYPE, - JS_UINT8_ARRAY_VALUE_ITERATOR_TYPE, - JS_UINT8_CLAMPED_ARRAY_VALUE_ITERATOR_TYPE, - JS_INT8_ARRAY_VALUE_ITERATOR_TYPE, - JS_UINT16_ARRAY_VALUE_ITERATOR_TYPE, - JS_INT16_ARRAY_VALUE_ITERATOR_TYPE, - JS_UINT32_ARRAY_VALUE_ITERATOR_TYPE, - JS_INT32_ARRAY_VALUE_ITERATOR_TYPE, - JS_FLOAT32_ARRAY_VALUE_ITERATOR_TYPE, - JS_FLOAT64_ARRAY_VALUE_ITERATOR_TYPE, - JS_BIGUINT64_ARRAY_VALUE_ITERATOR_TYPE, - JS_BIGINT64_ARRAY_VALUE_ITERATOR_TYPE, - }; - - Label uint8_values(this), int8_values(this), uint16_values(this), - int16_values(this), uint32_values(this), int32_values(this), - float32_values(this), float64_values(this), biguint64_values(this), - bigint64_values(this); - Label* kInstanceTypeHandlers[] = { - &allocate_key_result, &uint8_values, &uint8_values, - &int8_values, &uint16_values, &int16_values, - &uint32_values, &int32_values, &float32_values, - &float64_values, &biguint64_values, &bigint64_values, - &uint8_values, &uint8_values, &int8_values, - &uint16_values, &int16_values, &uint32_values, - &int32_values, &float32_values, &float64_values, - &biguint64_values, &bigint64_values, - }; - - var_done.Bind(FalseConstant()); - Switch(instance_type, &throw_bad_receiver, kInstanceType, - kInstanceTypeHandlers, arraysize(kInstanceType)); - - BIND(&uint8_values); - { - var_value.Bind(LoadFixedTypedArrayElementAsTagged( - data_ptr, index, UINT8_ELEMENTS, SMI_PARAMETERS)); - Goto(&allocate_entry_if_needed); - } - BIND(&int8_values); - { - var_value.Bind(LoadFixedTypedArrayElementAsTagged( - data_ptr, index, INT8_ELEMENTS, SMI_PARAMETERS)); - Goto(&allocate_entry_if_needed); - } - BIND(&uint16_values); - { - var_value.Bind(LoadFixedTypedArrayElementAsTagged( - data_ptr, index, UINT16_ELEMENTS, SMI_PARAMETERS)); - Goto(&allocate_entry_if_needed); - } - BIND(&int16_values); - { - var_value.Bind(LoadFixedTypedArrayElementAsTagged( - data_ptr, index, INT16_ELEMENTS, SMI_PARAMETERS)); - Goto(&allocate_entry_if_needed); - } - BIND(&uint32_values); - { - var_value.Bind(LoadFixedTypedArrayElementAsTagged( - data_ptr, index, UINT32_ELEMENTS, SMI_PARAMETERS)); - Goto(&allocate_entry_if_needed); - } - BIND(&int32_values); - { - var_value.Bind(LoadFixedTypedArrayElementAsTagged( - data_ptr, index, INT32_ELEMENTS, SMI_PARAMETERS)); - Goto(&allocate_entry_if_needed); - } - BIND(&float32_values); - { - var_value.Bind(LoadFixedTypedArrayElementAsTagged( - data_ptr, index, FLOAT32_ELEMENTS, SMI_PARAMETERS)); - Goto(&allocate_entry_if_needed); - } - BIND(&float64_values); - { - var_value.Bind(LoadFixedTypedArrayElementAsTagged( - data_ptr, index, FLOAT64_ELEMENTS, SMI_PARAMETERS)); - Goto(&allocate_entry_if_needed); - } - BIND(&biguint64_values); - { - var_value.Bind(LoadFixedTypedArrayElementAsTagged( - data_ptr, index, BIGUINT64_ELEMENTS, SMI_PARAMETERS)); - Goto(&allocate_entry_if_needed); - } - BIND(&bigint64_values); - { - var_value.Bind(LoadFixedTypedArrayElementAsTagged( - data_ptr, index, BIGINT64_ELEMENTS, SMI_PARAMETERS)); - Goto(&allocate_entry_if_needed); - } - } + BIND(&if_detached); + ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name); } BIND(&set_done); @@ -3790,17 +3791,11 @@ TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) { Goto(&allocate_iterator_result); } - BIND(&allocate_key_result); - { - var_value.Bind(index); - var_done.Bind(FalseConstant()); - Goto(&allocate_iterator_result); - } - BIND(&allocate_entry_if_needed); { - GotoIf(Uint32LessThan(Int32Constant(LAST_ARRAY_KEY_VALUE_ITERATOR_TYPE), - instance_type), + GotoIf(Word32Equal(LoadAndUntagToWord32ObjectField( + iterator, JSArrayIterator::kKindOffset), + Int32Constant(static_cast<int>(IterationKind::kValues))), &allocate_iterator_result); Node* elements = AllocateFixedArray(PACKED_ELEMENTS, IntPtrConstant(2)); @@ -3845,9 +3840,261 @@ TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) { ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver, StringConstant(method_name), iterator); } +} + +namespace { + +class ArrayFlattenAssembler : public CodeStubAssembler { + public: + explicit ArrayFlattenAssembler(compiler::CodeAssemblerState* state) + : CodeStubAssembler(state) {} + + // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray + Node* FlattenIntoArray(Node* context, Node* target, Node* source, + Node* source_length, Node* start, Node* depth, + Node* mapper_function = nullptr, + Node* this_arg = nullptr) { + CSA_ASSERT(this, IsJSReceiver(target)); + CSA_ASSERT(this, IsJSReceiver(source)); + CSA_ASSERT(this, IsNumberPositive(source_length)); + CSA_ASSERT(this, IsNumberPositive(start)); + CSA_ASSERT(this, IsNumber(depth)); + + // 1. Let targetIndex be start. + VARIABLE(var_target_index, MachineRepresentation::kTagged, start); + + // 2. Let sourceIndex be 0. + VARIABLE(var_source_index, MachineRepresentation::kTagged, SmiConstant(0)); + + // 3. Repeat... + Label loop(this, {&var_target_index, &var_source_index}), done_loop(this); + Goto(&loop); + BIND(&loop); + { + Node* const source_index = var_source_index.value(); + Node* const target_index = var_target_index.value(); + + // ...while sourceIndex < sourceLen + GotoIfNumberGreaterThanOrEqual(source_index, source_length, &done_loop); + + // a. Let P be ! ToString(sourceIndex). + // b. Let exists be ? HasProperty(source, P). + CSA_ASSERT(this, SmiGreaterThanOrEqual(source_index, SmiConstant(0))); + Node* const exists = + HasProperty(source, source_index, context, kHasProperty); + + // c. If exists is true, then + Label next(this); + GotoIfNot(IsTrue(exists), &next); + { + // i. Let element be ? Get(source, P). + Node* element = GetProperty(context, source, source_index); + + // ii. If mapperFunction is present, then + if (mapper_function != nullptr) { + CSA_ASSERT(this, Word32Or(IsUndefined(mapper_function), + IsCallable(mapper_function))); + DCHECK_NOT_NULL(this_arg); + + // 1. Set element to ? Call(mapperFunction, thisArg , « element, + // sourceIndex, source »). + element = + CallJS(CodeFactory::Call(isolate()), context, mapper_function, + this_arg, element, source_index, source); + } + + // iii. Let shouldFlatten be false. + Label if_flatten_array(this), if_flatten_proxy(this, Label::kDeferred), + if_noflatten(this); + // iv. If depth > 0, then + GotoIfNumberGreaterThanOrEqual(SmiConstant(0), depth, &if_noflatten); + // 1. Set shouldFlatten to ? IsArray(element). + GotoIf(TaggedIsSmi(element), &if_noflatten); + GotoIf(IsJSArray(element), &if_flatten_array); + GotoIfNot(IsJSProxy(element), &if_noflatten); + Branch(IsTrue(CallRuntime(Runtime::kArrayIsArray, context, element)), + &if_flatten_proxy, &if_noflatten); + + BIND(&if_flatten_array); + { + CSA_ASSERT(this, IsJSArray(element)); + + // 1. Let elementLen be ? ToLength(? Get(element, "length")). + Node* const element_length = + LoadObjectField(element, JSArray::kLengthOffset); + + // 2. Set targetIndex to ? FlattenIntoArray(target, element, + // elementLen, targetIndex, + // depth - 1). + var_target_index.Bind( + CallBuiltin(Builtins::kFlattenIntoArray, context, target, element, + element_length, target_index, NumberDec(depth))); + Goto(&next); + } + + BIND(&if_flatten_proxy); + { + CSA_ASSERT(this, IsJSProxy(element)); + + // 1. Let elementLen be ? ToLength(? Get(element, "length")). + Node* const element_length = ToLength_Inline( + context, GetProperty(context, element, LengthStringConstant())); + + // 2. Set targetIndex to ? FlattenIntoArray(target, element, + // elementLen, targetIndex, + // depth - 1). + var_target_index.Bind( + CallBuiltin(Builtins::kFlattenIntoArray, context, target, element, + element_length, target_index, NumberDec(depth))); + Goto(&next); + } + + BIND(&if_noflatten); + { + // 1. If targetIndex >= 2^53-1, throw a TypeError exception. + Label throw_error(this, Label::kDeferred); + GotoIfNumberGreaterThanOrEqual( + target_index, NumberConstant(kMaxSafeInteger), &throw_error); + + // 2. Perform ? CreateDataPropertyOrThrow(target, + // ! ToString(targetIndex), + // element). + CallRuntime(Runtime::kCreateDataProperty, context, target, + target_index, element); + + // 3. Increase targetIndex by 1. + var_target_index.Bind(NumberInc(target_index)); + Goto(&next); + + BIND(&throw_error); + ThrowTypeError(context, MessageTemplate::kFlattenPastSafeLength, + source_length, target_index); + } + } + BIND(&next); + + // d. Increase sourceIndex by 1. + var_source_index.Bind(NumberInc(source_index)); + Goto(&loop); + } + + BIND(&done_loop); + return var_target_index.value(); + } +}; + +} // namespace + +// https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray +TF_BUILTIN(FlattenIntoArray, ArrayFlattenAssembler) { + Node* const context = Parameter(Descriptor::kContext); + Node* const target = Parameter(Descriptor::kTarget); + Node* const source = Parameter(Descriptor::kSource); + Node* const source_length = Parameter(Descriptor::kSourceLength); + Node* const start = Parameter(Descriptor::kStart); + Node* const depth = Parameter(Descriptor::kDepth); + + Return( + FlattenIntoArray(context, target, source, source_length, start, depth)); +} + +// https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray +TF_BUILTIN(FlatMapIntoArray, ArrayFlattenAssembler) { + Node* const context = Parameter(Descriptor::kContext); + Node* const target = Parameter(Descriptor::kTarget); + Node* const source = Parameter(Descriptor::kSource); + Node* const source_length = Parameter(Descriptor::kSourceLength); + Node* const start = Parameter(Descriptor::kStart); + Node* const depth = Parameter(Descriptor::kDepth); + Node* const mapper_function = Parameter(Descriptor::kMapperFunction); + Node* const this_arg = Parameter(Descriptor::kThisArg); + + Return(FlattenIntoArray(context, target, source, source_length, start, depth, + mapper_function, this_arg)); +} + +// https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatten +TF_BUILTIN(ArrayPrototypeFlatten, CodeStubAssembler) { + Node* const argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* const context = Parameter(BuiltinDescriptor::kContext); + Node* const receiver = args.GetReceiver(); + Node* const depth = args.GetOptionalArgumentValue(0); + + // 1. Let O be ? ToObject(this value). + Node* const o = ToObject(context, receiver); + + // 2. Let sourceLen be ? ToLength(? Get(O, "length")). + Node* const source_length = + ToLength_Inline(context, GetProperty(context, o, LengthStringConstant())); + + // 3. Let depthNum be 1. + VARIABLE(var_depth_num, MachineRepresentation::kTagged, SmiConstant(1)); + + // 4. If depth is not undefined, then + Label done(this); + GotoIf(IsUndefined(depth), &done); + { + // a. Set depthNum to ? ToInteger(depth). + var_depth_num.Bind(ToInteger_Inline(context, depth)); + Goto(&done); + } + BIND(&done); + + // 5. Let A be ? ArraySpeciesCreate(O, 0). + Node* const constructor = + CallRuntime(Runtime::kArraySpeciesConstructor, context, o); + Node* const a = ConstructJS(CodeFactory::Construct(isolate()), context, + constructor, SmiConstant(0)); + + // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum). + CallBuiltin(Builtins::kFlattenIntoArray, context, a, o, source_length, + SmiConstant(0), var_depth_num.value()); + + // 7. Return A. + args.PopAndReturn(a); +} + +// https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap +TF_BUILTIN(ArrayPrototypeFlatMap, CodeStubAssembler) { + Node* const argc = + ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)); + CodeStubArguments args(this, argc); + Node* const context = Parameter(BuiltinDescriptor::kContext); + Node* const receiver = args.GetReceiver(); + Node* const mapper_function = args.GetOptionalArgumentValue(0); + + // 1. Let O be ? ToObject(this value). + Node* const o = ToObject(context, receiver); + + // 2. Let sourceLen be ? ToLength(? Get(O, "length")). + Node* const source_length = + ToLength_Inline(context, GetProperty(context, o, LengthStringConstant())); + + // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception. + Label if_not_callable(this, Label::kDeferred); + GotoIf(TaggedIsSmi(mapper_function), &if_not_callable); + GotoIfNot(IsCallable(mapper_function), &if_not_callable); + + // 4. If thisArg is present, let T be thisArg; else let T be undefined. + Node* const t = args.GetOptionalArgumentValue(1); + + // 5. Let A be ? ArraySpeciesCreate(O, 0). + Node* const constructor = + CallRuntime(Runtime::kArraySpeciesConstructor, context, o); + Node* const a = ConstructJS(CodeFactory::Construct(isolate()), context, + constructor, SmiConstant(0)); + + // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T). + CallBuiltin(Builtins::kFlatMapIntoArray, context, a, o, source_length, + SmiConstant(0), SmiConstant(1), mapper_function, t); + + // 7. Return A. + args.PopAndReturn(a); - BIND(&if_isdetached); - ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name); + BIND(&if_not_callable); + { ThrowTypeError(context, MessageTemplate::kMapperFunctionNonCallable); } } } // namespace internal |