Revert of [Ignition/turbo] Add a CallWithSpread bytecode. (patchset #10 id:170001 of https://codereview.chromium.org/2629363002/ )
Reason for revert:
Causes a few bugs caught by clusterfuzz.
Original issue's description:
> [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-Commit-Position: refs/heads/master@{#42455}
> Committed: 4bae43471d
TBR=bmeurer@chromium.org,rmcilroy@chromium.org,verwaest@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:5511
Review-Url: https://codereview.chromium.org/2642843002
Cr-Commit-Position: refs/heads/master@{#42470}
This commit is contained in:
parent
51740cc16a
commit
75b861210f
@ -409,8 +409,8 @@ void AstNumberingVisitor::VisitCompareOperation(CompareOperation* node) {
|
|||||||
|
|
||||||
void AstNumberingVisitor::VisitSpread(Spread* node) {
|
void AstNumberingVisitor::VisitSpread(Spread* node) {
|
||||||
IncrementNodeCount();
|
IncrementNodeCount();
|
||||||
// We can only get here from spread calls currently.
|
// We can only get here from super calls currently.
|
||||||
DisableFullCodegenAndCrankshaft(kSpreadCall);
|
DisableFullCodegenAndCrankshaft(kSuperReference);
|
||||||
node->set_base_id(ReserveIdRange(Spread::num_ids()));
|
node->set_base_id(ReserveIdRange(Spread::num_ids()));
|
||||||
Visit(node->expression());
|
Visit(node->expression());
|
||||||
}
|
}
|
||||||
|
@ -1900,10 +1900,6 @@ class Call final : public Expression {
|
|||||||
}
|
}
|
||||||
void MarkTail() { bit_field_ = IsTailField::update(bit_field_, true); }
|
void MarkTail() { bit_field_ = IsTailField::update(bit_field_, true); }
|
||||||
|
|
||||||
bool only_last_arg_is_spread() {
|
|
||||||
return !arguments_->is_empty() && arguments_->last()->IsSpread();
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CallType {
|
enum CallType {
|
||||||
GLOBAL_CALL,
|
GLOBAL_CALL,
|
||||||
WITH_CALL,
|
WITH_CALL,
|
||||||
@ -2003,10 +1999,6 @@ class CallNew final : public Expression {
|
|||||||
set_is_monomorphic(true);
|
set_is_monomorphic(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool only_last_arg_is_spread() {
|
|
||||||
return !arguments_->is_empty() && arguments_->last()->IsSpread();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class AstNodeFactory;
|
friend class AstNodeFactory;
|
||||||
|
|
||||||
|
@ -185,7 +185,6 @@ namespace internal {
|
|||||||
"Sloppy function expects JSReceiver as receiver.") \
|
"Sloppy function expects JSReceiver as receiver.") \
|
||||||
V(kSmiAdditionOverflow, "Smi addition overflow") \
|
V(kSmiAdditionOverflow, "Smi addition overflow") \
|
||||||
V(kSmiSubtractionOverflow, "Smi subtraction overflow") \
|
V(kSmiSubtractionOverflow, "Smi subtraction overflow") \
|
||||||
V(kSpreadCall, "Call with spread argument") \
|
|
||||||
V(kStackAccessBelowStackPointer, "Stack access below stack pointer") \
|
V(kStackAccessBelowStackPointer, "Stack access below stack pointer") \
|
||||||
V(kStackFrameTypesMustMatch, "Stack frame types must match") \
|
V(kStackFrameTypesMustMatch, "Stack frame types must match") \
|
||||||
V(kSuperReference, "Super reference") \
|
V(kSuperReference, "Super reference") \
|
||||||
|
@ -1353,16 +1353,6 @@ Node* BytecodeGraphBuilder::ProcessCallNewWithSpreadArguments(
|
|||||||
return value;
|
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() {
|
void BytecodeGraphBuilder::VisitNewWithSpread() {
|
||||||
PrepareEagerCheckpoint();
|
PrepareEagerCheckpoint();
|
||||||
interpreter::Register callee_reg = bytecode_iterator().GetRegisterOperand(0);
|
interpreter::Register callee_reg = bytecode_iterator().GetRegisterOperand(0);
|
||||||
|
@ -891,11 +891,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CallWithSpread(RegisterList args) {
|
|
||||||
OutputCallWithSpread(args, args.register_count());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
|
BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
|
||||||
RegisterList args,
|
RegisterList args,
|
||||||
int feedback_slot_id) {
|
int feedback_slot_id) {
|
||||||
|
@ -213,11 +213,6 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
|
|||||||
Call::CallType call_type,
|
Call::CallType call_type,
|
||||||
TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
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|.
|
// Call the new operator. The accumulator holds the |new_target|.
|
||||||
// The |constructor| is in a register and arguments are in |args|.
|
// The |constructor| is in a register and arguments are in |args|.
|
||||||
BytecodeArrayBuilder& New(Register constructor, RegisterList args,
|
BytecodeArrayBuilder& New(Register constructor, RegisterList args,
|
||||||
|
@ -2445,27 +2445,12 @@ void BytecodeGenerator::VisitCall(Call* expr) {
|
|||||||
return VisitCallSuper(expr);
|
return VisitCallSuper(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Register callee = register_allocator()->NewRegister();
|
||||||
// Grow the args list as we visit receiver / arguments to avoid allocating all
|
// Grow the args list as we visit receiver / arguments to avoid allocating all
|
||||||
// the registers up-front. Otherwise these registers are unavailable during
|
// the registers up-front. Otherwise these registers are unavailable during
|
||||||
// receiver / argument visiting and we can end up with memory leaks due to
|
// receiver / argument visiting and we can end up with memory leaks due to
|
||||||
// registers keeping objects alive.
|
// registers keeping objects alive.
|
||||||
RegisterList args;
|
RegisterList args = register_allocator()->NewGrowableRegisterList();
|
||||||
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
|
// Prepare the callee and the receiver to the function call. This depends on
|
||||||
// the semantics of the underlying call type.
|
// the semantics of the underlying call type.
|
||||||
@ -2474,7 +2459,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
|
|||||||
case Call::KEYED_PROPERTY_CALL: {
|
case Call::KEYED_PROPERTY_CALL: {
|
||||||
Property* property = callee_expr->AsProperty();
|
Property* property = callee_expr->AsProperty();
|
||||||
VisitAndPushIntoRegisterList(property->obj(), &args);
|
VisitAndPushIntoRegisterList(property->obj(), &args);
|
||||||
VisitPropertyLoadForRegister(args.last_register(), property, callee);
|
VisitPropertyLoadForRegister(args[0], property, callee);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Call::GLOBAL_CALL: {
|
case Call::GLOBAL_CALL: {
|
||||||
@ -2535,11 +2520,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
|
|||||||
// Evaluate all arguments to the function call and store in sequential args
|
// Evaluate all arguments to the function call and store in sequential args
|
||||||
// registers.
|
// registers.
|
||||||
VisitArguments(expr->arguments(), &args);
|
VisitArguments(expr->arguments(), &args);
|
||||||
// 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());
|
CHECK_EQ(expr->arguments()->length() + 1, args.register_count());
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve callee for a potential direct eval call. This block will mutate the
|
// Resolve callee for a potential direct eval call. This block will mutate the
|
||||||
// callee value.
|
// callee value.
|
||||||
@ -2569,17 +2550,9 @@ void BytecodeGenerator::VisitCall(Call* expr) {
|
|||||||
|
|
||||||
builder()->SetExpressionPosition(expr);
|
builder()->SetExpressionPosition(expr);
|
||||||
|
|
||||||
// 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());
|
int const feedback_slot_index = feedback_index(expr->CallFeedbackICSlot());
|
||||||
builder()->Call(callee, args, feedback_slot_index, call_type,
|
builder()->Call(callee, args, feedback_slot_index, call_type,
|
||||||
expr->tail_call_mode());
|
expr->tail_call_mode());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BytecodeGenerator::VisitCallSuper(Call* expr) {
|
void BytecodeGenerator::VisitCallSuper(Call* expr) {
|
||||||
@ -2601,7 +2574,7 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
|
|||||||
|
|
||||||
// When a super call contains a spread, a CallSuper AST node is only created
|
// 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 there is exactly one spread, and it is the last argument.
|
||||||
if (expr->only_last_arg_is_spread()) {
|
if (!args->is_empty() && args->last()->IsSpread()) {
|
||||||
// TODO(petermarshall): Collect type on the feedback slot.
|
// TODO(petermarshall): Collect type on the feedback slot.
|
||||||
builder()->NewWithSpread(constructor, args_regs);
|
builder()->NewWithSpread(constructor, args_regs);
|
||||||
} else {
|
} else {
|
||||||
@ -2622,18 +2595,12 @@ void BytecodeGenerator::VisitCallNew(CallNew* expr) {
|
|||||||
RegisterList args = register_allocator()->NewGrowableRegisterList();
|
RegisterList args = register_allocator()->NewGrowableRegisterList();
|
||||||
VisitArguments(expr->arguments(), &args);
|
VisitArguments(expr->arguments(), &args);
|
||||||
|
|
||||||
|
builder()->SetExpressionPosition(expr);
|
||||||
// The accumulator holds new target which is the same as the
|
// The accumulator holds new target which is the same as the
|
||||||
// constructor for CallNew.
|
// constructor for CallNew.
|
||||||
builder()->SetExpressionPosition(expr);
|
builder()
|
||||||
builder()->LoadAccumulatorWithRegister(constructor);
|
->LoadAccumulatorWithRegister(constructor)
|
||||||
|
.New(constructor, args, feedback_index(expr->CallNewFeedbackSlot()));
|
||||||
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) {
|
void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
|
||||||
|
@ -151,8 +151,6 @@ namespace interpreter {
|
|||||||
OperandType::kRegCount, OperandType::kIdx) \
|
OperandType::kRegCount, OperandType::kIdx) \
|
||||||
V(CallProperty, AccumulatorUse::kWrite, OperandType::kReg, \
|
V(CallProperty, AccumulatorUse::kWrite, OperandType::kReg, \
|
||||||
OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \
|
OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \
|
||||||
V(CallWithSpread, AccumulatorUse::kWrite, OperandType::kRegList, \
|
|
||||||
OperandType::kRegCount) \
|
|
||||||
V(TailCall, AccumulatorUse::kWrite, OperandType::kReg, \
|
V(TailCall, AccumulatorUse::kWrite, OperandType::kReg, \
|
||||||
OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \
|
OperandType::kRegList, OperandType::kRegCount, OperandType::kIdx) \
|
||||||
V(CallRuntime, AccumulatorUse::kWrite, OperandType::kRuntimeId, \
|
V(CallRuntime, AccumulatorUse::kWrite, OperandType::kRuntimeId, \
|
||||||
|
@ -2176,27 +2176,7 @@ void Interpreter::DoCallJSRuntime(InterpreterAssembler* assembler) {
|
|||||||
__ Dispatch();
|
__ Dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallWithSpread <first_arg> <arg_count>
|
// NewWithSpread <constructor> <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
|
// Call the constructor in |constructor| with the first argument in register
|
||||||
// |first_arg| and |arg_count| arguments in subsequent registers. The final
|
// |first_arg| and |arg_count| arguments in subsequent registers. The final
|
||||||
|
@ -2597,7 +2597,6 @@ typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments(
|
|||||||
bool done = (peek() == Token::RPAREN);
|
bool done = (peek() == Token::RPAREN);
|
||||||
bool was_unspread = false;
|
bool was_unspread = false;
|
||||||
int unspread_sequences_count = 0;
|
int unspread_sequences_count = 0;
|
||||||
int spread_count = 0;
|
|
||||||
while (!done) {
|
while (!done) {
|
||||||
int start_pos = peek_position();
|
int start_pos = peek_position();
|
||||||
bool is_spread = Check(Token::ELLIPSIS);
|
bool is_spread = Check(Token::ELLIPSIS);
|
||||||
@ -2621,7 +2620,6 @@ typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments(
|
|||||||
// are not prefixed with a spread '...' operator.
|
// are not prefixed with a spread '...' operator.
|
||||||
if (is_spread) {
|
if (is_spread) {
|
||||||
was_unspread = false;
|
was_unspread = false;
|
||||||
spread_count++;
|
|
||||||
} else if (!was_unspread) {
|
} else if (!was_unspread) {
|
||||||
was_unspread = true;
|
was_unspread = true;
|
||||||
unspread_sequences_count++;
|
unspread_sequences_count++;
|
||||||
@ -2657,13 +2655,9 @@ typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments(
|
|||||||
// Unspread parameter sequences are translated into array literals in the
|
// Unspread parameter sequences are translated into array literals in the
|
||||||
// parser. Ensure that the number of materialized literals matches between
|
// parser. Ensure that the number of materialized literals matches between
|
||||||
// the parser and preparser
|
// the parser and preparser
|
||||||
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);
|
impl()->MaterializeUnspreadArgumentsLiterals(unspread_sequences_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -3203,7 +3197,7 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) {
|
|||||||
|
|
||||||
bool is_super_call = result->IsSuperCallReference();
|
bool is_super_call = result->IsSuperCallReference();
|
||||||
if (spread_pos.IsValid()) {
|
if (spread_pos.IsValid()) {
|
||||||
result = impl()->SpreadCall(result, args, pos, is_possibly_eval);
|
result = impl()->SpreadCall(result, args, pos);
|
||||||
} else {
|
} else {
|
||||||
result = factory()->NewCall(result, args, pos, is_possibly_eval);
|
result = factory()->NewCall(result, args, pos, is_possibly_eval);
|
||||||
}
|
}
|
||||||
|
@ -3682,27 +3682,24 @@ uint32_t Parser::ComputeTemplateLiteralHash(const TemplateLiteral* lit) {
|
|||||||
return running_hash;
|
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*>* Parser::PrepareSpreadArguments(
|
||||||
ZoneList<Expression*>* list) {
|
ZoneList<Expression*>* list) {
|
||||||
// Here we only deal with multiple arguments where the spread is not at the
|
|
||||||
// end, or there are multiple spreads.
|
|
||||||
DCHECK_GT(list->length(), 1);
|
|
||||||
DCHECK(!OnlyLastArgIsSpread(list));
|
|
||||||
|
|
||||||
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
|
||||||
|
if (list->length() == 1) {
|
||||||
|
// Spread-call with single spread argument produces an InternalArray
|
||||||
|
// containing the values from the array.
|
||||||
|
//
|
||||||
|
// Function is called or constructed with the produced array of arguments
|
||||||
|
//
|
||||||
|
// EG: Apply(Func, Spread(spread0))
|
||||||
|
ZoneList<Expression*>* spread_list =
|
||||||
|
new (zone()) ZoneList<Expression*>(0, zone());
|
||||||
|
spread_list->Add(list->at(0)->AsSpread()->expression(), zone());
|
||||||
|
args->Add(factory()->NewCallRuntime(Runtime::kSpreadIterablePrepare,
|
||||||
|
spread_list, kNoSourcePosition),
|
||||||
|
zone());
|
||||||
|
return args;
|
||||||
|
} else {
|
||||||
// Spread-call with multiple arguments produces array literals for each
|
// Spread-call with multiple arguments produces array literals for each
|
||||||
// sequences of unspread arguments, and converts each spread iterable to
|
// sequences of unspread arguments, and converts each spread iterable to
|
||||||
// an Internal array. Finally, all of these produced arrays are flattened
|
// an Internal array. Finally, all of these produced arrays are flattened
|
||||||
@ -3743,24 +3740,32 @@ ZoneList<Expression*>* Parser::PrepareSpreadArguments(
|
|||||||
kNoSourcePosition),
|
kNoSourcePosition),
|
||||||
zone());
|
zone());
|
||||||
return list;
|
return list;
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression* Parser::SpreadCall(Expression* function,
|
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.
|
|
||||||
if (OnlyLastArgIsSpread(args)) {
|
|
||||||
if (function->IsSuperCallReference()) {
|
|
||||||
function = NewSuperCallReference(pos);
|
|
||||||
}
|
|
||||||
return factory()->NewCall(function, args, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (function->IsSuperCallReference()) {
|
if (function->IsSuperCallReference()) {
|
||||||
// Super calls
|
// Super calls
|
||||||
// $super_constructor = %_GetSuperConstructor(<this-function>)
|
// $super_constructor = %_GetSuperConstructor(<this-function>)
|
||||||
// %reflect_construct($super_constructor, args, new.target)
|
// %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);
|
args = PrepareSpreadArguments(args);
|
||||||
ZoneList<Expression*>* tmp = new (zone()) ZoneList<Expression*>(1, zone());
|
ZoneList<Expression*>* tmp = new (zone()) ZoneList<Expression*>(1, zone());
|
||||||
tmp->Add(function->AsSuperCallReference()->this_function_var(), zone());
|
tmp->Add(function->AsSuperCallReference()->this_function_var(), zone());
|
||||||
@ -3802,10 +3807,6 @@ Expression* Parser::SpreadCall(Expression* function,
|
|||||||
|
|
||||||
Expression* Parser::SpreadCallNew(Expression* function,
|
Expression* Parser::SpreadCallNew(Expression* function,
|
||||||
ZoneList<Expression*>* args, int pos) {
|
ZoneList<Expression*>* args, int pos) {
|
||||||
if (OnlyLastArgIsSpread(args)) {
|
|
||||||
// Handle in BytecodeGenerator.
|
|
||||||
return factory()->NewCallNew(function, args, pos);
|
|
||||||
}
|
|
||||||
args = PrepareSpreadArguments(args);
|
args = PrepareSpreadArguments(args);
|
||||||
args->InsertAt(0, function, zone());
|
args->InsertAt(0, function, zone());
|
||||||
|
|
||||||
|
@ -591,7 +591,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
|
|||||||
|
|
||||||
ZoneList<Expression*>* PrepareSpreadArguments(ZoneList<Expression*>* list);
|
ZoneList<Expression*>* PrepareSpreadArguments(ZoneList<Expression*>* list);
|
||||||
Expression* SpreadCall(Expression* function, ZoneList<Expression*>* args,
|
Expression* SpreadCall(Expression* function, ZoneList<Expression*>* args,
|
||||||
int pos, Call::PossiblyEval is_possibly_eval);
|
int pos);
|
||||||
Expression* SpreadCallNew(Expression* function, ZoneList<Expression*>* args,
|
Expression* SpreadCallNew(Expression* function, ZoneList<Expression*>* args,
|
||||||
int pos);
|
int pos);
|
||||||
Expression* RewriteSuperCall(Expression* call_expression);
|
Expression* RewriteSuperCall(Expression* call_expression);
|
||||||
|
@ -988,8 +988,7 @@ class PreParser : public ParserBase<PreParser> {
|
|||||||
|
|
||||||
V8_INLINE PreParserExpression SpreadCall(PreParserExpression function,
|
V8_INLINE PreParserExpression SpreadCall(PreParserExpression function,
|
||||||
PreParserExpressionList args,
|
PreParserExpressionList args,
|
||||||
int pos,
|
int pos);
|
||||||
Call::PossiblyEval possibly_eval);
|
|
||||||
V8_INLINE PreParserExpression SpreadCallNew(PreParserExpression function,
|
V8_INLINE PreParserExpression SpreadCallNew(PreParserExpression function,
|
||||||
PreParserExpressionList args,
|
PreParserExpressionList args,
|
||||||
int pos);
|
int pos);
|
||||||
@ -1613,9 +1612,9 @@ class PreParser : public ParserBase<PreParser> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
PreParserExpression PreParser::SpreadCall(PreParserExpression function,
|
PreParserExpression PreParser::SpreadCall(PreParserExpression function,
|
||||||
PreParserExpressionList args, int pos,
|
PreParserExpressionList args,
|
||||||
Call::PossiblyEval possibly_eval) {
|
int pos) {
|
||||||
return factory()->NewCall(function, args, pos, possibly_eval);
|
return factory()->NewCall(function, args, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
PreParserExpression PreParser::SpreadCallNew(PreParserExpression function,
|
PreParserExpression PreParser::SpreadCallNew(PreParserExpression function,
|
||||||
|
@ -459,48 +459,5 @@ RUNTIME_FUNCTION(Runtime_GetSuperConstructor) {
|
|||||||
return prototype;
|
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 internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -91,8 +91,7 @@ namespace internal {
|
|||||||
F(StoreToSuper_Sloppy, 4, 1) \
|
F(StoreToSuper_Sloppy, 4, 1) \
|
||||||
F(StoreKeyedToSuper_Strict, 4, 1) \
|
F(StoreKeyedToSuper_Strict, 4, 1) \
|
||||||
F(StoreKeyedToSuper_Sloppy, 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) \
|
#define FOR_EACH_INTRINSIC_COLLECTIONS(F) \
|
||||||
F(StringGetRawHashField, 1, 1) \
|
F(StringGetRawHashField, 1, 1) \
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
#
|
|
||||||
# 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: [
|
|
||||||
]
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
|||||||
#
|
|
||||||
# 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,34 +2343,6 @@ TEST(SuperCallAndSpread) {
|
|||||||
LoadGolden("SuperCallAndSpread.golden")));
|
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 interpreter
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -138,8 +138,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
|||||||
.CallRuntime(Runtime::kIsArray, reg)
|
.CallRuntime(Runtime::kIsArray, reg)
|
||||||
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg_list, pair)
|
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg_list, pair)
|
||||||
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg_list)
|
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg_list)
|
||||||
.NewWithSpread(reg, reg_list)
|
.NewWithSpread(reg, reg_list);
|
||||||
.CallWithSpread(reg_list);
|
|
||||||
|
|
||||||
// Emit binary operator invocations.
|
// Emit binary operator invocations.
|
||||||
builder.BinaryOperation(Token::Value::ADD, reg, 1)
|
builder.BinaryOperation(Token::Value::ADD, reg, 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user