Commit 96220730 authored by petermarshall's avatar petermarshall Committed by Commit bot

[Ignition/turbo] Add a CallWithSpread bytecode.

Also, emit a NewWithSpread bytecode for CallNew AST nodes where possible, rather than desugaring in the parser.

BUG=v8:5511

Review-Url: https://codereview.chromium.org/2629363002
Cr-Original-Commit-Position: refs/heads/master@{#42455}
Committed: https://chromium.googlesource.com/v8/v8/+/4bae43471d5685e34d8bd74458889b83e60235a0
Review-Url: https://codereview.chromium.org/2629363002
Cr-Commit-Position: refs/heads/master@{#42590}
parent 7b6ac70a
......@@ -403,8 +403,8 @@ void AstNumberingVisitor::VisitCompareOperation(CompareOperation* node) {
void AstNumberingVisitor::VisitSpread(Spread* node) {
IncrementNodeCount();
// We can only get here from super calls currently.
DisableFullCodegenAndCrankshaft(kSuperReference);
// We can only get here from spread calls currently.
DisableFullCodegenAndCrankshaft(kSpreadCall);
node->set_base_id(ReserveIdRange(Spread::num_ids()));
Visit(node->expression());
}
......
......@@ -1906,6 +1906,10 @@ class Call final : public Expression {
}
void MarkTail() { bit_field_ = IsTailField::update(bit_field_, true); }
bool only_last_arg_is_spread() {
return !arguments_->is_empty() && arguments_->last()->IsSpread();
}
enum CallType {
GLOBAL_CALL,
WITH_CALL,
......@@ -2005,6 +2009,10 @@ class CallNew final : public Expression {
set_is_monomorphic(true);
}
bool only_last_arg_is_spread() {
return !arguments_->is_empty() && arguments_->last()->IsSpread();
}
private:
friend class AstNodeFactory;
......
......@@ -186,6 +186,7 @@ namespace internal {
"Sloppy function expects JSReceiver as receiver.") \
V(kSmiAdditionOverflow, "Smi addition overflow") \
V(kSmiSubtractionOverflow, "Smi subtraction overflow") \
V(kSpreadCall, "Call with spread argument") \
V(kStackAccessBelowStackPointer, "Stack access below stack pointer") \
V(kStackFrameTypesMustMatch, "Stack frame types must match") \
V(kSuperReference, "Super reference") \
......
......@@ -1353,6 +1353,16 @@ Node* BytecodeGraphBuilder::ProcessCallNewWithSpreadArguments(
return value;
}
void BytecodeGraphBuilder::VisitCallWithSpread() {
PrepareEagerCheckpoint();
interpreter::Register first_arg = bytecode_iterator().GetRegisterOperand(0);
size_t arg_count = bytecode_iterator().GetRegisterCountOperand(1);
const Operator* call =
javascript()->CallRuntime(Runtime::kCallWithSpread, arg_count);
Node* value = ProcessCallRuntimeArguments(call, first_arg, arg_count);
environment()->BindAccumulator(value, Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitNewWithSpread() {
PrepareEagerCheckpoint();
interpreter::Register callee_reg = bytecode_iterator().GetRegisterOperand(0);
......
......@@ -891,6 +891,11 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallWithSpread(RegisterList args) {
OutputCallWithSpread(args, args.register_count());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
RegisterList args,
int feedback_slot_id) {
......
......@@ -213,6 +213,11 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
Call::CallType call_type,
TailCallMode tail_call_mode = TailCallMode::kDisallow);
// Call a JS function. The JSFunction or Callable to be called should be in
// |args[0]|, the receiver in |args[1]| and the arguments in |args[2]|
// onwards. The final argument must be a spread.
BytecodeArrayBuilder& CallWithSpread(RegisterList args);
// Call the new operator. The accumulator holds the |new_target|.
// The |constructor| is in a register and arguments are in |args|.
BytecodeArrayBuilder& New(Register constructor, RegisterList args,
......
......@@ -2445,12 +2445,27 @@ void BytecodeGenerator::VisitCall(Call* expr) {
return VisitCallSuper(expr);
}
Register callee = register_allocator()->NewRegister();
// Grow the args list as we visit receiver / arguments to avoid allocating all
// the registers up-front. Otherwise these registers are unavailable during
// receiver / argument visiting and we can end up with memory leaks due to
// registers keeping objects alive.
RegisterList args = register_allocator()->NewGrowableRegisterList();
RegisterList args;
Register callee;
// The CallWithSpread bytecode takes all arguments in a register list so that
// it can easily call into a runtime function for its implementation. This
// will change once CallWithSpread has an implementation in ASM.
// TODO(petermarshall): Remove this special path when CallWithSpread is done.
if (expr->only_last_arg_is_spread()) {
args = register_allocator()->NewGrowableRegisterList();
callee = register_allocator()->GrowRegisterList(&args);
} else {
callee = register_allocator()->NewRegister();
args = register_allocator()->NewGrowableRegisterList();
}
// TODO(petermarshall): We have a lot of call bytecodes that are very similar,
// see if we can reduce the number by adding a separate argument which
// specifies the call type (e.g., property, spread, tailcall, etc.).
// Prepare the callee and the receiver to the function call. This depends on
// the semantics of the underlying call type.
......@@ -2459,7 +2474,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
case Call::KEYED_PROPERTY_CALL: {
Property* property = callee_expr->AsProperty();
VisitAndPushIntoRegisterList(property->obj(), &args);
VisitPropertyLoadForRegister(args[0], property, callee);
VisitPropertyLoadForRegister(args.last_register(), property, callee);
break;
}
case Call::GLOBAL_CALL: {
......@@ -2520,7 +2535,11 @@ void BytecodeGenerator::VisitCall(Call* expr) {
// Evaluate all arguments to the function call and store in sequential args
// registers.
VisitArguments(expr->arguments(), &args);
CHECK_EQ(expr->arguments()->length() + 1, args.register_count());
// TODO(petermarshall): Check this for spread calls as well when
// CallWithSpread is done.
if (!expr->only_last_arg_is_spread()) {
CHECK_EQ(expr->arguments()->length() + 1, args.register_count());
}
// Resolve callee for a potential direct eval call. This block will mutate the
// callee value.
......@@ -2550,9 +2569,17 @@ void BytecodeGenerator::VisitCall(Call* expr) {
builder()->SetExpressionPosition(expr);
int const feedback_slot_index = feedback_index(expr->CallFeedbackICSlot());
builder()->Call(callee, args, feedback_slot_index, call_type,
expr->tail_call_mode());
// When a call contains a spread, a Call AST node is only created if there is
// exactly one spread, and it is the last argument.
if (expr->only_last_arg_is_spread()) {
CHECK_EQ(expr->arguments()->length() + 2, args.register_count());
DCHECK_EQ(TailCallMode::kDisallow, expr->tail_call_mode());
builder()->CallWithSpread(args);
} else {
int const feedback_slot_index = feedback_index(expr->CallFeedbackICSlot());
builder()->Call(callee, args, feedback_slot_index, call_type,
expr->tail_call_mode());
}
}
void BytecodeGenerator::VisitCallSuper(Call* expr) {
......@@ -2574,7 +2601,7 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
// When a super call contains a spread, a CallSuper AST node is only created
// if there is exactly one spread, and it is the last argument.
if (!args->is_empty() && args->last()->IsSpread()) {
if (expr->only_last_arg_is_spread()) {
// TODO(petermarshall): Collect type on the feedback slot.
builder()->NewWithSpread(constructor, args_regs);
} else {
......@@ -2595,12 +2622,18 @@ void BytecodeGenerator::VisitCallNew(CallNew* expr) {
RegisterList args = register_allocator()->NewGrowableRegisterList();
VisitArguments(expr->arguments(), &args);
builder()->SetExpressionPosition(expr);
// The accumulator holds new target which is the same as the
// constructor for CallNew.
builder()
->LoadAccumulatorWithRegister(constructor)
.New(constructor, args, feedback_index(expr->CallNewFeedbackSlot()));
builder()->SetExpressionPosition(expr);
builder()->LoadAccumulatorWithRegister(constructor);
if (expr->only_last_arg_is_spread()) {
// TODO(petermarshall): Collect type on the feedback slot.
builder()->NewWithSpread(constructor, args);
} else {
builder()->New(constructor, args,
feedback_index(expr->CallNewFeedbackSlot()));
}
}
void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
......
......@@ -151,6 +151,8 @@ namespace interpreter {
OperandType::kRegCount, OperandType::kIdx) \
V(CallProperty, AccumulatorUse::kWrite, OperandType::kReg, \
OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \
V(CallWithSpread, AccumulatorUse::kWrite, OperandType::kRegList, \
OperandType::kRegCount) \
V(TailCall, AccumulatorUse::kWrite, OperandType::kReg, \
OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \
V(CallRuntime, AccumulatorUse::kWrite, OperandType::kRuntimeId, \
......
......@@ -2176,7 +2176,27 @@ void Interpreter::DoCallJSRuntime(InterpreterAssembler* assembler) {
__ Dispatch();
}
// NewWithSpread <constructor> <first_arg> <arg_count>
// CallWithSpread <first_arg> <arg_count>
//
// Call a JSfunction or Callable in |first_arg| with the receiver in
// |first_arg + 1| and |arg_count - 2| arguments in subsequent registers. The
// final argument is always a spread.
//
void Interpreter::DoCallWithSpread(InterpreterAssembler* assembler) {
Node* first_arg_reg = __ BytecodeOperandReg(0);
Node* first_arg = __ RegisterLocation(first_arg_reg);
Node* args_count = __ BytecodeOperandCount(1);
Node* context = __ GetContext();
// Call into Runtime function CallWithSpread which does everything.
Node* runtime_function = __ Int32Constant(Runtime::kCallWithSpread);
Node* result =
__ CallRuntimeN(runtime_function, context, first_arg, args_count);
__ SetAccumulator(result);
__ Dispatch();
}
// NewWithSpread <first_arg> <arg_count>
//
// Call the constructor in |constructor| with the first argument in register
// |first_arg| and |arg_count| arguments in subsequent registers. The final
......
......@@ -2615,6 +2615,7 @@ typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments(
bool done = (peek() == Token::RPAREN);
bool was_unspread = false;
int unspread_sequences_count = 0;
int spread_count = 0;
while (!done) {
int start_pos = peek_position();
bool is_spread = Check(Token::ELLIPSIS);
......@@ -2638,6 +2639,7 @@ typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments(
// are not prefixed with a spread '...' operator.
if (is_spread) {
was_unspread = false;
spread_count++;
} else if (!was_unspread) {
was_unspread = true;
unspread_sequences_count++;
......@@ -2673,7 +2675,11 @@ typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments(
// Unspread parameter sequences are translated into array literals in the
// parser. Ensure that the number of materialized literals matches between
// the parser and preparser
impl()->MaterializeUnspreadArgumentsLiterals(unspread_sequences_count);
if (was_unspread || spread_count > 1) {
// There was more than one spread, or the spread was not the final
// argument, so the parser will materialize literals.
impl()->MaterializeUnspreadArgumentsLiterals(unspread_sequences_count);
}
}
}
......@@ -3215,7 +3221,7 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) {
bool is_super_call = result->IsSuperCallReference();
if (spread_pos.IsValid()) {
result = impl()->SpreadCall(result, args, pos);
result = impl()->SpreadCall(result, args, pos, is_possibly_eval);
} else {
result = factory()->NewCall(result, args, pos, is_possibly_eval);
}
......
......@@ -3591,6 +3591,19 @@ uint32_t Parser::ComputeTemplateLiteralHash(const TemplateLiteral* lit) {
return running_hash;
}
namespace {
bool OnlyLastArgIsSpread(ZoneList<Expression*>* args) {
for (int i = 0; i < args->length() - 1; i++) {
if (args->at(i)->IsSpread()) {
return false;
}
}
return args->at(args->length() - 1)->IsSpread();
}
} // namespace
ZoneList<Expression*>* Parser::PrepareSpreadArguments(
ZoneList<Expression*>* list) {
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
......@@ -3654,27 +3667,20 @@ ZoneList<Expression*>* Parser::PrepareSpreadArguments(
}
Expression* Parser::SpreadCall(Expression* function,
ZoneList<Expression*>* args, int pos) {
ZoneList<Expression*>* args, int pos,
Call::PossiblyEval is_possibly_eval) {
// Handle these cases in BytecodeGenerator.
// [Call,New]WithSpread bytecodes aren't used with tailcalls - see
// https://crbug.com/v8/5867
if (!allow_tailcalls() && OnlyLastArgIsSpread(args)) {
return factory()->NewCall(function, args, pos);
}
if (function->IsSuperCallReference()) {
// Super calls
// $super_constructor = %_GetSuperConstructor(<this-function>)
// %reflect_construct($super_constructor, args, new.target)
bool only_last_arg_is_spread = false;
for (int i = 0; i < args->length(); i++) {
if (args->at(i)->IsSpread()) {
if (i == args->length() - 1) {
only_last_arg_is_spread = true;
}
break;
}
}
if (only_last_arg_is_spread) {
// Handle in BytecodeGenerator.
Expression* super_call_ref = NewSuperCallReference(pos);
return factory()->NewCall(super_call_ref, args, pos);
}
args = PrepareSpreadArguments(args);
ZoneList<Expression*>* tmp = new (zone()) ZoneList<Expression*>(1, zone());
tmp->Add(function->AsSuperCallReference()->this_function_var(), zone());
......@@ -3716,6 +3722,10 @@ Expression* Parser::SpreadCall(Expression* function,
Expression* Parser::SpreadCallNew(Expression* function,
ZoneList<Expression*>* args, int pos) {
if (OnlyLastArgIsSpread(args)) {
// Handle in BytecodeGenerator.
return factory()->NewCallNew(function, args, pos);
}
args = PrepareSpreadArguments(args);
args->InsertAt(0, function, zone());
......
......@@ -593,7 +593,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
ZoneList<Expression*>* PrepareSpreadArguments(ZoneList<Expression*>* list);
Expression* SpreadCall(Expression* function, ZoneList<Expression*>* args,
int pos);
int pos, Call::PossiblyEval is_possibly_eval);
Expression* SpreadCallNew(Expression* function, ZoneList<Expression*>* args,
int pos);
Expression* RewriteSuperCall(Expression* call_expression);
......
......@@ -985,7 +985,8 @@ class PreParser : public ParserBase<PreParser> {
V8_INLINE PreParserExpression SpreadCall(PreParserExpression function,
PreParserExpressionList args,
int pos);
int pos,
Call::PossiblyEval possibly_eval);
V8_INLINE PreParserExpression SpreadCallNew(PreParserExpression function,
PreParserExpressionList args,
int pos);
......@@ -1636,9 +1637,9 @@ class PreParser : public ParserBase<PreParser> {
};
PreParserExpression PreParser::SpreadCall(PreParserExpression function,
PreParserExpressionList args,
int pos) {
return factory()->NewCall(function, args, pos);
PreParserExpressionList args, int pos,
Call::PossiblyEval possibly_eval) {
return factory()->NewCall(function, args, pos, possibly_eval);
}
PreParserExpression PreParser::SpreadCallNew(PreParserExpression function,
......
......@@ -459,5 +459,48 @@ RUNTIME_FUNCTION(Runtime_GetSuperConstructor) {
return prototype;
}
RUNTIME_FUNCTION(Runtime_CallWithSpread) {
HandleScope scope(isolate);
DCHECK_LE(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, callable, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 1);
int function_argc = args.length() - 2;
CONVERT_ARG_HANDLE_CHECKED(Object, spread, args.length() - 1);
// Iterate over the spread if we need to.
if (spread->IterationHasObservableEffects()) {
Handle<JSFunction> spread_iterable_function = isolate->spread_iterable();
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, spread,
Execution::Call(isolate, spread_iterable_function,
isolate->factory()->undefined_value(), 1, &spread));
}
uint32_t spread_length;
Handle<JSArray> spread_array = Handle<JSArray>::cast(spread);
CHECK(spread_array->length()->ToArrayIndex(&spread_length));
int result_length = function_argc - 1 + spread_length;
ScopedVector<Handle<Object>> function_args(result_length);
// Append each of the individual args to the result.
for (int i = 0; i < function_argc - 1; i++) {
function_args[i] = args.at<Object>(2 + i);
}
// Append element of the spread to the result.
ElementsAccessor* accessor = spread_array->GetElementsAccessor();
for (uint32_t i = 0; i < spread_length; i++) {
DCHECK(accessor->HasElement(spread_array, i));
Handle<Object> element = accessor->Get(spread_array, i);
function_args[function_argc - 1 + i] = element;
}
// Call the function.
RETURN_RESULT_OR_FAILURE(
isolate, Execution::Call(isolate, callable, receiver, result_length,
function_args.start()));
}
} // namespace internal
} // namespace v8
......@@ -91,7 +91,8 @@ namespace internal {
F(StoreToSuper_Sloppy, 4, 1) \
F(StoreKeyedToSuper_Strict, 4, 1) \
F(StoreKeyedToSuper_Sloppy, 4, 1) \
F(GetSuperConstructor, 1, 1)
F(GetSuperConstructor, 1, 1) \
F(CallWithSpread, -1, 1)
#define FOR_EACH_INTRINSIC_COLLECTIONS(F) \
F(StringGetRawHashField, 1, 1) \
......
#
# Autogenerated by generate-bytecode-expectations.
#
---
wrap: yes
---
snippet: "
Math.max(...[1, 2, 3]);
"
frame size: 3
parameter count: 1
bytecode array length: 23
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 34 S> */ B(LdaGlobal), U8(0), U8(4),
B(Star), R(1),
/* 38 E> */ B(LdaNamedProperty), R(1), U8(1), U8(6),
B(Star), R(0),
B(CreateArrayLiteral), U8(2), U8(0), U8(9),
B(Star), R(2),
/* 39 E> */ B(CallWithSpread), R(0), U8(3),
B(LdaUndefined),
/* 58 S> */ B(Return),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["Math"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["max"],
CONSTANT_ELEMENTS_PAIR_TYPE,
]
handlers: [
]
---
snippet: "
Math.max(0, ...[1, 2, 3]);
"
frame size: 4
parameter count: 1
bytecode array length: 26
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 34 S> */ B(LdaGlobal), U8(0), U8(4),
B(Star), R(1),
/* 38 E> */ B(LdaNamedProperty), R(1), U8(1), U8(6),
B(Star), R(0),
B(LdaZero),
B(Star), R(2),
B(CreateArrayLiteral), U8(2), U8(0), U8(9),
B(Star), R(3),
/* 39 E> */ B(CallWithSpread), R(0), U8(4),
B(LdaUndefined),
/* 61 S> */ B(Return),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["Math"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["max"],
CONSTANT_ELEMENTS_PAIR_TYPE,
]
handlers: [
]
---
snippet: "
Math.max(0, ...[1, 2, 3], 4);
"
frame size: 8
parameter count: 1
bytecode array length: 60
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 34 S> */ B(LdaUndefined),
B(Star), R(1),
/* 34 E> */ B(LdaGlobal), U8(0), U8(2),
B(Star), R(0),
B(LdaNamedProperty), R(0), U8(1), U8(4),
B(Star), R(2),
B(LdaUndefined),
B(Star), R(4),
B(CreateArrayLiteral), U8(2), U8(1), U8(9),
B(Star), R(5),
B(LdaUndefined),
B(Star), R(6),
B(CreateArrayLiteral), U8(3), U8(0), U8(9),
B(Star), R(7),
B(CallJSRuntime), U8(%spread_iterable), R(6), U8(2),
B(Star), R(6),
B(CreateArrayLiteral), U8(4), U8(2), U8(9),
B(Star), R(7),
B(CallJSRuntime), U8(%spread_arguments), R(4), U8(4),
B(Star), R(4),
B(Mov), R(0), R(3),
B(CallJSRuntime), U8(%reflect_apply), R(1), U8(4),
B(LdaUndefined),
/* 64 S> */ B(Return),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["Math"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["max"],
CONSTANT_ELEMENTS_PAIR_TYPE,
CONSTANT_ELEMENTS_PAIR_TYPE,
CONSTANT_ELEMENTS_PAIR_TYPE,
]
handlers: [
]
#
# Autogenerated by generate-bytecode-expectations.
#
---
wrap: yes
---
snippet: "
class A { constructor(...args) { this.args = args; } }
new A(...[1, 2, 3]);
"
frame size: 8
parameter count: 1
bytecode array length: 64
bytecodes: [
B(LdaTheHole),
B(Star), R(2),
/* 30 E> */ B(StackCheck),
B(LdaTheHole),
B(Star), R(0),
/* 34 S> */ B(CreateClosure), U8(0), U8(2), U8(2),
B(Star), R(3),
B(LdaTheHole),
B(Star), R(4),
B(LdaSmi), U8(34),
B(Star), R(6),
B(LdaSmi), U8(88),
B(Star), R(7),
B(Mov), R(3), R(5),
B(CallRuntime), U16(Runtime::kDefineClass), R(4), U8(4),
B(Star), R(4),
B(CallRuntime), U16(Runtime::kInstallClassNameAccessor), R(3), U8(1),
B(CallRuntime), U16(Runtime::kToFastProperties), R(3), U8(1),
B(Star), R(0),
B(Star), R(1),
B(Star), R(2),
/* 89 S> */ B(CreateArrayLiteral), U8(1), U8(0), U8(9),
B(Star), R(4),
B(Ldar), R(2),
/* 89 E> */ B(NewWithSpread), R(2), R(4), U8(1),
B(LdaUndefined),
/* 110 S> */ B(Return),
]
constant pool: [
SHARED_FUNCTION_INFO_TYPE,
CONSTANT_ELEMENTS_PAIR_TYPE,
]
handlers: [
]
---
snippet: "
class A { constructor(...args) { this.args = args; } }
new A(0, ...[1, 2, 3]);
"
frame size: 8
parameter count: 1
bytecode array length: 67
bytecodes: [
B(LdaTheHole),
B(Star), R(2),
/* 30 E> */ B(StackCheck),
B(LdaTheHole),
B(Star), R(0),
/* 34 S> */ B(CreateClosure), U8(0), U8(2), U8(2),
B(Star), R(3),
B(LdaTheHole),
B(Star), R(4),
B(LdaSmi), U8(34),
B(Star), R(6),
B(LdaSmi), U8(88),
B(Star), R(7),
B(Mov), R(3), R(5),
B(CallRuntime), U16(Runtime::kDefineClass), R(4), U8(4),
B(Star), R(4),
B(CallRuntime), U16(Runtime::kInstallClassNameAccessor), R(3), U8(1),
B(CallRuntime), U16(Runtime::kToFastProperties), R(3), U8(1),
B(Star), R(0),
B(Star), R(1),
B(Star), R(2),
/* 89 S> */ B(LdaZero),
B(Star), R(4),
B(CreateArrayLiteral), U8(1), U8(0), U8(9),
B(Star), R(5),
B(Ldar), R(2),
/* 89 E> */ B(NewWithSpread), R(2), R(4), U8(2),
B(LdaUndefined),
/* 113 S> */ B(Return),
]
constant pool: [
SHARED_FUNCTION_INFO_TYPE,
CONSTANT_ELEMENTS_PAIR_TYPE,
]
handlers: [
]
---
snippet: "
class A { constructor(...args) { this.args = args; } }
new A(0, ...[1, 2, 3], 4);
"
frame size: 9
parameter count: 1
bytecode array length: 98
bytecodes: [
B(LdaTheHole),
B(Star), R(2),
/* 30 E> */ B(StackCheck),
B(LdaTheHole),
B(Star), R(0),
/* 34 S> */ B(CreateClosure), U8(0), U8(2), U8(2),
B(Star), R(3),
B(LdaTheHole),
B(Star), R(4),
B(LdaSmi), U8(34),
B(Star), R(6),
B(LdaSmi), U8(88),
B(Star), R(7),
B(Mov), R(3), R(5),
B(CallRuntime), U16(Runtime::kDefineClass), R(4), U8(4),
B(Star), R(4),
B(CallRuntime), U16(Runtime::kInstallClassNameAccessor), R(3), U8(1),
B(CallRuntime), U16(Runtime::kToFastProperties), R(3), U8(1),
B(Star), R(0),
B(Star), R(1),
B(Star), R(2),
/* 89 S> */ B(LdaUndefined),
B(Star), R(3),
B(LdaUndefined),
B(Star), R(5),
/* 93 E> */ B(CreateArrayLiteral), U8(1), U8(1), U8(9),
B(Star), R(6),
B(LdaUndefined),
B(Star), R(7),
B(CreateArrayLiteral), U8(2), U8(0), U8(9),
B(Star), R(8),
B(CallJSRuntime), U8(%spread_iterable), R(7), U8(2),
B(Star), R(7),
B(CreateArrayLiteral), U8(3), U8(2), U8(9),
B(Star), R(8),
B(CallJSRuntime), U8(%spread_arguments), R(5), U8(4),
B(Star), R(5),
B(Mov), R(1), R(4),
B(CallJSRuntime), U8(%reflect_construct), R(3), U8(3),
B(LdaUndefined),
/* 116 S> */ B(Return),
]
constant pool: [
SHARED_FUNCTION_INFO_TYPE,
CONSTANT_ELEMENTS_PAIR_TYPE,
CONSTANT_ELEMENTS_PAIR_TYPE,
CONSTANT_ELEMENTS_PAIR_TYPE,
]
handlers: [
]
......@@ -2343,6 +2343,34 @@ TEST(SuperCallAndSpread) {
LoadGolden("SuperCallAndSpread.golden")));
}
TEST(CallAndSpread) {
InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate());
const char* snippets[] = {"Math.max(...[1, 2, 3]);\n",
"Math.max(0, ...[1, 2, 3]);\n",
"Math.max(0, ...[1, 2, 3], 4);\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("CallAndSpread.golden")));
}
TEST(NewAndSpread) {
InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate());
const char* snippets[] = {
"class A { constructor(...args) { this.args = args; } }\n"
"new A(...[1, 2, 3]);\n",
"class A { constructor(...args) { this.args = args; } }\n"
"new A(0, ...[1, 2, 3]);\n",
"class A { constructor(...args) { this.args = args; } }\n"
"new A(0, ...[1, 2, 3], 4);\n"};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("NewAndSpread.golden")));
}
} // namespace interpreter
} // namespace internal
} // namespace v8
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --es-staging
class BaseClass {
method() {
return 1;
}
}
class SubClass extends BaseClass {
method(...args) {
return super.method(...args);
}
}
var a = new SubClass();
a.method();
......@@ -138,7 +138,8 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CallRuntime(Runtime::kIsArray, reg)
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg_list, pair)
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg_list)
.NewWithSpread(reg, reg_list);
.NewWithSpread(reg, reg_list)
.CallWithSpread(reg_list);
// Emit binary operator invocations.
builder.BinaryOperation(Token::Value::ADD, reg, 1)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment