| Index: src/builtins/builtins-array.cc
|
| diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
|
| index 0fdab4a8dfece3c39caf5a02e73077449b43362b..a82a1a98a646675ba0cc242633607cbee81d063f 100644
|
| --- a/src/builtins/builtins-array.cc
|
| +++ b/src/builtins/builtins-array.cc
|
| @@ -426,6 +426,242 @@ BUILTIN(ArrayUnshift) {
|
| return Smi::FromInt(new_length);
|
| }
|
|
|
| +class ForEachCodeStubAssembler : public CodeStubAssembler {
|
| + public:
|
| + explicit ForEachCodeStubAssembler(compiler::CodeAssemblerState* state)
|
| + : CodeStubAssembler(state) {}
|
| +
|
| + void VisitOneElement(Node* context, Node* this_arg, Node* o, Node* k,
|
| + Node* callbackfn) {
|
| + Comment("begin VisitOneElement");
|
| +
|
| + // a. Let Pk be ToString(k).
|
| + Node* p_k = ToString(context, k);
|
| +
|
| + // 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
|
| + Label not_present(this);
|
| + GotoIf(WordNotEqual(k_present, TrueConstant()), ¬_present);
|
| +
|
| + // i. Let kValue be Get(O, Pk).
|
| + // ii. ReturnIfAbrupt(kValue).
|
| + Node* k_value =
|
| + CallStub(CodeFactory::GetProperty(isolate()), context, o, k);
|
| +
|
| + // iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»).
|
| + // iv. ReturnIfAbrupt(funcResult).
|
| + CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg, k_value,
|
| + k, o);
|
| +
|
| + Goto(¬_present);
|
| + Bind(¬_present);
|
| + Comment("end VisitOneElement");
|
| + }
|
| +
|
| + void VisitAllFastElements(Node* context, ElementsKind kind, Node* this_arg,
|
| + Node* o, Node* len, Node* callbackfn,
|
| + ParameterMode mode) {
|
| + Comment("begin VisitAllFastElements");
|
| + Variable original_map(this, MachineRepresentation::kTagged);
|
| + original_map.Bind(LoadMap(o));
|
| + VariableList list({&original_map}, zone());
|
| + BuildFastLoop(
|
| + list, IntPtrOrSmiConstant(0, mode), TaggedToParameter(len, mode),
|
| + [context, kind, this, o, &original_map, callbackfn, this_arg,
|
| + mode](Node* index) {
|
| + Label one_element_done(this), array_changed(this, Label::kDeferred),
|
| + 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);
|
| + }
|
| + CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg,
|
| + value, tagged_index, o);
|
| + Goto(&one_element_done);
|
| +
|
| + Bind(&hole_element);
|
| + BranchIfPrototypesHaveNoElements(o_map, &one_element_done,
|
| + &array_changed);
|
| +
|
| + // O's changed during the forEach. Use the implementation precisely
|
| + // specified in the spec for the rest of the iteration, also making
|
| + // the failed original_map sticky in case of a subseuent change that
|
| + // goes back to the original map.
|
| + Bind(&array_changed);
|
| + VisitOneElement(context, this_arg, o, ParameterToTagged(index, mode),
|
| + callbackfn);
|
| + original_map.Bind(UndefinedConstant());
|
| + Goto(&one_element_done);
|
| +
|
| + Bind(&one_element_done);
|
| + },
|
| + 1, mode, IndexAdvanceMode::kPost);
|
| + Comment("end VisitAllFastElements");
|
| + }
|
| +};
|
| +
|
| +TF_BUILTIN(ArrayForEach, ForEachCodeStubAssembler) {
|
| + Label non_array(this), examine_elements(this), fast_elements(this),
|
| + slow(this), maybe_double_elements(this), fast_double_elements(this);
|
| +
|
| + Node* receiver = Parameter(ForEachDescriptor::kReceiver);
|
| + Node* callbackfn = Parameter(ForEachDescriptor::kCallback);
|
| + Node* this_arg = Parameter(ForEachDescriptor::kThisArg);
|
| + Node* context = Parameter(ForEachDescriptor::kContext);
|
| +
|
| + // 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 =
|
| + CallStub(CodeFactory::GetProperty(isolate()), context, o,
|
| + HeapConstant(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);
|
| + GotoIf(TaggedIsSmi(callbackfn), &type_exception);
|
| + GotoUnless(IsCallableMap(LoadMap(callbackfn)), &type_exception);
|
| +
|
| + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
| + // [Already done by the arguments adapter]
|
| +
|
| + // Non-smi lengths must use the slow path.
|
| + GotoIf(TaggedIsNotSmi(len), &slow);
|
| +
|
| + BranchIfFastJSArray(o, context,
|
| + CodeStubAssembler::FastJSArrayAccessMode::INBOUNDS_READ,
|
| + &examine_elements, &slow);
|
| +
|
| + Bind(&examine_elements);
|
| +
|
| + ParameterMode mode = OptimalParameterMode();
|
| +
|
| + // 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);
|
| +
|
| + Bind(&fast_elements);
|
| + {
|
| + VisitAllFastElements(context, FAST_ELEMENTS, this_arg, o, len, callbackfn,
|
| + mode);
|
| +
|
| + // No exception, return success
|
| + Return(UndefinedConstant());
|
| + }
|
| +
|
| + Bind(&maybe_double_elements);
|
| + Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_DOUBLE_ELEMENTS)),
|
| + &slow, &fast_double_elements);
|
| +
|
| + Bind(&fast_double_elements);
|
| + {
|
| + VisitAllFastElements(context, FAST_DOUBLE_ELEMENTS, this_arg, o, len,
|
| + callbackfn, mode);
|
| +
|
| + // No exception, return success
|
| + Return(UndefinedConstant());
|
| + }
|
| +
|
| + Bind(&slow);
|
| + {
|
| + // By the book: taken from the ECMAScript 2015 specification (cont.)
|
| +
|
| + // 7. Let k be 0.
|
| + Variable k(this, MachineRepresentation::kTagged);
|
| + k.Bind(SmiConstant(0));
|
| +
|
| + // 8. Repeat, while k < len
|
| + Label loop(this, &k);
|
| + Label after_loop(this);
|
| + Goto(&loop);
|
| + Bind(&loop);
|
| + {
|
| + GotoUnlessNumberLessThan(k.value(), len, &after_loop);
|
| +
|
| + VisitOneElement(context, this_arg, o, k.value(), callbackfn);
|
| +
|
| + // e. Increase k by 1.
|
| + k.Bind(NumberInc(k.value()));
|
| + Goto(&loop);
|
| + }
|
| + Bind(&after_loop);
|
| + Return(UndefinedConstant());
|
| + }
|
| +
|
| + Bind(&throw_null_undefined_exception);
|
| + {
|
| + CallRuntime(Runtime::kThrowTypeError, context,
|
| + SmiConstant(MessageTemplate::kCalledOnNullOrUndefined),
|
| + HeapConstant(isolate()->factory()->NewStringFromAsciiChecked(
|
| + "Array.prototype.forEach")));
|
| + Return(UndefinedConstant());
|
| + }
|
| +
|
| + Bind(&type_exception);
|
| + {
|
| + CallRuntime(Runtime::kThrowTypeError, context,
|
| + SmiConstant(MessageTemplate::kCalledNonCallable), callbackfn);
|
| + Return(UndefinedConstant());
|
| + }
|
| +}
|
| +
|
| BUILTIN(ArraySlice) {
|
| HandleScope scope(isolate);
|
| Handle<Object> receiver = args.receiver();
|
|
|