| Index: src/builtins/builtins-array.cc
|
| diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
|
| index 2916cff23d2cf9b767a0f072156fa6829fc9603f..c565fff1cd0c9770e136057002450b7e2ebdbd47 100644
|
| --- a/src/builtins/builtins-array.cc
|
| +++ b/src/builtins/builtins-array.cc
|
| @@ -188,160 +188,6 @@ BUILTIN(ArrayPush) {
|
| return Smi::FromInt(new_length);
|
| }
|
|
|
| -TF_BUILTIN(FastArrayPush, CodeStubAssembler) {
|
| - Variable arg_index(this, MachineType::PointerRepresentation());
|
| - Label default_label(this, &arg_index);
|
| - Label smi_transition(this);
|
| - Label object_push_pre(this);
|
| - Label object_push(this, &arg_index);
|
| - Label double_push(this, &arg_index);
|
| - Label double_transition(this);
|
| - Label runtime(this, Label::kDeferred);
|
| -
|
| - Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
|
| - Node* context = Parameter(BuiltinDescriptor::kContext);
|
| - Node* new_target = Parameter(BuiltinDescriptor::kNewTarget);
|
| -
|
| - CodeStubArguments args(this, ChangeInt32ToIntPtr(argc));
|
| - Node* receiver = args.GetReceiver();
|
| - Node* kind = nullptr;
|
| -
|
| - Label fast(this);
|
| - BranchIfFastJSArray(receiver, context, FastJSArrayAccessMode::ANY_ACCESS,
|
| - &fast, &runtime);
|
| -
|
| - Bind(&fast);
|
| - {
|
| - // Disallow pushing onto prototypes. It might be the JSArray prototype.
|
| - // Disallow pushing onto non-extensible objects.
|
| - Comment("Disallow pushing onto prototypes");
|
| - Node* map = LoadMap(receiver);
|
| - Node* bit_field2 = LoadMapBitField2(map);
|
| - int mask = static_cast<int>(Map::IsPrototypeMapBits::kMask) |
|
| - (1 << Map::kIsExtensible);
|
| - Node* test = Word32And(bit_field2, Int32Constant(mask));
|
| - GotoIf(Word32NotEqual(test, Int32Constant(1 << Map::kIsExtensible)),
|
| - &runtime);
|
| -
|
| - // Disallow pushing onto arrays in dictionary named property mode. We need
|
| - // to figure out whether the length property is still writable.
|
| - Comment("Disallow pushing onto arrays in dictionary named property mode");
|
| - GotoIf(IsDictionaryMap(map), &runtime);
|
| -
|
| - // Check whether the length property is writable. The length property is the
|
| - // only default named property on arrays. It's nonconfigurable, hence is
|
| - // guaranteed to stay the first property.
|
| - Node* descriptors = LoadMapDescriptors(map);
|
| - Node* details =
|
| - LoadFixedArrayElement(descriptors, DescriptorArray::ToDetailsIndex(0));
|
| - GotoIf(IsSetSmi(details, PropertyDetails::kAttributesReadOnlyMask),
|
| - &runtime);
|
| -
|
| - arg_index.Bind(IntPtrConstant(0));
|
| - kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
|
| -
|
| - GotoIf(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_SMI_ELEMENTS)),
|
| - &object_push_pre);
|
| -
|
| - Node* new_length = BuildAppendJSArray(FAST_SMI_ELEMENTS, context, receiver,
|
| - args, arg_index, &smi_transition);
|
| - args.PopAndReturn(new_length);
|
| - }
|
| -
|
| - // If the argument is not a smi, then use a heavyweight SetProperty to
|
| - // transition the array for only the single next element. If the argument is
|
| - // a smi, the failure is due to some other reason and we should fall back on
|
| - // the most generic implementation for the rest of the array.
|
| - Bind(&smi_transition);
|
| - {
|
| - Node* arg = args.AtIndex(arg_index.value());
|
| - GotoIf(TaggedIsSmi(arg), &default_label);
|
| - Node* length = LoadJSArrayLength(receiver);
|
| - // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
|
| - // calling into the runtime to do the elements transition is overkill.
|
| - CallRuntime(Runtime::kSetProperty, context, receiver, length, arg,
|
| - SmiConstant(STRICT));
|
| - Increment(arg_index);
|
| - // The runtime SetProperty call could have converted the array to dictionary
|
| - // mode, which must be detected to abort the fast-path.
|
| - Node* map = LoadMap(receiver);
|
| - Node* bit_field2 = LoadMapBitField2(map);
|
| - Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
|
| - GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)),
|
| - &default_label);
|
| -
|
| - GotoIfNotNumber(arg, &object_push);
|
| - Goto(&double_push);
|
| - }
|
| -
|
| - Bind(&object_push_pre);
|
| - {
|
| - Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)),
|
| - &double_push, &object_push);
|
| - }
|
| -
|
| - Bind(&object_push);
|
| - {
|
| - Node* new_length = BuildAppendJSArray(FAST_ELEMENTS, context, receiver,
|
| - args, arg_index, &default_label);
|
| - args.PopAndReturn(new_length);
|
| - }
|
| -
|
| - Bind(&double_push);
|
| - {
|
| - Node* new_length =
|
| - BuildAppendJSArray(FAST_DOUBLE_ELEMENTS, context, receiver, args,
|
| - arg_index, &double_transition);
|
| - args.PopAndReturn(new_length);
|
| - }
|
| -
|
| - // If the argument is not a double, then use a heavyweight SetProperty to
|
| - // transition the array for only the single next element. If the argument is
|
| - // a double, the failure is due to some other reason and we should fall back
|
| - // on the most generic implementation for the rest of the array.
|
| - Bind(&double_transition);
|
| - {
|
| - Node* arg = args.AtIndex(arg_index.value());
|
| - GotoIfNumber(arg, &default_label);
|
| - Node* length = LoadJSArrayLength(receiver);
|
| - // TODO(danno): Use the KeyedStoreGeneric stub here when possible,
|
| - // calling into the runtime to do the elements transition is overkill.
|
| - CallRuntime(Runtime::kSetProperty, context, receiver, length, arg,
|
| - SmiConstant(STRICT));
|
| - Increment(arg_index);
|
| - // The runtime SetProperty call could have converted the array to dictionary
|
| - // mode, which must be detected to abort the fast-path.
|
| - Node* map = LoadMap(receiver);
|
| - Node* bit_field2 = LoadMapBitField2(map);
|
| - Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
|
| - GotoIf(Word32Equal(kind, Int32Constant(DICTIONARY_ELEMENTS)),
|
| - &default_label);
|
| - Goto(&object_push);
|
| - }
|
| -
|
| - // Fallback that stores un-processed arguments using the full, heavyweight
|
| - // SetProperty machinery.
|
| - Bind(&default_label);
|
| - {
|
| - args.ForEach(
|
| - [this, receiver, context](Node* arg) {
|
| - Node* length = LoadJSArrayLength(receiver);
|
| - CallRuntime(Runtime::kSetProperty, context, receiver, length, arg,
|
| - SmiConstant(STRICT));
|
| - },
|
| - arg_index.value());
|
| - args.PopAndReturn(LoadJSArrayLength(receiver));
|
| - }
|
| -
|
| - Bind(&runtime);
|
| - {
|
| - Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
|
| - MachineType::TaggedPointer());
|
| - TailCallStub(CodeFactory::ArrayPush(isolate()), context, target, new_target,
|
| - argc);
|
| - }
|
| -}
|
| -
|
| BUILTIN(ArrayPop) {
|
| HandleScope scope(isolate);
|
| Handle<Object> receiver = args.receiver();
|
| @@ -415,305 +261,6 @@ BUILTIN(ArrayUnshift) {
|
| return Smi::FromInt(new_length);
|
| }
|
|
|
| -class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
| - public:
|
| - explicit ArrayBuiltinCodeStubAssembler(compiler::CodeAssemblerState* state)
|
| - : CodeStubAssembler(state) {}
|
| -
|
| - typedef std::function<Node*(Node* o, Node* len)> BuiltinResultGenerator;
|
| - typedef std::function<void(Node* a, Node* pK, Node* value)>
|
| - CallResultProcessor;
|
| -
|
| - void GenerateArrayIteratingBuiltinBody(
|
| - const char* name, Node* receiver, Node* callbackfn, Node* this_arg,
|
| - Node* context, const BuiltinResultGenerator& generator,
|
| - const CallResultProcessor& processor) {
|
| - Variable k(this, MachineRepresentation::kTagged, SmiConstant(0));
|
| - Label non_array(this), slow(this, &k), array_changes(this, &k);
|
| -
|
| - // TODO(danno): Seriously? Do we really need to throw the exact error
|
| - // message on null and undefined so that the webkit tests pass?
|
| - Label throw_null_undefined_exception(this, Label::kDeferred);
|
| - GotoIf(WordEqual(receiver, NullConstant()),
|
| - &throw_null_undefined_exception);
|
| - GotoIf(WordEqual(receiver, UndefinedConstant()),
|
| - &throw_null_undefined_exception);
|
| -
|
| - // By the book: taken directly from the ECMAScript 2015 specification
|
| -
|
| - // 1. Let O be ToObject(this value).
|
| - // 2. ReturnIfAbrupt(O)
|
| - Node* o = CallStub(CodeFactory::ToObject(isolate()), context, receiver);
|
| -
|
| - // 3. Let len be ToLength(Get(O, "length")).
|
| - // 4. ReturnIfAbrupt(len).
|
| - Variable merged_length(this, MachineRepresentation::kTagged);
|
| - Label has_length(this, &merged_length), not_js_array(this);
|
| - GotoIf(DoesntHaveInstanceType(o, JS_ARRAY_TYPE), ¬_js_array);
|
| - merged_length.Bind(LoadJSArrayLength(o));
|
| - Goto(&has_length);
|
| - Bind(¬_js_array);
|
| - Node* len_property =
|
| - GetProperty(context, o, isolate()->factory()->length_string());
|
| - merged_length.Bind(
|
| - CallStub(CodeFactory::ToLength(isolate()), context, len_property));
|
| - Goto(&has_length);
|
| - Bind(&has_length);
|
| - Node* len = merged_length.value();
|
| -
|
| - // 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
|
| - Label type_exception(this, Label::kDeferred);
|
| - Label done(this);
|
| - GotoIf(TaggedIsSmi(callbackfn), &type_exception);
|
| - Branch(IsCallableMap(LoadMap(callbackfn)), &done, &type_exception);
|
| -
|
| - Bind(&throw_null_undefined_exception);
|
| - {
|
| - CallRuntime(
|
| - Runtime::kThrowTypeError, context,
|
| - SmiConstant(MessageTemplate::kCalledOnNullOrUndefined),
|
| - HeapConstant(isolate()->factory()->NewStringFromAsciiChecked(name)));
|
| - Unreachable();
|
| - }
|
| -
|
| - Bind(&type_exception);
|
| - {
|
| - CallRuntime(Runtime::kThrowTypeError, context,
|
| - SmiConstant(MessageTemplate::kCalledNonCallable), callbackfn);
|
| - Unreachable();
|
| - }
|
| -
|
| - Bind(&done);
|
| -
|
| - Node* a = generator(o, len);
|
| -
|
| - // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
| - // [Already done by the arguments adapter]
|
| -
|
| - HandleFastElements(context, this_arg, o, len, callbackfn, processor, a, k,
|
| - &slow);
|
| -
|
| - // 7. Let k be 0.
|
| - // Already done above in initialization of the Variable k
|
| -
|
| - Bind(&slow);
|
| - {
|
| - // 8. Repeat, while k < len
|
| - Label loop(this, &k);
|
| - Label after_loop(this);
|
| - Goto(&loop);
|
| - Bind(&loop);
|
| - {
|
| - GotoUnlessNumberLessThan(k.value(), len, &after_loop);
|
| -
|
| - Label done_element(this);
|
| - // a. Let Pk be ToString(k).
|
| - Node* p_k = ToString(context, k.value());
|
| -
|
| - // b. Let kPresent be HasProperty(O, Pk).
|
| - // c. ReturnIfAbrupt(kPresent).
|
| - Node* k_present =
|
| - CallStub(CodeFactory::HasProperty(isolate()), context, p_k, o);
|
| -
|
| - // d. If kPresent is true, then
|
| - GotoIf(WordNotEqual(k_present, TrueConstant()), &done_element);
|
| -
|
| - // i. Let kValue be Get(O, Pk).
|
| - // ii. ReturnIfAbrupt(kValue).
|
| - Node* k_value = GetProperty(context, o, k.value());
|
| -
|
| - // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»).
|
| - // iv. ReturnIfAbrupt(funcResult).
|
| - Node* result = CallJS(CodeFactory::Call(isolate()), context, callbackfn,
|
| - this_arg, k_value, k.value(), o);
|
| -
|
| - processor(a, p_k, result);
|
| - Goto(&done_element);
|
| - Bind(&done_element);
|
| -
|
| - // e. Increase k by 1.
|
| - k.Bind(NumberInc(k.value()));
|
| - Goto(&loop);
|
| - }
|
| - Bind(&after_loop);
|
| - Return(a);
|
| - }
|
| - }
|
| -
|
| - private:
|
| - Node* VisitAllFastElementsOneKind(Node* context, ElementsKind kind,
|
| - Node* this_arg, Node* o, Node* len,
|
| - Node* callbackfn,
|
| - const CallResultProcessor& processor,
|
| - Node* a, Label* array_changed,
|
| - ParameterMode mode) {
|
| - Comment("begin VisitAllFastElementsOneKind");
|
| - Variable original_map(this, MachineRepresentation::kTagged);
|
| - original_map.Bind(LoadMap(o));
|
| - VariableList list({&original_map}, zone());
|
| - Node* last_index = nullptr;
|
| - BuildFastLoop(
|
| - list, IntPtrOrSmiConstant(0, mode), TaggedToParameter(len, mode),
|
| - [=, &original_map, &last_index](Node* index) {
|
| - last_index = index;
|
| - Label one_element_done(this), hole_element(this);
|
| -
|
| - // Check if o's map has changed during the callback. If so, we have to
|
| - // fall back to the slower spec implementation for the rest of the
|
| - // iteration.
|
| - Node* o_map = LoadMap(o);
|
| - GotoIf(WordNotEqual(o_map, original_map.value()), array_changed);
|
| -
|
| - // Check if o's length has changed during the callback and if the
|
| - // index is now out of range of the new length.
|
| - Node* tagged_index = ParameterToTagged(index, mode);
|
| - GotoIf(SmiGreaterThanOrEqual(tagged_index, LoadJSArrayLength(o)),
|
| - array_changed);
|
| -
|
| - // Re-load the elements array. If may have been resized.
|
| - Node* elements = LoadElements(o);
|
| -
|
| - // Fast case: load the element directly from the elements FixedArray
|
| - // and call the callback if the element is not the hole.
|
| - DCHECK(kind == FAST_ELEMENTS || kind == FAST_DOUBLE_ELEMENTS);
|
| - int base_size = kind == FAST_ELEMENTS
|
| - ? FixedArray::kHeaderSize
|
| - : (FixedArray::kHeaderSize - kHeapObjectTag);
|
| - Node* offset = ElementOffsetFromIndex(index, kind, mode, base_size);
|
| - Node* value = nullptr;
|
| - if (kind == FAST_ELEMENTS) {
|
| - value = LoadObjectField(elements, offset);
|
| - GotoIf(WordEqual(value, TheHoleConstant()), &hole_element);
|
| - } else {
|
| - Node* double_value =
|
| - LoadDoubleWithHoleCheck(elements, offset, &hole_element);
|
| - value = AllocateHeapNumberWithValue(double_value);
|
| - }
|
| - Node* result = CallJS(CodeFactory::Call(isolate()), context,
|
| - callbackfn, this_arg, value, tagged_index, o);
|
| - processor(a, tagged_index, result);
|
| - Goto(&one_element_done);
|
| -
|
| - Bind(&hole_element);
|
| - // Check if o's prototype change unexpectedly has elements after the
|
| - // callback in the case of a hole.
|
| - BranchIfPrototypesHaveNoElements(o_map, &one_element_done,
|
| - array_changed);
|
| -
|
| - Bind(&one_element_done);
|
| - },
|
| - 1, mode, IndexAdvanceMode::kPost);
|
| - Comment("end VisitAllFastElementsOneKind");
|
| - return last_index;
|
| - }
|
| -
|
| - void HandleFastElements(Node* context, Node* this_arg, Node* o, Node* len,
|
| - Node* callbackfn, CallResultProcessor processor,
|
| - Node* a, Variable& k, Label* slow) {
|
| - Label switch_on_elements_kind(this), fast_elements(this),
|
| - maybe_double_elements(this), fast_double_elements(this);
|
| -
|
| - Comment("begin HandleFastElements");
|
| - // Non-smi lengths must use the slow path.
|
| - GotoIf(TaggedIsNotSmi(len), slow);
|
| -
|
| - BranchIfFastJSArray(o, context,
|
| - CodeStubAssembler::FastJSArrayAccessMode::INBOUNDS_READ,
|
| - &switch_on_elements_kind, slow);
|
| -
|
| - Bind(&switch_on_elements_kind);
|
| - // Select by ElementsKind
|
| - Node* o_map = LoadMap(o);
|
| - Node* bit_field2 = LoadMapBitField2(o_map);
|
| - Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
|
| - Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)),
|
| - &maybe_double_elements, &fast_elements);
|
| -
|
| - ParameterMode mode = OptimalParameterMode();
|
| - Bind(&fast_elements);
|
| - {
|
| - Label array_changed(this, Label::kDeferred);
|
| - Node* last_index = VisitAllFastElementsOneKind(
|
| - context, FAST_ELEMENTS, this_arg, o, len, callbackfn, processor, a,
|
| - &array_changed, mode);
|
| -
|
| - // No exception, return success
|
| - Return(a);
|
| -
|
| - Bind(&array_changed);
|
| - k.Bind(ParameterToTagged(last_index, mode));
|
| - Goto(slow);
|
| - }
|
| -
|
| - Bind(&maybe_double_elements);
|
| - Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_DOUBLE_ELEMENTS)),
|
| - slow, &fast_double_elements);
|
| -
|
| - Bind(&fast_double_elements);
|
| - {
|
| - Label array_changed(this, Label::kDeferred);
|
| - Node* last_index = VisitAllFastElementsOneKind(
|
| - context, FAST_DOUBLE_ELEMENTS, this_arg, o, len, callbackfn,
|
| - processor, a, &array_changed, mode);
|
| -
|
| - // No exception, return success
|
| - Return(a);
|
| -
|
| - Bind(&array_changed);
|
| - k.Bind(ParameterToTagged(last_index, mode));
|
| - Goto(slow);
|
| - }
|
| - }
|
| -};
|
| -
|
| -TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) {
|
| - Node* receiver = Parameter(ForEachDescriptor::kReceiver);
|
| - Node* callbackfn = Parameter(ForEachDescriptor::kCallback);
|
| - Node* this_arg = Parameter(ForEachDescriptor::kThisArg);
|
| - Node* context = Parameter(ForEachDescriptor::kContext);
|
| -
|
| - GenerateArrayIteratingBuiltinBody(
|
| - "Array.prototype.forEach", receiver, callbackfn, this_arg, context,
|
| - [=](Node*, Node*) { return UndefinedConstant(); },
|
| - [](Node* a, Node* p_k, Node* value) {});
|
| -}
|
| -
|
| -TF_BUILTIN(ArrayEvery, ArrayBuiltinCodeStubAssembler) {
|
| - Node* receiver = Parameter(ForEachDescriptor::kReceiver);
|
| - Node* callbackfn = Parameter(ForEachDescriptor::kCallback);
|
| - Node* this_arg = Parameter(ForEachDescriptor::kThisArg);
|
| - Node* context = Parameter(ForEachDescriptor::kContext);
|
| -
|
| - GenerateArrayIteratingBuiltinBody(
|
| - "Array.prototype.every", receiver, callbackfn, this_arg, context,
|
| - [=](Node*, Node*) { return TrueConstant(); },
|
| - [=](Node* a, Node* p_k, Node* value) {
|
| - Label true_continue(this), return_false(this);
|
| - BranchIfToBooleanIsTrue(value, &true_continue, &return_false);
|
| - Bind(&return_false);
|
| - Return(FalseConstant());
|
| - Bind(&true_continue);
|
| - });
|
| -}
|
| -
|
| -TF_BUILTIN(ArraySome, ArrayBuiltinCodeStubAssembler) {
|
| - Node* receiver = Parameter(ForEachDescriptor::kReceiver);
|
| - Node* callbackfn = Parameter(ForEachDescriptor::kCallback);
|
| - Node* this_arg = Parameter(ForEachDescriptor::kThisArg);
|
| - Node* context = Parameter(ForEachDescriptor::kContext);
|
| -
|
| - GenerateArrayIteratingBuiltinBody(
|
| - "Array.prototype.some", receiver, callbackfn, this_arg, context,
|
| - [=](Node*, Node*) { return FalseConstant(); },
|
| - [=](Node* a, Node* p_k, Node* value) {
|
| - Label false_continue(this), return_true(this);
|
| - BranchIfToBooleanIsTrue(value, &return_true, &false_continue);
|
| - Bind(&return_true);
|
| - Return(TrueConstant());
|
| - Bind(&false_continue);
|
| - });
|
| -}
|
| -
|
| BUILTIN(ArraySlice) {
|
| HandleScope scope(isolate);
|
| Handle<Object> receiver = args.receiver();
|
| @@ -1692,1057 +1239,5 @@ BUILTIN(ArrayConcat) {
|
| return Slow_ArrayConcat(&args, species, isolate);
|
| }
|
|
|
| -TF_BUILTIN(ArrayIsArray, CodeStubAssembler) {
|
| - Node* object = Parameter(1);
|
| - Node* context = Parameter(4);
|
| -
|
| - Label call_runtime(this), return_true(this), return_false(this);
|
| -
|
| - GotoIf(TaggedIsSmi(object), &return_false);
|
| - Node* instance_type = LoadInstanceType(object);
|
| -
|
| - GotoIf(Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE)),
|
| - &return_true);
|
| -
|
| - // TODO(verwaest): Handle proxies in-place.
|
| - Branch(Word32Equal(instance_type, Int32Constant(JS_PROXY_TYPE)),
|
| - &call_runtime, &return_false);
|
| -
|
| - Bind(&return_true);
|
| - Return(BooleanConstant(true));
|
| -
|
| - Bind(&return_false);
|
| - Return(BooleanConstant(false));
|
| -
|
| - Bind(&call_runtime);
|
| - Return(CallRuntime(Runtime::kArrayIsArray, context, object));
|
| -}
|
| -
|
| -TF_BUILTIN(ArrayIncludes, CodeStubAssembler) {
|
| - Node* const array = Parameter(0);
|
| - Node* const search_element = Parameter(1);
|
| - Node* const start_from = Parameter(2);
|
| - Node* const context = Parameter(3 + 2);
|
| -
|
| - Variable index_var(this, MachineType::PointerRepresentation());
|
| -
|
| - Label init_k(this), return_true(this), return_false(this), call_runtime(this);
|
| - Label init_len(this), select_loop(this);
|
| -
|
| - index_var.Bind(IntPtrConstant(0));
|
| -
|
| - // Take slow path if not a JSArray, if retrieving elements requires
|
| - // traversing prototype, or if access checks are required.
|
| - BranchIfFastJSArray(array, context, FastJSArrayAccessMode::INBOUNDS_READ,
|
| - &init_len, &call_runtime);
|
| -
|
| - Bind(&init_len);
|
| - // JSArray length is always an Smi for fast arrays.
|
| - CSA_ASSERT(this, TaggedIsSmi(LoadObjectField(array, JSArray::kLengthOffset)));
|
| - Node* const len = LoadAndUntagObjectField(array, JSArray::kLengthOffset);
|
| -
|
| - GotoIf(IsUndefined(start_from), &select_loop);
|
| -
|
| - // Bailout to slow path if startIndex is not an Smi.
|
| - Branch(TaggedIsSmi(start_from), &init_k, &call_runtime);
|
| -
|
| - Bind(&init_k);
|
| - CSA_ASSERT(this, TaggedIsSmi(start_from));
|
| - Node* const untagged_start_from = SmiToWord(start_from);
|
| - index_var.Bind(Select(
|
| - IntPtrGreaterThanOrEqual(untagged_start_from, IntPtrConstant(0)),
|
| - [=]() { return untagged_start_from; },
|
| - [=]() {
|
| - Node* const index = IntPtrAdd(len, untagged_start_from);
|
| - return SelectConstant(IntPtrLessThan(index, IntPtrConstant(0)),
|
| - IntPtrConstant(0), index,
|
| - MachineType::PointerRepresentation());
|
| - },
|
| - MachineType::PointerRepresentation()));
|
| -
|
| - Goto(&select_loop);
|
| - Bind(&select_loop);
|
| - static int32_t kElementsKind[] = {
|
| - FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS,
|
| - FAST_HOLEY_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS,
|
| - };
|
| -
|
| - Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this);
|
| - Label* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects,
|
| - &if_smiorobjects, &if_smiorobjects,
|
| - &if_packed_doubles, &if_holey_doubles};
|
| -
|
| - Node* map = LoadMap(array);
|
| - Node* elements_kind = LoadMapElementsKind(map);
|
| - Node* elements = LoadElements(array);
|
| - Switch(elements_kind, &return_false, kElementsKind, element_kind_handlers,
|
| - arraysize(kElementsKind));
|
| -
|
| - Bind(&if_smiorobjects);
|
| - {
|
| - Variable search_num(this, MachineRepresentation::kFloat64);
|
| - Label ident_loop(this, &index_var), heap_num_loop(this, &search_num),
|
| - string_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(search_element));
|
| - Goto(&heap_num_loop);
|
| -
|
| - Bind(¬_smi);
|
| - GotoIf(WordEqual(search_element, UndefinedConstant()), &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);
|
| - Goto(&ident_loop);
|
| -
|
| - Bind(&ident_loop);
|
| - {
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false);
|
| - Node* element_k = LoadFixedArrayElement(elements, index_var.value());
|
| - GotoIf(WordEqual(element_k, search_element), &return_true);
|
| -
|
| - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1)));
|
| - Goto(&ident_loop);
|
| - }
|
| -
|
| - Bind(&undef_loop);
|
| - {
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false);
|
| - Node* element_k = LoadFixedArrayElement(elements, index_var.value());
|
| - GotoIf(WordEqual(element_k, UndefinedConstant()), &return_true);
|
| - GotoIf(WordEqual(element_k, TheHoleConstant()), &return_true);
|
| -
|
| - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1)));
|
| - Goto(&undef_loop);
|
| - }
|
| -
|
| - Bind(&heap_num_loop);
|
| - {
|
| - Label nan_loop(this, &index_var), not_nan_loop(this, &index_var);
|
| - BranchIfFloat64IsNaN(search_num.value(), &nan_loop, ¬_nan_loop);
|
| -
|
| - Bind(¬_nan_loop);
|
| - {
|
| - Label continue_loop(this), not_smi(this);
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false);
|
| - Node* element_k = LoadFixedArrayElement(elements, index_var.value());
|
| - GotoIfNot(TaggedIsSmi(element_k), ¬_smi);
|
| - Branch(Float64Equal(search_num.value(), SmiToFloat64(element_k)),
|
| - &return_true, &continue_loop);
|
| -
|
| - Bind(¬_smi);
|
| - GotoIfNot(IsHeapNumber(element_k), &continue_loop);
|
| - Branch(Float64Equal(search_num.value(), LoadHeapNumberValue(element_k)),
|
| - &return_true, &continue_loop);
|
| -
|
| - Bind(&continue_loop);
|
| - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1)));
|
| - Goto(¬_nan_loop);
|
| - }
|
| -
|
| - Bind(&nan_loop);
|
| - {
|
| - Label continue_loop(this);
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false);
|
| - 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_true,
|
| - &continue_loop);
|
| -
|
| - Bind(&continue_loop);
|
| - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1)));
|
| - Goto(&nan_loop);
|
| - }
|
| - }
|
| -
|
| - Bind(&string_loop);
|
| - {
|
| - Label continue_loop(this);
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false);
|
| - Node* element_k = LoadFixedArrayElement(elements, index_var.value());
|
| - GotoIf(TaggedIsSmi(element_k), &continue_loop);
|
| - GotoIfNot(IsStringInstanceType(LoadInstanceType(element_k)),
|
| - &continue_loop);
|
| -
|
| - // TODO(bmeurer): Consider inlining the StringEqual logic here.
|
| - Node* result = CallStub(CodeFactory::StringEqual(isolate()), context,
|
| - search_element, element_k);
|
| - Branch(WordEqual(BooleanConstant(true), result), &return_true,
|
| - &continue_loop);
|
| -
|
| - Bind(&continue_loop);
|
| - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1)));
|
| - Goto(&string_loop);
|
| - }
|
| - }
|
| -
|
| - Bind(&if_packed_doubles);
|
| - {
|
| - Label nan_loop(this, &index_var), not_nan_loop(this, &index_var),
|
| - hole_loop(this, &index_var), search_notnan(this);
|
| - Variable search_num(this, MachineRepresentation::kFloat64);
|
| -
|
| - GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
|
| - search_num.Bind(SmiToFloat64(search_element));
|
| - Goto(¬_nan_loop);
|
| -
|
| - Bind(&search_notnan);
|
| - GotoIfNot(IsHeapNumber(search_element), &return_false);
|
| -
|
| - search_num.Bind(LoadHeapNumberValue(search_element));
|
| -
|
| - BranchIfFloat64IsNaN(search_num.value(), &nan_loop, ¬_nan_loop);
|
| -
|
| - // Search for HeapNumber
|
| - Bind(¬_nan_loop);
|
| - {
|
| - Label continue_loop(this);
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false);
|
| - Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(),
|
| - MachineType::Float64());
|
| - Branch(Float64Equal(element_k, search_num.value()), &return_true,
|
| - &continue_loop);
|
| - Bind(&continue_loop);
|
| - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1)));
|
| - Goto(¬_nan_loop);
|
| - }
|
| -
|
| - // Search for NaN
|
| - Bind(&nan_loop);
|
| - {
|
| - Label continue_loop(this);
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false);
|
| - Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(),
|
| - MachineType::Float64());
|
| - BranchIfFloat64IsNaN(element_k, &return_true, &continue_loop);
|
| - Bind(&continue_loop);
|
| - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1)));
|
| - Goto(&nan_loop);
|
| - }
|
| - }
|
| -
|
| - Bind(&if_holey_doubles);
|
| - {
|
| - Label nan_loop(this, &index_var), not_nan_loop(this, &index_var),
|
| - hole_loop(this, &index_var), search_notnan(this);
|
| - Variable search_num(this, MachineRepresentation::kFloat64);
|
| -
|
| - GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
|
| - search_num.Bind(SmiToFloat64(search_element));
|
| - Goto(¬_nan_loop);
|
| -
|
| - Bind(&search_notnan);
|
| - GotoIf(WordEqual(search_element, UndefinedConstant()), &hole_loop);
|
| - GotoIfNot(IsHeapNumber(search_element), &return_false);
|
| -
|
| - search_num.Bind(LoadHeapNumberValue(search_element));
|
| -
|
| - BranchIfFloat64IsNaN(search_num.value(), &nan_loop, ¬_nan_loop);
|
| -
|
| - // Search for HeapNumber
|
| - Bind(¬_nan_loop);
|
| - {
|
| - Label continue_loop(this);
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false);
|
| -
|
| - // Load double value or continue if it contains a double hole.
|
| - Node* element_k = LoadFixedDoubleArrayElement(
|
| - elements, index_var.value(), MachineType::Float64(), 0,
|
| - INTPTR_PARAMETERS, &continue_loop);
|
| -
|
| - Branch(Float64Equal(element_k, search_num.value()), &return_true,
|
| - &continue_loop);
|
| - Bind(&continue_loop);
|
| - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1)));
|
| - Goto(¬_nan_loop);
|
| - }
|
| -
|
| - // Search for NaN
|
| - Bind(&nan_loop);
|
| - {
|
| - Label continue_loop(this);
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false);
|
| -
|
| - // Load double value or continue if it contains a double hole.
|
| - Node* element_k = LoadFixedDoubleArrayElement(
|
| - elements, index_var.value(), MachineType::Float64(), 0,
|
| - INTPTR_PARAMETERS, &continue_loop);
|
| -
|
| - BranchIfFloat64IsNaN(element_k, &return_true, &continue_loop);
|
| - Bind(&continue_loop);
|
| - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1)));
|
| - Goto(&nan_loop);
|
| - }
|
| -
|
| - // Search for the Hole
|
| - Bind(&hole_loop);
|
| - {
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len), &return_false);
|
| -
|
| - // Check if the element is a double hole, but don't load it.
|
| - LoadFixedDoubleArrayElement(elements, index_var.value(),
|
| - MachineType::None(), 0, INTPTR_PARAMETERS,
|
| - &return_true);
|
| -
|
| - index_var.Bind(IntPtrAdd(index_var.value(), IntPtrConstant(1)));
|
| - Goto(&hole_loop);
|
| - }
|
| - }
|
| -
|
| - Bind(&return_true);
|
| - Return(TrueConstant());
|
| -
|
| - Bind(&return_false);
|
| - Return(FalseConstant());
|
| -
|
| - Bind(&call_runtime);
|
| - Return(CallRuntime(Runtime::kArrayIncludes_Slow, context, array,
|
| - search_element, start_from));
|
| -}
|
| -
|
| -TF_BUILTIN(ArrayIndexOf, CodeStubAssembler) {
|
| - Node* array = Parameter(0);
|
| - Node* search_element = Parameter(1);
|
| - Node* start_from = Parameter(2);
|
| - Node* context = Parameter(3 + 2);
|
| -
|
| - Node* intptr_zero = IntPtrConstant(0);
|
| - Node* intptr_one = IntPtrConstant(1);
|
| -
|
| - Variable len_var(this, MachineType::PointerRepresentation()),
|
| - index_var(this, MachineType::PointerRepresentation()),
|
| - start_from_var(this, MachineType::PointerRepresentation());
|
| -
|
| - Label init_k(this), return_found(this), return_not_found(this),
|
| - call_runtime(this);
|
| -
|
| - Label init_len(this);
|
| -
|
| - index_var.Bind(intptr_zero);
|
| - len_var.Bind(intptr_zero);
|
| -
|
| - // Take slow path if not a JSArray, if retrieving elements requires
|
| - // traversing prototype, or if access checks are required.
|
| - BranchIfFastJSArray(array, context, FastJSArrayAccessMode::INBOUNDS_READ,
|
| - &init_len, &call_runtime);
|
| -
|
| - Bind(&init_len);
|
| - {
|
| - // JSArray length is always an Smi for fast arrays.
|
| - CSA_ASSERT(this,
|
| - TaggedIsSmi(LoadObjectField(array, JSArray::kLengthOffset)));
|
| - Node* len = LoadAndUntagObjectField(array, JSArray::kLengthOffset);
|
| -
|
| - len_var.Bind(len);
|
| - Branch(WordEqual(len_var.value(), intptr_zero), &return_not_found, &init_k);
|
| - }
|
| -
|
| - Bind(&init_k);
|
| - {
|
| - // For now only deal with undefined and Smis here; we must be really careful
|
| - // with side-effects from the ToInteger conversion as the side-effects might
|
| - // render our assumptions about the receiver being a fast JSArray and the
|
| - // length invalid.
|
| - Label done(this), init_k_smi(this), init_k_other(this), init_k_zero(this),
|
| - init_k_n(this);
|
| - Branch(TaggedIsSmi(start_from), &init_k_smi, &init_k_other);
|
| -
|
| - Bind(&init_k_smi);
|
| - {
|
| - // The fromIndex is a Smi.
|
| - start_from_var.Bind(SmiUntag(start_from));
|
| - Goto(&init_k_n);
|
| - }
|
| -
|
| - Bind(&init_k_other);
|
| - {
|
| - // The fromIndex must be undefined then, otherwise bailout and let the
|
| - // runtime deal with the full ToInteger conversion.
|
| - GotoIfNot(IsUndefined(start_from), &call_runtime);
|
| - start_from_var.Bind(intptr_zero);
|
| - Goto(&init_k_n);
|
| - }
|
| -
|
| - Bind(&init_k_n);
|
| - {
|
| - Label if_positive(this), if_negative(this), done(this);
|
| - Branch(IntPtrLessThan(start_from_var.value(), intptr_zero), &if_negative,
|
| - &if_positive);
|
| -
|
| - Bind(&if_positive);
|
| - {
|
| - index_var.Bind(start_from_var.value());
|
| - Goto(&done);
|
| - }
|
| -
|
| - Bind(&if_negative);
|
| - {
|
| - index_var.Bind(IntPtrAdd(len_var.value(), start_from_var.value()));
|
| - Branch(IntPtrLessThan(index_var.value(), intptr_zero), &init_k_zero,
|
| - &done);
|
| - }
|
| -
|
| - Bind(&init_k_zero);
|
| - {
|
| - index_var.Bind(intptr_zero);
|
| - Goto(&done);
|
| - }
|
| -
|
| - Bind(&done);
|
| - }
|
| - }
|
| -
|
| - static int32_t kElementsKind[] = {
|
| - FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS,
|
| - FAST_HOLEY_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS,
|
| - };
|
| -
|
| - Label if_smiorobjects(this), if_packed_doubles(this), if_holey_doubles(this);
|
| - Label* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects,
|
| - &if_smiorobjects, &if_smiorobjects,
|
| - &if_packed_doubles, &if_holey_doubles};
|
| -
|
| - Node* map = LoadMap(array);
|
| - Node* elements_kind = LoadMapElementsKind(map);
|
| - Node* elements = LoadElements(array);
|
| - Switch(elements_kind, &return_not_found, kElementsKind, element_kind_handlers,
|
| - arraysize(kElementsKind));
|
| -
|
| - Bind(&if_smiorobjects);
|
| - {
|
| - Variable search_num(this, MachineRepresentation::kFloat64);
|
| - Label ident_loop(this, &index_var), heap_num_loop(this, &search_num),
|
| - string_loop(this, &index_var), not_smi(this), not_heap_num(this);
|
| -
|
| - GotoIfNot(TaggedIsSmi(search_element), ¬_smi);
|
| - search_num.Bind(SmiToFloat64(search_element));
|
| - Goto(&heap_num_loop);
|
| -
|
| - Bind(¬_smi);
|
| - 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);
|
| - Goto(&ident_loop);
|
| -
|
| - Bind(&ident_loop);
|
| - {
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()),
|
| - &return_not_found);
|
| - Node* element_k = LoadFixedArrayElement(elements, index_var.value());
|
| - GotoIf(WordEqual(element_k, search_element), &return_found);
|
| -
|
| - index_var.Bind(IntPtrAdd(index_var.value(), intptr_one));
|
| - Goto(&ident_loop);
|
| - }
|
| -
|
| - Bind(&heap_num_loop);
|
| - {
|
| - Label not_nan_loop(this, &index_var);
|
| - BranchIfFloat64IsNaN(search_num.value(), &return_not_found,
|
| - ¬_nan_loop);
|
| -
|
| - Bind(¬_nan_loop);
|
| - {
|
| - Label continue_loop(this), not_smi(this);
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()),
|
| - &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);
|
| - index_var.Bind(IntPtrAdd(index_var.value(), intptr_one));
|
| - Goto(¬_nan_loop);
|
| - }
|
| - }
|
| -
|
| - Bind(&string_loop);
|
| - {
|
| - Label continue_loop(this);
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()),
|
| - &return_not_found);
|
| - Node* element_k = LoadFixedArrayElement(elements, index_var.value());
|
| - GotoIf(TaggedIsSmi(element_k), &continue_loop);
|
| - GotoIfNot(IsString(element_k), &continue_loop);
|
| -
|
| - // TODO(bmeurer): Consider inlining the StringEqual logic here.
|
| - Callable callable = CodeFactory::StringEqual(isolate());
|
| - Node* result = CallStub(callable, context, search_element, element_k);
|
| - Branch(WordEqual(BooleanConstant(true), result), &return_found,
|
| - &continue_loop);
|
| -
|
| - Bind(&continue_loop);
|
| - index_var.Bind(IntPtrAdd(index_var.value(), intptr_one));
|
| - Goto(&string_loop);
|
| - }
|
| - }
|
| -
|
| - Bind(&if_packed_doubles);
|
| - {
|
| - Label not_nan_loop(this, &index_var), search_notnan(this);
|
| - Variable search_num(this, MachineRepresentation::kFloat64);
|
| -
|
| - GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
|
| - search_num.Bind(SmiToFloat64(search_element));
|
| - Goto(¬_nan_loop);
|
| -
|
| - Bind(&search_notnan);
|
| - GotoIfNot(IsHeapNumber(search_element), &return_not_found);
|
| -
|
| - search_num.Bind(LoadHeapNumberValue(search_element));
|
| -
|
| - BranchIfFloat64IsNaN(search_num.value(), &return_not_found, ¬_nan_loop);
|
| -
|
| - // Search for HeapNumber
|
| - Bind(¬_nan_loop);
|
| - {
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()),
|
| - &return_not_found);
|
| - Node* element_k = LoadFixedDoubleArrayElement(elements, index_var.value(),
|
| - MachineType::Float64());
|
| - GotoIf(Float64Equal(element_k, search_num.value()), &return_found);
|
| -
|
| - index_var.Bind(IntPtrAdd(index_var.value(), intptr_one));
|
| - Goto(¬_nan_loop);
|
| - }
|
| - }
|
| -
|
| - Bind(&if_holey_doubles);
|
| - {
|
| - Label not_nan_loop(this, &index_var), search_notnan(this);
|
| - Variable search_num(this, MachineRepresentation::kFloat64);
|
| -
|
| - GotoIfNot(TaggedIsSmi(search_element), &search_notnan);
|
| - search_num.Bind(SmiToFloat64(search_element));
|
| - Goto(¬_nan_loop);
|
| -
|
| - Bind(&search_notnan);
|
| - GotoIfNot(IsHeapNumber(search_element), &return_not_found);
|
| -
|
| - search_num.Bind(LoadHeapNumberValue(search_element));
|
| -
|
| - BranchIfFloat64IsNaN(search_num.value(), &return_not_found, ¬_nan_loop);
|
| -
|
| - // Search for HeapNumber
|
| - Bind(¬_nan_loop);
|
| - {
|
| - Label continue_loop(this);
|
| - GotoIfNot(UintPtrLessThan(index_var.value(), len_var.value()),
|
| - &return_not_found);
|
| -
|
| - // Load double value or continue if it contains a double hole.
|
| - Node* element_k = LoadFixedDoubleArrayElement(
|
| - elements, index_var.value(), MachineType::Float64(), 0,
|
| - INTPTR_PARAMETERS, &continue_loop);
|
| -
|
| - Branch(Float64Equal(element_k, search_num.value()), &return_found,
|
| - &continue_loop);
|
| - Bind(&continue_loop);
|
| - index_var.Bind(IntPtrAdd(index_var.value(), intptr_one));
|
| - Goto(¬_nan_loop);
|
| - }
|
| - }
|
| -
|
| - Bind(&return_found);
|
| - Return(SmiTag(index_var.value()));
|
| -
|
| - Bind(&return_not_found);
|
| - Return(NumberConstant(-1));
|
| -
|
| - Bind(&call_runtime);
|
| - Return(CallRuntime(Runtime::kArrayIndexOf, context, array, search_element,
|
| - start_from));
|
| -}
|
| -
|
| -class ArrayPrototypeIterationAssembler : public CodeStubAssembler {
|
| - public:
|
| - explicit ArrayPrototypeIterationAssembler(compiler::CodeAssemblerState* state)
|
| - : CodeStubAssembler(state) {}
|
| -
|
| - protected:
|
| - void Generate_ArrayPrototypeIterationMethod(IterationKind iteration_kind) {
|
| - Node* receiver = Parameter(0);
|
| - Node* context = Parameter(3);
|
| -
|
| - Variable var_array(this, MachineRepresentation::kTagged);
|
| - Variable var_map(this, MachineRepresentation::kTagged);
|
| - Variable var_type(this, MachineRepresentation::kWord32);
|
| -
|
| - Label if_isnotobject(this, Label::kDeferred);
|
| - Label create_array_iterator(this);
|
| -
|
| - GotoIf(TaggedIsSmi(receiver), &if_isnotobject);
|
| - var_array.Bind(receiver);
|
| - var_map.Bind(LoadMap(receiver));
|
| - var_type.Bind(LoadMapInstanceType(var_map.value()));
|
| - Branch(IsJSReceiverInstanceType(var_type.value()), &create_array_iterator,
|
| - &if_isnotobject);
|
| -
|
| - Bind(&if_isnotobject);
|
| - {
|
| - Callable callable = CodeFactory::ToObject(isolate());
|
| - Node* result = CallStub(callable, context, receiver);
|
| - var_array.Bind(result);
|
| - var_map.Bind(LoadMap(result));
|
| - var_type.Bind(LoadMapInstanceType(var_map.value()));
|
| - Goto(&create_array_iterator);
|
| - }
|
| -
|
| - Bind(&create_array_iterator);
|
| - Return(CreateArrayIterator(var_array.value(), var_map.value(),
|
| - var_type.value(), context, iteration_kind));
|
| - }
|
| -};
|
| -
|
| -TF_BUILTIN(ArrayPrototypeValues, ArrayPrototypeIterationAssembler) {
|
| - Generate_ArrayPrototypeIterationMethod(IterationKind::kValues);
|
| -}
|
| -
|
| -TF_BUILTIN(ArrayPrototypeEntries, ArrayPrototypeIterationAssembler) {
|
| - Generate_ArrayPrototypeIterationMethod(IterationKind::kEntries);
|
| -}
|
| -
|
| -TF_BUILTIN(ArrayPrototypeKeys, ArrayPrototypeIterationAssembler) {
|
| - Generate_ArrayPrototypeIterationMethod(IterationKind::kKeys);
|
| -}
|
| -
|
| -TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) {
|
| - Handle<String> operation = factory()->NewStringFromAsciiChecked(
|
| - "Array Iterator.prototype.next", TENURED);
|
| -
|
| - Node* iterator = Parameter(0);
|
| - Node* context = Parameter(3);
|
| -
|
| - Variable var_value(this, MachineRepresentation::kTagged);
|
| - Variable var_done(this, MachineRepresentation::kTagged);
|
| -
|
| - // Required, or else `throw_bad_receiver` fails a DCHECK due to these
|
| - // variables not being bound along all paths, despite not being used.
|
| - var_done.Bind(TrueConstant());
|
| - var_value.Bind(UndefinedConstant());
|
| -
|
| - 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);
|
| - Node* instance_type = LoadInstanceType(iterator);
|
| - GotoIf(
|
| - Uint32LessThan(
|
| - Int32Constant(LAST_ARRAY_ITERATOR_TYPE - FIRST_ARRAY_ITERATOR_TYPE),
|
| - Int32Sub(instance_type, Int32Constant(FIRST_ARRAY_ITERATOR_TYPE))),
|
| - &throw_bad_receiver);
|
| -
|
| - // Let a be O.[[IteratedObject]].
|
| - Node* array =
|
| - LoadObjectField(iterator, JSArrayIterator::kIteratedObjectOffset);
|
| -
|
| - // 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);
|
| -
|
| - Branch(WordEqual(orig_map, array_map), &if_isfastarray, &if_isnotfastarray);
|
| -
|
| - Bind(&if_isfastarray);
|
| - {
|
| - CSA_ASSERT(this, Word32Equal(LoadMapInstanceType(array_map),
|
| - Int32Constant(JS_ARRAY_TYPE)));
|
| -
|
| - Node* length = LoadObjectField(array, JSArray::kLengthOffset);
|
| -
|
| - CSA_ASSERT(this, TaggedIsSmi(length));
|
| - CSA_ASSERT(this, TaggedIsSmi(index));
|
| -
|
| - GotoIfNot(SmiBelow(index, length), &set_done);
|
| -
|
| - Node* one = SmiConstant(Smi::FromInt(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));
|
| -
|
| - Bind(&packed_object_values);
|
| - {
|
| - var_value.Bind(LoadFixedArrayElement(elements, index, 0, SMI_PARAMETERS));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| -
|
| - Bind(&packed_double_values);
|
| - {
|
| - Node* value = LoadFixedDoubleArrayElement(
|
| - elements, index, MachineType::Float64(), 0, SMI_PARAMETERS);
|
| - var_value.Bind(AllocateHeapNumberWithValue(value));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| -
|
| - Bind(&holey_object_values);
|
| - {
|
| - // Check the array_protector cell, and take the slow path if it's invalid.
|
| - Node* invalid = SmiConstant(Smi::FromInt(Isolate::kProtectorInvalid));
|
| - Node* cell = LoadRoot(Heap::kArrayProtectorRootIndex);
|
| - Node* cell_value = LoadObjectField(cell, PropertyCell::kValueOffset);
|
| - GotoIf(WordEqual(cell_value, invalid), &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);
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| -
|
| - Bind(&holey_double_values);
|
| - {
|
| - // Check the array_protector cell, and take the slow path if it's invalid.
|
| - Node* invalid = SmiConstant(Smi::FromInt(Isolate::kProtectorInvalid));
|
| - Node* cell = LoadRoot(Heap::kArrayProtectorRootIndex);
|
| - Node* cell_value = LoadObjectField(cell, PropertyCell::kValueOffset);
|
| - GotoIf(WordEqual(cell_value, invalid), &generic_values);
|
| -
|
| - var_value.Bind(UndefinedConstant());
|
| - Node* value = LoadFixedDoubleArrayElement(
|
| - elements, index, MachineType::Float64(), 0, SMI_PARAMETERS,
|
| - &allocate_entry_if_needed);
|
| - var_value.Bind(AllocateHeapNumberWithValue(value));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| - }
|
| -
|
| - Bind(&if_isnotfastarray);
|
| - {
|
| - Label if_istypedarray(this), if_isgeneric(this);
|
| -
|
| - // If a is undefined, return CreateIterResultObject(undefined, true)
|
| - GotoIf(WordEqual(array, UndefinedConstant()), &allocate_iterator_result);
|
| -
|
| - Node* array_type = LoadInstanceType(array);
|
| - Branch(Word32Equal(array_type, Int32Constant(JS_TYPED_ARRAY_TYPE)),
|
| - &if_istypedarray, &if_isgeneric);
|
| -
|
| - Bind(&if_isgeneric);
|
| - {
|
| - Label if_wasfastarray(this);
|
| -
|
| - Node* length = nullptr;
|
| - {
|
| - Variable var_length(this, MachineRepresentation::kTagged);
|
| - Label if_isarray(this), if_isnotarray(this), done(this);
|
| - Branch(Word32Equal(array_type, Int32Constant(JS_ARRAY_TYPE)),
|
| - &if_isarray, &if_isnotarray);
|
| -
|
| - Bind(&if_isarray);
|
| - {
|
| - var_length.Bind(LoadObjectField(array, JSArray::kLengthOffset));
|
| -
|
| - // Invalidate protector cell if needed
|
| - Branch(WordNotEqual(orig_map, UndefinedConstant()), &if_wasfastarray,
|
| - &done);
|
| -
|
| - Bind(&if_wasfastarray);
|
| - {
|
| - Label if_invalid(this, Label::kDeferred);
|
| - // A fast array iterator transitioned to a slow iterator during
|
| - // iteration. Invalidate fast_array_iteration_prtoector 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(Smi::FromInt(Isolate::kProtectorInvalid));
|
| - Node* cell = LoadRoot(Heap::kFastArrayIterationProtectorRootIndex);
|
| - StoreObjectFieldNoWriteBarrier(cell, Cell::kValueOffset, invalid);
|
| - Goto(&done);
|
| - }
|
| - }
|
| -
|
| - Bind(&if_isnotarray);
|
| - {
|
| - Node* length =
|
| - GetProperty(context, array, factory()->length_string());
|
| - Callable to_length = CodeFactory::ToLength(isolate());
|
| - var_length.Bind(CallStub(to_length, context, length));
|
| - Goto(&done);
|
| - }
|
| -
|
| - Bind(&done);
|
| - length = var_length.value();
|
| - }
|
| -
|
| - GotoUnlessNumberLessThan(index, length, &set_done);
|
| -
|
| - StoreObjectField(iterator, JSArrayIterator::kNextIndexOffset,
|
| - NumberInc(index));
|
| - var_done.Bind(FalseConstant());
|
| -
|
| - Branch(
|
| - Uint32LessThanOrEqual(
|
| - instance_type, Int32Constant(JS_GENERIC_ARRAY_KEY_ITERATOR_TYPE)),
|
| - &allocate_key_result, &generic_values);
|
| -
|
| - Bind(&generic_values);
|
| - {
|
| - var_value.Bind(GetProperty(context, array, index));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| - }
|
| -
|
| - Bind(&if_istypedarray);
|
| - {
|
| - Node* buffer = LoadObjectField(array, JSTypedArray::kBufferOffset);
|
| - GotoIf(IsDetachedBuffer(buffer), &if_isdetached);
|
| -
|
| - Node* length = LoadObjectField(array, JSTypedArray::kLengthOffset);
|
| -
|
| - CSA_ASSERT(this, TaggedIsSmi(length));
|
| - CSA_ASSERT(this, TaggedIsSmi(index));
|
| -
|
| - GotoIfNot(SmiBelow(index, length), &set_done);
|
| -
|
| - Node* one = SmiConstant(1);
|
| - StoreObjectFieldNoWriteBarrier(
|
| - iterator, JSArrayIterator::kNextIndexOffset, SmiAdd(index, one));
|
| - var_done.Bind(FalseConstant());
|
| -
|
| - 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);
|
| -
|
| - 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_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,
|
| - };
|
| -
|
| - 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);
|
| - Label* kInstanceTypeHandlers[] = {
|
| - &allocate_key_result, &uint8_values, &uint8_values,
|
| - &int8_values, &uint16_values, &int16_values,
|
| - &uint32_values, &int32_values, &float32_values,
|
| - &float64_values, &uint8_values, &uint8_values,
|
| - &int8_values, &uint16_values, &int16_values,
|
| - &uint32_values, &int32_values, &float32_values,
|
| - &float64_values,
|
| - };
|
| -
|
| - var_done.Bind(FalseConstant());
|
| - Switch(instance_type, &throw_bad_receiver, kInstanceType,
|
| - kInstanceTypeHandlers, arraysize(kInstanceType));
|
| -
|
| - Bind(&uint8_values);
|
| - {
|
| - Node* value_uint8 = LoadFixedTypedArrayElement(
|
| - data_ptr, index, UINT8_ELEMENTS, SMI_PARAMETERS);
|
| - var_value.Bind(SmiFromWord32(value_uint8));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| - Bind(&int8_values);
|
| - {
|
| - Node* value_int8 = LoadFixedTypedArrayElement(
|
| - data_ptr, index, INT8_ELEMENTS, SMI_PARAMETERS);
|
| - var_value.Bind(SmiFromWord32(value_int8));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| - Bind(&uint16_values);
|
| - {
|
| - Node* value_uint16 = LoadFixedTypedArrayElement(
|
| - data_ptr, index, UINT16_ELEMENTS, SMI_PARAMETERS);
|
| - var_value.Bind(SmiFromWord32(value_uint16));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| - Bind(&int16_values);
|
| - {
|
| - Node* value_int16 = LoadFixedTypedArrayElement(
|
| - data_ptr, index, INT16_ELEMENTS, SMI_PARAMETERS);
|
| - var_value.Bind(SmiFromWord32(value_int16));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| - Bind(&uint32_values);
|
| - {
|
| - Node* value_uint32 = LoadFixedTypedArrayElement(
|
| - data_ptr, index, UINT32_ELEMENTS, SMI_PARAMETERS);
|
| - var_value.Bind(ChangeUint32ToTagged(value_uint32));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| - Bind(&int32_values);
|
| - {
|
| - Node* value_int32 = LoadFixedTypedArrayElement(
|
| - data_ptr, index, INT32_ELEMENTS, SMI_PARAMETERS);
|
| - var_value.Bind(ChangeInt32ToTagged(value_int32));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| - Bind(&float32_values);
|
| - {
|
| - Node* value_float32 = LoadFixedTypedArrayElement(
|
| - data_ptr, index, FLOAT32_ELEMENTS, SMI_PARAMETERS);
|
| - var_value.Bind(
|
| - AllocateHeapNumberWithValue(ChangeFloat32ToFloat64(value_float32)));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| - Bind(&float64_values);
|
| - {
|
| - Node* value_float64 = LoadFixedTypedArrayElement(
|
| - data_ptr, index, FLOAT64_ELEMENTS, SMI_PARAMETERS);
|
| - var_value.Bind(AllocateHeapNumberWithValue(value_float64));
|
| - Goto(&allocate_entry_if_needed);
|
| - }
|
| - }
|
| - }
|
| -
|
| - Bind(&set_done);
|
| - {
|
| - StoreObjectFieldNoWriteBarrier(
|
| - iterator, JSArrayIterator::kIteratedObjectOffset, UndefinedConstant());
|
| - 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(Int32GreaterThan(instance_type,
|
| - Int32Constant(LAST_ARRAY_KEY_VALUE_ITERATOR_TYPE)),
|
| - &allocate_iterator_result);
|
| -
|
| - Node* elements = AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2));
|
| - StoreFixedArrayElement(elements, 0, index, SKIP_WRITE_BARRIER);
|
| - StoreFixedArrayElement(elements, 1, var_value.value(), SKIP_WRITE_BARRIER);
|
| -
|
| - Node* entry = Allocate(JSArray::kSize);
|
| - Node* map = LoadContextElement(LoadNativeContext(context),
|
| - Context::JS_ARRAY_FAST_ELEMENTS_MAP_INDEX);
|
| -
|
| - StoreMapNoWriteBarrier(entry, map);
|
| - StoreObjectFieldRoot(entry, JSArray::kPropertiesOffset,
|
| - Heap::kEmptyFixedArrayRootIndex);
|
| - StoreObjectFieldNoWriteBarrier(entry, JSArray::kElementsOffset, elements);
|
| - StoreObjectFieldNoWriteBarrier(entry, JSArray::kLengthOffset,
|
| - SmiConstant(Smi::FromInt(2)));
|
| -
|
| - var_value.Bind(entry);
|
| - Goto(&allocate_iterator_result);
|
| - }
|
| -
|
| - Bind(&allocate_iterator_result);
|
| - {
|
| - Node* result = Allocate(JSIteratorResult::kSize);
|
| - Node* map = LoadContextElement(LoadNativeContext(context),
|
| - Context::ITERATOR_RESULT_MAP_INDEX);
|
| - StoreMapNoWriteBarrier(result, map);
|
| - StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset,
|
| - Heap::kEmptyFixedArrayRootIndex);
|
| - StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset,
|
| - Heap::kEmptyFixedArrayRootIndex);
|
| - StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset,
|
| - var_value.value());
|
| - StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset,
|
| - var_done.value());
|
| - Return(result);
|
| - }
|
| -
|
| - Bind(&throw_bad_receiver);
|
| - {
|
| - // The {receiver} is not a valid JSArrayIterator.
|
| - CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
|
| - HeapConstant(operation), iterator);
|
| - Unreachable();
|
| - }
|
| -
|
| - Bind(&if_isdetached);
|
| - {
|
| - Node* message = SmiConstant(MessageTemplate::kDetachedOperation);
|
| - CallRuntime(Runtime::kThrowTypeError, context, message,
|
| - HeapConstant(operation));
|
| - Unreachable();
|
| - }
|
| -}
|
| -
|
| } // namespace internal
|
| } // namespace v8
|
|
|