[builtins] Implement Array.prototype.reduce in the CSA

BUG=v8:1956

Review-Url: https://codereview.chromium.org/2752273003
Cr-Commit-Position: refs/heads/master@{#43987}
This commit is contained in:
danno 2017-03-21 08:57:38 -07:00 committed by Commit bot
parent 64ffea8509
commit 176a43fb86
9 changed files with 343 additions and 261 deletions

View File

@ -216,6 +216,8 @@ class Genesis BASE_EMBEDDED {
HARMONY_SHIPPING(DECLARE_FEATURE_INITIALIZATION)
#undef DECLARE_FEATURE_INITIALIZATION
void InstallOneBuiltinFunction(const char* object, const char* method,
Builtins::Name name);
void InitializeGlobal_enable_fast_array_builtins();
Handle<JSFunction> InstallArrayBuffer(Handle<JSObject> target,
@ -3664,63 +3666,39 @@ void InstallPublicSymbol(Factory* factory, Handle<Context> native_context,
JSObject::AddProperty(symbol, name_string, value, attributes);
}
void Genesis::InitializeGlobal_enable_fast_array_builtins() {
if (!FLAG_enable_fast_array_builtins) return;
void Genesis::InstallOneBuiltinFunction(const char* object_name,
const char* method_name,
Builtins::Name builtin_name) {
Handle<JSGlobalObject> global(native_context()->global_object());
Isolate* isolate = global->GetIsolate();
Factory* factory = isolate->factory();
LookupIterator it1(global, factory->NewStringFromAsciiChecked("Array"),
LookupIterator it1(global, factory->NewStringFromAsciiChecked(object_name),
LookupIterator::OWN_SKIP_INTERCEPTOR);
Handle<Object> array_object = Object::GetProperty(&it1).ToHandleChecked();
LookupIterator it2(array_object,
factory->NewStringFromAsciiChecked("prototype"),
Handle<Object> object = Object::GetProperty(&it1).ToHandleChecked();
LookupIterator it2(object, factory->NewStringFromAsciiChecked("prototype"),
LookupIterator::OWN_SKIP_INTERCEPTOR);
Handle<Object> array_prototype = Object::GetProperty(&it2).ToHandleChecked();
Handle<Object> prototype = Object::GetProperty(&it2).ToHandleChecked();
LookupIterator it3(array_prototype,
factory->NewStringFromAsciiChecked("forEach"),
LookupIterator it3(prototype, factory->NewStringFromAsciiChecked(method_name),
LookupIterator::OWN_SKIP_INTERCEPTOR);
Handle<Object> for_each_function =
Object::GetProperty(&it3).ToHandleChecked();
Handle<JSFunction>::cast(for_each_function)
->set_code(isolate->builtins()->builtin(Builtins::kArrayForEach));
Handle<JSFunction>::cast(for_each_function)
->shared()
->set_code(isolate->builtins()->builtin(Builtins::kArrayForEach));
Handle<Object> function = Object::GetProperty(&it3).ToHandleChecked();
Handle<JSFunction>::cast(function)->set_code(
isolate->builtins()->builtin(builtin_name));
Handle<JSFunction>::cast(function)->shared()->set_code(
isolate->builtins()->builtin(builtin_name));
}
LookupIterator it4(array_prototype,
factory->NewStringFromAsciiChecked("every"),
LookupIterator::OWN_SKIP_INTERCEPTOR);
Handle<Object> every_function = Object::GetProperty(&it4).ToHandleChecked();
Handle<JSFunction>::cast(every_function)
->set_code(isolate->builtins()->builtin(Builtins::kArrayEvery));
Handle<JSFunction>::cast(every_function)
->shared()
->set_code(isolate->builtins()->builtin(Builtins::kArrayEvery));
void Genesis::InitializeGlobal_enable_fast_array_builtins() {
if (!FLAG_enable_fast_array_builtins) return;
LookupIterator it5(array_prototype,
factory->NewStringFromAsciiChecked("some"),
LookupIterator::OWN_SKIP_INTERCEPTOR);
Handle<Object> some_function = Object::GetProperty(&it5).ToHandleChecked();
Handle<JSFunction>::cast(some_function)
->set_code(isolate->builtins()->builtin(Builtins::kArraySome));
Handle<JSFunction>::cast(some_function)
->shared()
->set_code(isolate->builtins()->builtin(Builtins::kArraySome));
InstallOneBuiltinFunction("Array", "forEach", Builtins::kArrayForEach);
InstallOneBuiltinFunction("Array", "every", Builtins::kArrayEvery);
InstallOneBuiltinFunction("Array", "some", Builtins::kArraySome);
InstallOneBuiltinFunction("Array", "reduce", Builtins::kArrayReduce);
if (FLAG_experimental_array_builtins) {
LookupIterator it6(array_prototype,
factory->NewStringFromAsciiChecked("filter"),
LookupIterator::OWN_SKIP_INTERCEPTOR);
Handle<Object> filter_function =
Object::GetProperty(&it6).ToHandleChecked();
Handle<JSFunction>::cast(filter_function)
->set_code(isolate->builtins()->builtin(Builtins::kArrayFilter));
Handle<JSFunction>::cast(filter_function)
->shared()
->set_code(isolate->builtins()->builtin(Builtins::kArrayFilter));
InstallOneBuiltinFunction("Array", "filter", Builtins::kArrayFilter);
}
}

View File

@ -12,32 +12,189 @@ namespace internal {
class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
public:
explicit ArrayBuiltinCodeStubAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state), to_(this, MachineRepresentation::kTagged) {
to_.Bind(SmiConstant(0));
: CodeStubAssembler(state),
k_(this, MachineRepresentation::kTagged, SmiConstant(0)),
a_(this, MachineRepresentation::kTagged, SmiConstant(0)),
to_(this, MachineRepresentation::kTagged, SmiConstant(0)) {}
typedef std::function<Node*(ArrayBuiltinCodeStubAssembler* masm)>
BuiltinResultGenerator;
typedef std::function<void(ArrayBuiltinCodeStubAssembler* masm)>
BuiltinResultIndexInitializer;
typedef std::function<Node*(ArrayBuiltinCodeStubAssembler* masm,
Node* k_value, Node* k)>
CallResultProcessor;
typedef std::function<void(ArrayBuiltinCodeStubAssembler* masm)>
PostLoopAction;
Node* ForEachResultGenerator() { return UndefinedConstant(); }
Node* ForEachProcessor(Node* k_value, Node* k) {
CallJS(CodeFactory::Call(isolate()), context(), callbackfn(), this_arg(),
k_value, k, o());
return a();
}
typedef std::function<Node*(Node* context, Node* o, Node* len)>
BuiltinResultGenerator;
typedef std::function<void(Node* context, Node* a)>
BuiltinResultIndexInitializer;
typedef std::function<void(Node* context, Node* value, Node* a,
Node* callback_result)>
CallResultProcessor;
Node* SomeResultGenerator() { return FalseConstant(); }
Node* SomeProcessor(Node* k_value, Node* k) {
Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(),
this_arg(), k_value, k, o());
Label false_continue(this), return_true(this);
BranchIfToBooleanIsTrue(value, &return_true, &false_continue);
Bind(&return_true);
Return(TrueConstant());
Bind(&false_continue);
return a();
}
Node* EveryResultGenerator() { return TrueConstant(); }
Node* EveryProcessor(Node* k_value, Node* k) {
Node* value = CallJS(CodeFactory::Call(isolate()), context(), callbackfn(),
this_arg(), k_value, k, o());
Label true_continue(this), return_false(this);
BranchIfToBooleanIsTrue(value, &true_continue, &return_false);
Bind(&return_false);
Return(FalseConstant());
Bind(&true_continue);
return a();
}
Node* ReduceResultGenerator() {
Variable a(this, MachineRepresentation::kTagged, UndefinedConstant());
Label no_initial_value(this), has_initial_value(this), done(this, {&a});
// 8. If initialValue is present, then
Node* parent_frame_ptr = LoadParentFramePointer();
Node* marker_or_function = LoadBufferObject(
parent_frame_ptr, CommonFrameConstants::kContextOrFrameTypeOffset);
GotoIf(
MarkerIsNotFrameType(marker_or_function, StackFrame::ARGUMENTS_ADAPTOR),
&has_initial_value);
// Has arguments adapter, check count.
Node* adapted_parameter_count = LoadBufferObject(
parent_frame_ptr, ArgumentsAdaptorFrameConstants::kLengthOffset);
Branch(SmiLessThan(adapted_parameter_count,
SmiConstant(IteratingArrayBuiltinDescriptor::kThisArg)),
&no_initial_value, &has_initial_value);
// a. Set accumulator to initialValue.
Bind(&has_initial_value);
a.Bind(this_arg());
Goto(&done);
// 9. Else initialValue is not present,
Bind(&no_initial_value);
// a. Let kPresent be false.
a.Bind(TheHoleConstant());
Goto(&done);
Bind(&done);
return a.value();
}
Node* ReduceProcessor(Node* k_value, Node* k) {
Variable result(this, MachineRepresentation::kTagged);
Label done(this, {&result}), initial(this);
GotoIf(WordEqual(a(), TheHoleConstant()), &initial);
result.Bind(CallJS(CodeFactory::Call(isolate()), context(), callbackfn(),
UndefinedConstant(), a(), k_value, k, o()));
Goto(&done);
Bind(&initial);
result.Bind(k_value);
Goto(&done);
Bind(&done);
return result.value();
}
void ReducePostLoopAction() {
Label ok(this);
GotoIf(WordNotEqual(a(), TheHoleConstant()), &ok);
CallRuntime(Runtime::kThrowTypeError, context(),
SmiConstant(MessageTemplate::kReduceNoInitial));
Unreachable();
Bind(&ok);
}
Node* FilterResultGenerator() {
// 7. Let A be ArraySpeciesCreate(O, 0).
return ArraySpeciesCreate(context(), o(), SmiConstant(0));
}
void FilterResultIndexReinitializer() {
Variable merged_length(this, MachineRepresentation::kTagged);
Label has_length(this, &merged_length), not_js_array(this);
GotoIf(DoesntHaveInstanceType(a(), JS_ARRAY_TYPE), &not_js_array);
merged_length.Bind(LoadJSArrayLength(a()));
Goto(&has_length);
Bind(&not_js_array);
Node* len_property =
GetProperty(context(), a(), 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();
to_.Bind(len);
}
Node* FilterProcessor(Node* k_value, Node* k) {
Node* callback_result = CallJS(CodeFactory::Call(isolate()), context(),
callbackfn(), this_arg(), k_value, k, o());
Label true_continue(this, &to_), false_continue(this);
BranchIfToBooleanIsTrue(callback_result, &true_continue, &false_continue);
Bind(&true_continue);
// 1. let status be CreateDataPropertyOrThrow(A, ToString(to), kValue).
// 2. ReturnIfAbrupt(status)
Node* const p_to = ToString(context(), to_.value());
CallRuntime(Runtime::kCreateDataProperty, context(), a(), p_to, k_value);
// 3. Increase to by 1.
to_.Bind(NumberInc(to_.value()));
Goto(&false_continue);
Bind(&false_continue);
return a();
}
void NullPostLoopAction() {}
void NullResultIndexReinitializer() {}
protected:
Node* context() { return context_; }
Node* o() { return o_; }
Node* len() { return len_; }
Node* callbackfn() { return callbackfn_; }
Node* this_arg() { return this_arg_; }
Node* k() { return k_.value(); }
Node* a() { return a_.value(); }
void GenerateIteratingArrayBuiltinBody(
const char* name, const BuiltinResultGenerator& generator,
const CallResultProcessor& processor,
const CallResultProcessor& processor, const PostLoopAction& action,
const Callable& slow_case_continuation) {
// TODO(ishell): use constants from Descriptor once the JSFunction linkage
// arguments are reordered.
Node* receiver = Parameter(IteratingArrayBuiltinDescriptor::kReceiver);
Node* callbackfn = Parameter(IteratingArrayBuiltinDescriptor::kCallback);
Node* this_arg = Parameter(IteratingArrayBuiltinDescriptor::kThisArg);
Node* context = Parameter(IteratingArrayBuiltinDescriptor::kContext);
callbackfn_ = Parameter(IteratingArrayBuiltinDescriptor::kCallback);
this_arg_ = Parameter(IteratingArrayBuiltinDescriptor::kThisArg);
context_ = Parameter(IteratingArrayBuiltinDescriptor::kContext);
Node* new_target = Parameter(IteratingArrayBuiltinDescriptor::kNewTarget);
Variable k(this, MachineRepresentation::kTagged, SmiConstant(0));
Label non_array(this), slow(this, &k), array_changes(this, &k);
k_.Bind(SmiConstant(0));
a_.Bind(UndefinedConstant());
Label non_array(this), slow(this, {&k_, &a_, &to_}),
array_changes(this, {&k_, &a_, &to_});
// TODO(danno): Seriously? Do we really need to throw the exact error
// message on null and undefined so that the webkit tests pass?
@ -51,34 +208,34 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
// 1. Let O be ToObject(this value).
// 2. ReturnIfAbrupt(O)
Node* o = CallStub(CodeFactory::ToObject(isolate()), context, receiver);
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), &not_js_array);
merged_length.Bind(LoadJSArrayLength(o));
GotoIf(DoesntHaveInstanceType(o(), JS_ARRAY_TYPE), &not_js_array);
merged_length.Bind(LoadJSArrayLength(o()));
Goto(&has_length);
Bind(&not_js_array);
Node* len_property =
GetProperty(context, o, isolate()->factory()->length_string());
GetProperty(context(), o(), isolate()->factory()->length_string());
merged_length.Bind(
CallStub(CodeFactory::ToLength(isolate()), context, len_property));
CallStub(CodeFactory::ToLength(isolate()), context(), len_property));
Goto(&has_length);
Bind(&has_length);
Node* len = merged_length.value();
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);
GotoIf(TaggedIsSmi(callbackfn()), &type_exception);
Branch(IsCallableMap(LoadMap(callbackfn())), &done, &type_exception);
Bind(&throw_null_undefined_exception);
{
CallRuntime(
Runtime::kThrowTypeError, context,
Runtime::kThrowTypeError, context(),
SmiConstant(MessageTemplate::kCalledOnNullOrUndefined),
HeapConstant(isolate()->factory()->NewStringFromAsciiChecked(name)));
Unreachable();
@ -86,192 +243,120 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
Bind(&type_exception);
{
CallRuntime(Runtime::kThrowTypeError, context,
SmiConstant(MessageTemplate::kCalledNonCallable), callbackfn);
CallRuntime(Runtime::kThrowTypeError, context(),
SmiConstant(MessageTemplate::kCalledNonCallable),
callbackfn());
Unreachable();
}
Bind(&done);
Node* a = generator(context, 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
// [Already done in code assembler initialization]
a_.Bind(generator(this));
HandleFastElements(processor, action, &slow);
Bind(&slow);
Node* target = LoadFromFrame(StandardFrameConstants::kFunctionOffset,
MachineType::TaggedPointer());
TailCallStub(
slow_case_continuation, context, target, new_target,
slow_case_continuation, context(), target, new_target,
Int32Constant(IteratingArrayBuiltinLoopContinuationDescriptor::kArity),
receiver, callbackfn, this_arg, a, o, k.value(), len);
receiver, callbackfn(), this_arg(), a_.value(), o(), k_.value(), len_);
}
void GenerateIteratingArrayBuiltinLoopContinuation(
const BuiltinResultIndexInitializer& index_initializer,
const CallResultProcessor& processor) {
const CallResultProcessor& processor, const PostLoopAction& action) {
// TODO(ishell): use constants from Descriptor once the JSFunction linkage
// arguments are reordered.
Node* callbackfn =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kCallback);
Node* this_arg =
this_arg_ =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kThisArg);
Node* a =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kArray);
Node* o =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kObject);
Node* initial_k =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kInitialK);
Node* len =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kLength);
Node* context =
callbackfn_ =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kCallback);
a_.Bind(Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kArray));
k_.Bind(
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kInitialK));
o_ = Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kObject);
len_ = Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kLength);
context_ =
Parameter(IteratingArrayBuiltinLoopContinuationDescriptor::kContext);
index_initializer(context, a);
index_initializer(this);
// 8. Repeat, while k < len
Variable k(this, MachineRepresentation::kTagged, initial_k);
VariableList list({&k, &to_}, zone());
Label loop(this, list);
Label loop(this, {&k_, &a_, &to_});
Label after_loop(this);
Goto(&loop);
Bind(&loop);
{
GotoUnlessNumberLessThan(k.value(), len, &after_loop);
GotoUnlessNumberLessThan(k(), len_, &after_loop);
Label done_element(this, &to_);
// a. Let Pk be ToString(k).
Node* p_k = ToString(context, k.value());
Node* p_k = ToString(context(), k());
// b. Let kPresent be HasProperty(O, Pk).
// c. ReturnIfAbrupt(kPresent).
Node* k_present = HasProperty(o, p_k, context);
Node* k_present = HasProperty(o(), p_k, context());
// 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());
Node* k_value = GetProperty(context(), o(), k());
// iii. Let funcResult be Call(callbackfn, T, «kValue, k, O»).
// iv. ReturnIfAbrupt(funcResult).
Node* callback_result =
CallJS(CodeFactory::Call(isolate()), context, callbackfn, this_arg,
k_value, k.value(), o);
processor(context, k_value, a, callback_result);
a_.Bind(processor(this, k_value, k()));
Goto(&done_element);
Bind(&done_element);
// e. Increase k by 1.
k.Bind(NumberInc(k.value()));
k_.Bind(NumberInc(k_.value()));
Goto(&loop);
}
Bind(&after_loop);
Return(a);
}
Node* FilterResultGenerator(Node* context, Node* o, Node* len) {
// 7. Let A be ArraySpeciesCreate(O, 0).
return ArraySpeciesCreate(context, o, SmiConstant(0));
}
void FilterResultIndexReinitializer(Node* context, Node* a) {
Variable merged_length(this, MachineRepresentation::kTagged);
Label has_length(this, &merged_length), not_js_array(this);
GotoIf(DoesntHaveInstanceType(a, JS_ARRAY_TYPE), &not_js_array);
merged_length.Bind(LoadJSArrayLength(a));
Goto(&has_length);
Bind(&not_js_array);
Node* len_property =
GetProperty(context, a, 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();
to_.Bind(len);
}
void ForEachProcessor(Node* context, Node* value, Node* a,
Node* callback_result) {}
void SomeProcessor(Node* context, Node* value, Node* a,
Node* callback_result) {
Label false_continue(this), return_true(this);
BranchIfToBooleanIsTrue(callback_result, &return_true, &false_continue);
Bind(&return_true);
Return(TrueConstant());
Bind(&false_continue);
}
void EveryProcessor(Node* context, Node* value, Node* a,
Node* callback_result) {
Label true_continue(this), return_false(this);
BranchIfToBooleanIsTrue(callback_result, &true_continue, &return_false);
Bind(&return_false);
Return(FalseConstant());
Bind(&true_continue);
}
void FilterProcessor(Node* context, Node* value, Node* a,
Node* callback_result) {
Label true_continue(this, &to_), false_continue(this);
BranchIfToBooleanIsTrue(callback_result, &true_continue, &false_continue);
Bind(&true_continue);
// 1. let status be CreateDataPropertyOrThrow(A, ToString(to), kValue).
// 2. ReturnIfAbrupt(status)
Node* const p_to = ToString(context, to_.value());
CallRuntime(Runtime::kCreateDataProperty, context, a, p_to, value);
// 3. Increase to by 1.
to_.Bind(NumberInc(to_.value()));
Goto(&false_continue);
Bind(&false_continue);
action(this);
Return(a_.value());
}
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) {
void VisitAllFastElementsOneKind(ElementsKind kind,
const CallResultProcessor& processor,
Label* array_changed, ParameterMode mode) {
Comment("begin VisitAllFastElementsOneKind");
Variable original_map(this, MachineRepresentation::kTagged);
original_map.Bind(LoadMap(o));
VariableList list({&original_map, &to_}, zone());
Node* last_index = nullptr;
original_map.Bind(LoadMap(o()));
VariableList list({&original_map, &a_, &k_, &to_}, zone());
BuildFastLoop(
list, IntPtrOrSmiConstant(0, mode), TaggedToParameter(len, mode),
[=, &original_map, &last_index](Node* index) {
last_index = index;
list, IntPtrOrSmiConstant(0, mode), TaggedToParameter(len(), mode),
[=, &original_map](Node* index) {
k_.Bind(ParameterToTagged(index, mode));
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);
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)),
GotoIf(SmiGreaterThanOrEqual(k_.value(), LoadJSArrayLength(o())),
array_changed);
// Re-load the elements array. If may have been resized.
Node* elements = LoadElements(o);
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.
@ -289,10 +374,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
LoadDoubleWithHoleCheck(elements, offset, &hole_element);
value = AllocateHeapNumberWithValue(double_value);
}
Node* callback_result =
CallJS(CodeFactory::Call(isolate()), context, callbackfn,
this_arg, value, tagged_index, o);
processor(context, value, a, callback_result);
a_.Bind(processor(this, value, k()));
Goto(&one_element_done);
Bind(&hole_element);
@ -305,26 +387,24 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
},
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) {
void HandleFastElements(const CallResultProcessor& processor,
const PostLoopAction& action, 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);
GotoIf(TaggedIsNotSmi(len()), slow);
BranchIfFastJSArray(o, context,
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* o_map = LoadMap(o());
Node* bit_field2 = LoadMapBitField2(o_map);
Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2);
Branch(Int32GreaterThan(kind, Int32Constant(FAST_HOLEY_ELEMENTS)),
@ -333,17 +413,12 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
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);
VisitAllFastElementsOneKind(FAST_ELEMENTS, processor, slow, mode);
action(this);
// No exception, return success
Return(a);
Bind(&array_changed);
k.Bind(ParameterToTagged(last_index, mode));
Goto(slow);
Return(a_.value());
}
Bind(&maybe_double_elements);
@ -352,20 +427,22 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
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);
VisitAllFastElementsOneKind(FAST_DOUBLE_ELEMENTS, processor, slow, mode);
action(this);
// No exception, return success
Return(a);
Bind(&array_changed);
k.Bind(ParameterToTagged(last_index, mode));
Goto(slow);
Return(a_.value());
}
}
Node* callbackfn_;
Node* o_;
Node* this_arg_;
Node* len_;
Node* context_;
Variable k_;
Variable a_;
Variable to_;
};
@ -527,80 +604,84 @@ TF_BUILTIN(FastArrayPush, CodeStubAssembler) {
TF_BUILTIN(ArrayForEachLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[](Node* context, Node* a) {},
[this](Node* context, Node* value, Node* a, Node* callback_result) {
ForEachProcessor(context, value, a, callback_result);
});
}
TF_BUILTIN(ArrayFilter, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.filter",
[=](Node* context, Node* o, Node* len) {
return FilterResultGenerator(context, o, len);
},
[this](Node* context, Node* value, Node* a, Node* callback_result) {
FilterProcessor(context, value, a, callback_result);
},
CodeFactory::ArrayFilterLoopContinuation(isolate()));
}
TF_BUILTIN(ArrayFilterLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[this](Node* context, Node* a) {
FilterResultIndexReinitializer(context, a);
},
[this](Node* context, Node* value, Node* a, Node* callback_result) {
FilterProcessor(context, value, a, callback_result);
});
&ArrayBuiltinCodeStubAssembler::NullResultIndexReinitializer,
&ArrayBuiltinCodeStubAssembler::ForEachProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
}
TF_BUILTIN(ArrayForEach, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.forEach",
[=](Node*, Node*, Node*) { return UndefinedConstant(); },
[this](Node* context, Node* value, Node* a, Node* callback_result) {
ForEachProcessor(context, value, a, callback_result);
},
&ArrayBuiltinCodeStubAssembler::ForEachResultGenerator,
&ArrayBuiltinCodeStubAssembler::ForEachProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
CodeFactory::ArrayForEachLoopContinuation(isolate()));
}
TF_BUILTIN(ArraySomeLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[](Node* context, Node* a) {},
[this](Node* context, Node* value, Node* a, Node* callback_result) {
SomeProcessor(context, value, a, callback_result);
});
&ArrayBuiltinCodeStubAssembler::NullResultIndexReinitializer,
&ArrayBuiltinCodeStubAssembler::SomeProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
}
TF_BUILTIN(ArraySome, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.some",
[=](Node*, Node*, Node*) { return FalseConstant(); },
[this](Node* context, Node* value, Node* a, Node* callback_result) {
SomeProcessor(context, value, a, callback_result);
},
&ArrayBuiltinCodeStubAssembler::SomeResultGenerator,
&ArrayBuiltinCodeStubAssembler::SomeProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
CodeFactory::ArraySomeLoopContinuation(isolate()));
}
TF_BUILTIN(ArrayEveryLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
[](Node* context, Node* a) {},
[this](Node* context, Node* value, Node* a, Node* callback_result) {
EveryProcessor(context, value, a, callback_result);
});
&ArrayBuiltinCodeStubAssembler::NullResultIndexReinitializer,
&ArrayBuiltinCodeStubAssembler::EveryProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
}
TF_BUILTIN(ArrayEvery, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.every",
[=](Node*, Node*, Node*) { return TrueConstant(); },
[this](Node* context, Node* value, Node* a, Node* callback_result) {
EveryProcessor(context, value, a, callback_result);
},
&ArrayBuiltinCodeStubAssembler::EveryResultGenerator,
&ArrayBuiltinCodeStubAssembler::EveryProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
CodeFactory::ArrayEveryLoopContinuation(isolate()));
}
TF_BUILTIN(ArrayReduceLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::NullResultIndexReinitializer,
&ArrayBuiltinCodeStubAssembler::ReduceProcessor,
&ArrayBuiltinCodeStubAssembler::ReducePostLoopAction);
}
TF_BUILTIN(ArrayReduce, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.reduce",
&ArrayBuiltinCodeStubAssembler::ReduceResultGenerator,
&ArrayBuiltinCodeStubAssembler::ReduceProcessor,
&ArrayBuiltinCodeStubAssembler::ReducePostLoopAction,
CodeFactory::ArrayReduceLoopContinuation(isolate()));
}
TF_BUILTIN(ArrayFilterLoopContinuation, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinLoopContinuation(
&ArrayBuiltinCodeStubAssembler::FilterResultIndexReinitializer,
&ArrayBuiltinCodeStubAssembler::FilterProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction);
}
TF_BUILTIN(ArrayFilter, ArrayBuiltinCodeStubAssembler) {
GenerateIteratingArrayBuiltinBody(
"Array.prototype.reduce",
&ArrayBuiltinCodeStubAssembler::FilterResultGenerator,
&ArrayBuiltinCodeStubAssembler::FilterProcessor,
&ArrayBuiltinCodeStubAssembler::NullPostLoopAction,
CodeFactory::ArrayFilterLoopContinuation(isolate()));
}
TF_BUILTIN(ArrayIsArray, CodeStubAssembler) {
Node* object = Parameter(Descriptor::kArg);
Node* context = Parameter(Descriptor::kContext);

View File

@ -296,6 +296,9 @@ class Isolate;
/* ES6 #sec-array.prototype.filter */ \
TFJ(ArrayFilterLoopContinuation, 6) \
TFJ(ArrayFilter, 2, kCallbackFn, kThisArg) \
/* ES6 #sec-array.prototype.reduce */ \
TFJ(ArrayReduceLoopContinuation, 6) \
TFJ(ArrayReduce, 2, kCallbackFn, kInitialValue) \
/* ES6 #sec-array.prototype.entries */ \
TFJ(ArrayPrototypeEntries, 0) \
/* ES6 #sec-array.prototype.keys */ \

View File

@ -512,6 +512,12 @@ Callable CodeFactory::ArrayEveryLoopContinuation(Isolate* isolate) {
IteratingArrayBuiltinLoopContinuationDescriptor(isolate));
}
// static
Callable CodeFactory::ArrayReduceLoopContinuation(Isolate* isolate) {
return Callable(isolate->builtins()->ArrayReduceLoopContinuation(),
IteratingArrayBuiltinLoopContinuationDescriptor(isolate));
}
// static
Callable CodeFactory::FunctionPrototypeBind(Isolate* isolate) {
return Callable(isolate->builtins()->FunctionPrototypeBind(),

View File

@ -185,6 +185,7 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable ArrayForEachLoopContinuation(Isolate* isolate);
static Callable ArraySomeLoopContinuation(Isolate* isolate);
static Callable ArrayEveryLoopContinuation(Isolate* isolate);
static Callable ArrayReduceLoopContinuation(Isolate* isolate);
static Callable FunctionPrototypeBind(Isolate* isolate);
static Callable PromiseHandleReject(Isolate* isolate);
};

View File

@ -587,7 +587,7 @@ Node* CodeAssembler::CallStubR(const CallInterfaceDescriptor& descriptor,
#define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::CallStubR( \
const CallInterfaceDescriptor& descriptor, size_t, Node*, __VA_ARGS__);
REPEAT_1_TO_7(INSTANTIATE, Node*)
REPEAT_1_TO_8(INSTANTIATE, Node*)
#undef INSTANTIATE
Node* CodeAssembler::CallStubN(const CallInterfaceDescriptor& descriptor,
@ -766,7 +766,7 @@ bool CodeAssemblerVariable::IsBound() const { return impl_->value_ != nullptr; }
CodeAssemblerLabel::CodeAssemblerLabel(CodeAssembler* assembler,
size_t vars_count,
CodeAssemblerVariable** vars,
CodeAssemblerVariable* const* vars,
CodeAssemblerLabel::Type type)
: bound_(false),
merge_count_(0),

View File

@ -462,8 +462,14 @@ class CodeAssemblerLabel {
: CodeAssemblerLabel(assembler, merged_variables.length(),
&(merged_variables[0]), type) {}
CodeAssemblerLabel(
CodeAssembler* assembler, size_t count, CodeAssemblerVariable** vars,
CodeAssembler* assembler, size_t count,
CodeAssemblerVariable* const* vars,
CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred);
CodeAssemblerLabel(
CodeAssembler* assembler,
std::initializer_list<CodeAssemblerVariable*> vars,
CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred)
: CodeAssemblerLabel(assembler, vars.size(), vars.begin(), type) {}
CodeAssemblerLabel(
CodeAssembler* assembler, CodeAssemblerVariable* merged_variable,
CodeAssemblerLabel::Type type = CodeAssemblerLabel::kNonDeferred)

View File

@ -434,6 +434,7 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kArrayForEach:
case Builtins::kArrayEvery:
case Builtins::kArraySome:
case Builtins::kArrayReduce:
// Boolean bulitins.
case Builtins::kBooleanConstructor:
case Builtins::kBooleanPrototypeToString:

View File

@ -408,6 +408,12 @@ testReduce("reduceRight", "ArrayWithNonElementPropertiesReduceRight", 6,
[5, 1, 0, arrayPlus, 6],
], arrayPlus, sum, 0);
// Test passing undefined as initial value (to test missing parameter
// detection).
[1].reduce((a, b) => { assertEquals(a, undefined); assertEquals(b, 1) },
undefined);
[1, 2].reduce((a, b) => { assertEquals(a, 1); assertEquals(b, 2); });
[1].reduce((a, b) => { assertTrue(false); });
// Test error conditions: