Commit 42049b43 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[interpreter] Move desugaring of spread super call to bytecode generator

This patch moves the desugaring from the parser to the bytecode
generator for super calls that have a spread at a non last position.

This allows us to have the post super() call behavior, such as
initializing instance fields in one place in VisitCallSuper.

Bug: v8:7642
Change-Id: I00a693beb7078a63282359c1121b66bb62c157c8
Reviewed-on: https://chromium-review.googlesource.com/1009907
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52596}
parent 2af0c316
...@@ -2303,6 +2303,8 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { ...@@ -2303,6 +2303,8 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
void BytecodeGenerator::BuildArrayLiteralElementsInsertion( void BytecodeGenerator::BuildArrayLiteralElementsInsertion(
Register array, int first_spread_index, ZoneList<Expression*>* elements, Register array, int first_spread_index, ZoneList<Expression*>* elements,
bool skip_constants) { bool skip_constants) {
DCHECK_LT(first_spread_index, elements->length());
Register index = register_allocator()->NewRegister(); Register index = register_allocator()->NewRegister();
int array_index = 0; int array_index = 0;
...@@ -3575,6 +3577,12 @@ void BytecodeGenerator::VisitCall(Call* expr) { ...@@ -3575,6 +3577,12 @@ void BytecodeGenerator::VisitCall(Call* expr) {
void BytecodeGenerator::VisitCallSuper(Call* expr) { void BytecodeGenerator::VisitCallSuper(Call* expr) {
RegisterAllocationScope register_scope(this); RegisterAllocationScope register_scope(this);
SuperCallReference* super = expr->expression()->AsSuperCallReference(); SuperCallReference* super = expr->expression()->AsSuperCallReference();
ZoneList<Expression*>* args = expr->arguments();
int first_spread_index = 0;
for (; first_spread_index < args->length(); first_spread_index++) {
if (args->at(first_spread_index)->IsSpread()) break;
}
// Prepare the constructor to the super call. // Prepare the constructor to the super call.
Register this_function = VisitForRegisterValue(super->this_function_var()); Register this_function = VisitForRegisterValue(super->this_function_var());
...@@ -3583,29 +3591,52 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) { ...@@ -3583,29 +3591,52 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
->LoadAccumulatorWithRegister(this_function) ->LoadAccumulatorWithRegister(this_function)
.GetSuperConstructor(constructor); .GetSuperConstructor(constructor);
ZoneList<Expression*>* args = expr->arguments(); if (first_spread_index < expr->arguments()->length() - 1) {
RegisterList args_regs = register_allocator()->NewGrowableRegisterList(); // We rewrite something like
VisitArguments(args, &args_regs); // super(1, ...x, 2)
// The new target is loaded into the accumulator from the // to
// {new.target} variable. // %reflect_construct(constructor, [1, ...x, 2], new_target)
VisitForAccumulatorValue(super->new_target_var()); // That is, we implement (non-last-arg) spreads in super calls via our
builder()->SetExpressionPosition(expr); // mechanism for spreads in array literals.
// First generate the array containing all arguments.
Register array = register_allocator()->NewRegister();
int literal_index = feedback_index(feedback_spec()->AddLiteralSlot());
builder()
->CreateEmptyArrayLiteral(literal_index)
.StoreAccumulatorInRegister(array);
BuildArrayLiteralElementsInsertion(array, first_spread_index, args, false);
// Now pass that array to %reflect_construct.
RegisterList construct_args = register_allocator()->NewRegisterList(3);
builder()->MoveRegister(constructor, construct_args[0]);
builder()->MoveRegister(array, construct_args[1]);
VisitForRegisterValue(super->new_target_var(), construct_args[2]);
builder()->CallJSRuntime(Context::REFLECT_CONSTRUCT_INDEX, construct_args);
} else {
RegisterList args_regs = register_allocator()->NewGrowableRegisterList();
VisitArguments(args, &args_regs);
// The new target is loaded into the accumulator from the
// {new.target} variable.
VisitForAccumulatorValue(super->new_target_var());
builder()->SetExpressionPosition(expr);
int feedback_slot_index = feedback_index(feedback_spec()->AddCallICSlot()); int feedback_slot_index = feedback_index(feedback_spec()->AddCallICSlot());
// When a super call contains a spread, a CallSuper AST node is only created if (first_spread_index == expr->arguments()->length() - 1) {
// if there is exactly one spread, and it is the last argument. builder()->ConstructWithSpread(constructor, args_regs,
if (expr->only_last_arg_is_spread()) { feedback_slot_index);
builder()->ConstructWithSpread(constructor, args_regs, feedback_slot_index); } else {
} else { DCHECK_EQ(first_spread_index, expr->arguments()->length());
// Call construct. // Call construct.
// TODO(turbofan): For now we do gather feedback on super constructor // TODO(turbofan): For now we do gather feedback on super constructor
// calls, utilizing the existing machinery to inline the actual call // calls, utilizing the existing machinery to inline the actual call
// target and the JSCreate for the implicit receiver allocation. This // target and the JSCreate for the implicit receiver allocation. This
// is not an ideal solution for super constructor calls, but it gets // is not an ideal solution for super constructor calls, but it gets
// the job done for now. In the long run we might want to revisit this // the job done for now. In the long run we might want to revisit this
// and come up with a better way. // and come up with a better way.
builder()->Construct(constructor, args_regs, feedback_slot_index); builder()->Construct(constructor, args_regs, feedback_slot_index);
}
} }
// The derived constructor has the correct bit set always, so we // The derived constructor has the correct bit set always, so we
......
...@@ -3543,52 +3543,35 @@ Expression* Parser::SpreadCall(Expression* function, ...@@ -3543,52 +3543,35 @@ Expression* Parser::SpreadCall(Expression* function,
ZoneList<Expression*>* args_list, int pos, ZoneList<Expression*>* args_list, int pos,
Call::PossiblyEval is_possibly_eval) { Call::PossiblyEval is_possibly_eval) {
// Handle this case in BytecodeGenerator. // Handle this case in BytecodeGenerator.
if (OnlyLastArgIsSpread(args_list)) { if (OnlyLastArgIsSpread(args_list) || function->IsSuperCallReference()) {
return factory()->NewCall(function, args_list, pos); return factory()->NewCall(function, args_list, pos);
} }
if (function->IsSuperCallReference()) { ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(3, zone());
// Super calls if (function->IsProperty()) {
// $super_constructor = %_GetSuperConstructor(<this-function>) // Method calls
// %reflect_construct($super_constructor, args, new.target) if (function->AsProperty()->IsSuperAccess()) {
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(3, zone()); Expression* home = ThisExpression(kNoSourcePosition);
ZoneList<Expression*>* tmp = new (zone()) ZoneList<Expression*>(1, zone()); args->Add(function, zone());
tmp->Add(function->AsSuperCallReference()->this_function_var(), zone()); args->Add(home, zone());
args->Add(factory()->NewCallRuntime(Runtime::kInlineGetSuperConstructor,
tmp, pos),
zone());
args->Add(ArrayLiteralFromListWithSpread(args_list), zone());
args->Add(function->AsSuperCallReference()->new_target_var(), zone());
return factory()->NewCallRuntime(Context::REFLECT_CONSTRUCT_INDEX, args,
pos);
} else {
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(3, zone());
if (function->IsProperty()) {
// Method calls
if (function->AsProperty()->IsSuperAccess()) {
Expression* home = ThisExpression(kNoSourcePosition);
args->Add(function, zone());
args->Add(home, zone());
} else {
Variable* temp = NewTemporary(ast_value_factory()->empty_string());
VariableProxy* obj = factory()->NewVariableProxy(temp);
Assignment* assign_obj = factory()->NewAssignment(
Token::ASSIGN, obj, function->AsProperty()->obj(),
kNoSourcePosition);
function = factory()->NewProperty(
assign_obj, function->AsProperty()->key(), kNoSourcePosition);
args->Add(function, zone());
obj = factory()->NewVariableProxy(temp);
args->Add(obj, zone());
}
} else { } else {
// Non-method calls Variable* temp = NewTemporary(ast_value_factory()->empty_string());
VariableProxy* obj = factory()->NewVariableProxy(temp);
Assignment* assign_obj = factory()->NewAssignment(
Token::ASSIGN, obj, function->AsProperty()->obj(), kNoSourcePosition);
function = factory()->NewProperty(
assign_obj, function->AsProperty()->key(), kNoSourcePosition);
args->Add(function, zone()); args->Add(function, zone());
args->Add(factory()->NewUndefinedLiteral(kNoSourcePosition), zone()); obj = factory()->NewVariableProxy(temp);
args->Add(obj, zone());
} }
args->Add(ArrayLiteralFromListWithSpread(args_list), zone()); } else {
return factory()->NewCallRuntime(Context::REFLECT_APPLY_INDEX, args, pos); // Non-method calls
args->Add(function, zone());
args->Add(factory()->NewUndefinedLiteral(kNoSourcePosition), zone());
} }
args->Add(ArrayLiteralFromListWithSpread(args_list), zone());
return factory()->NewCallRuntime(Context::REFLECT_APPLY_INDEX, args, pos);
} }
Expression* Parser::SpreadCallNew(Expression* function, Expression* Parser::SpreadCallNew(Expression* function,
......
...@@ -91,50 +91,57 @@ snippet: " ...@@ -91,50 +91,57 @@ snippet: "
test = new B(1, 2, 3).constructor; test = new B(1, 2, 3).constructor;
})(); })();
" "
frame size: 12 frame size: 13
parameter count: 1 parameter count: 1
bytecode array length: 124 bytecode array length: 137
bytecodes: [ bytecodes: [
B(CreateRestParameter), B(CreateRestParameter),
B(Star), R(2), B(Star), R(2),
B(Mov), R(closure), R(1), B(Mov), R(closure), R(1),
/* 128 E> */ B(StackCheck), /* 128 E> */ B(StackCheck),
B(Mov), R(2), R(3), B(Mov), R(2), R(3),
/* 140 S> */ B(CallRuntime), U16(Runtime::k_GetSuperConstructor), R(closure), U8(1), /* 140 S> */ B(Ldar), R(closure),
B(Star), R(4), B(GetSuperConstructor), R(5),
B(CreateArrayLiteral), U8(0), U8(0), U8(37), B(CreateEmptyArrayLiteral), U8(0),
B(Star), R(5), B(Star), R(6),
B(LdaConstant), U8(1), B(LdaZero),
/* 152 S> */ B(Star), R(6), B(Star), R(7),
B(LdaNamedProperty), R(2), U8(2), U8(6), B(LdaSmi), I8(1),
B(Star), R(11), B(StaKeyedProperty), R(6), R(7), U8(1),
B(CallProperty0), R(11), R(2), U8(8), B(LdaConstant), U8(0),
B(Mov), R(2), R(10), /* 152 S> */ B(Star), R(7),
B(LdaNamedProperty), R(2), U8(1), U8(8),
B(Star), R(12),
B(CallProperty0), R(12), R(2), U8(10),
B(Mov), R(2), R(11),
B(Mov), R(1), R(4),
B(JumpIfJSReceiver), U8(7), B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0), B(CallRuntime), U16(Runtime::kThrowSymbolIteratorInvalid), R(0), U8(0),
B(Star), R(10),
B(LdaNamedProperty), R(10), U8(2), U8(12),
B(Star), R(9), B(Star), R(9),
B(LdaNamedProperty), R(9), U8(3), U8(10), B(CallProperty0), R(9), R(10), U8(14),
B(Star), R(8), B(Star), R(8),
B(CallProperty0), R(8), R(9), U8(12),
B(Star), R(7),
B(JumpIfJSReceiver), U8(7), B(JumpIfJSReceiver), U8(7),
B(CallRuntime), U16(Runtime::kThrowIteratorResultNotAnObject), R(7), U8(1), B(CallRuntime), U16(Runtime::kThrowIteratorResultNotAnObject), R(8), U8(1),
B(LdaNamedProperty), R(7), U8(4), U8(14), B(LdaNamedProperty), R(8), U8(3), U8(16),
B(JumpIfToBooleanTrue), U8(21), B(JumpIfToBooleanTrue), U8(21),
B(LdaNamedProperty), R(7), U8(5), U8(16), B(LdaNamedProperty), R(8), U8(4), U8(18),
B(Star), R(8),
B(StaInArrayLiteral), R(6), R(7), U8(3),
B(Ldar), R(7),
B(Inc), U8(5),
B(Star), R(7), B(Star), R(7),
B(StaInArrayLiteral), R(5), R(6), U8(1),
B(Ldar), R(6),
B(Inc), U8(3),
B(Star), R(6),
B(JumpLoop), U8(35), I8(0), B(JumpLoop), U8(35), I8(0),
B(LdaSmi), I8(1), B(LdaSmi), I8(1),
B(StaInArrayLiteral), R(5), R(6), U8(1), B(StaInArrayLiteral), R(6), R(7), U8(3),
B(Ldar), R(6), B(Ldar), R(7),
B(Inc), U8(3), B(Inc), U8(5),
B(Star), R(6), B(Star), R(7),
B(Mov), R(0), R(6), B(Mov), R(5), R(8),
/* 140 E> */ B(CallJSRuntime), U8(%reflect_construct), R(4), U8(3), B(Mov), R(6), R(9),
B(Mov), R(0), R(10),
/* 140 E> */ B(CallJSRuntime), U8(%reflect_construct), R(8), U8(3),
B(Star), R(4), B(Star), R(4),
B(Ldar), R(this), B(Ldar), R(this),
/* 140 E> */ B(ThrowSuperAlreadyCalledIfNotHole), /* 140 E> */ B(ThrowSuperAlreadyCalledIfNotHole),
...@@ -144,7 +151,6 @@ bytecodes: [ ...@@ -144,7 +151,6 @@ bytecodes: [
/* 162 S> */ B(Return), /* 162 S> */ B(Return),
] ]
constant pool: [ constant pool: [
TUPLE2_TYPE,
Smi [1], Smi [1],
SYMBOL_TYPE, SYMBOL_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["next"], ONE_BYTE_INTERNALIZED_STRING_TYPE ["next"],
......
// Copyright 2018 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: --harmony-public-fields
const a = [2];
const b = [4];
let log;
class C {
constructor(...args) {
log = args;
}
}
class D extends C {
field = 42;
constructor() { super(1) };
}
assertEquals(42, (new D).field);
assertEquals([1], log);
class E extends C {
field = 42;
constructor() { super(...a) };
}
assertEquals(42, (new E).field);
assertEquals([2], log);
class F extends C {
field = 42;
constructor() { super(1, ...a) };
}
assertEquals(42, (new F).field);
assertEquals([1, 2], log);
class G extends C {
field = 42;
constructor() { super(1, ...a, 3) };
}
assertEquals(42, (new G).field);
assertEquals([1, 2, 3], log);
class H extends C {
field = 42;
constructor() { super(1, ...a, 3, ...b) };
}
assertEquals(42, (new H).field);
assertEquals([1, 2, 3, 4], log);
class I extends C {
field = 42;
constructor() { super(1, ...a, 3, ...b, 5) };
}
assertEquals(42, (new I).field);
assertEquals([1, 2, 3, 4, 5], log);
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