Skip to content

Commit e3b75cb

Browse files
committed
deps: V8: cherry-pick fd75c97d3f56
Original commit message: [interpreter] Apply Reflect.apply transform in BytecodeGenerator Calls with a spread expression in a non-final position get transformed to calls to Reflect.apply. This transformation is currently done in the parser, which does not compose well with other features (e.g. direct eval checking, optional chaining). Do this transform in the BytecodeGenerator instead. Bug: v8:11573, v8:11558, v8:5690 Change-Id: I56c90a2036fe5b43e0897c57766f666bf72bc3a8 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2765783 Auto-Submit: Shu-yu Guo <[email protected]> Commit-Queue: Leszek Swirski <[email protected]> Reviewed-by: Ross McIlroy <[email protected]> Reviewed-by: Leszek Swirski <[email protected]> Cr-Commit-Position: refs/heads/master@{#73534} Refs: v8/v8@fd75c97 PR-URL: #38455 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent 67bd0ec commit e3b75cb

File tree

11 files changed

+166
-132
lines changed

11 files changed

+166
-132
lines changed

common.gypi

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
# Reset this number to 0 on major V8 upgrades.
3838
# Increment by one for each non-official patch applied to deps/v8.
39-
'v8_embedder_string': '-node.10',
39+
'v8_embedder_string': '-node.11',
4040

4141
##### V8 defaults for Node.js #####
4242

deps/v8/src/ast/ast.cc

+16
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,22 @@ Call::CallType Call::GetCallType() const {
935935
return OTHER_CALL;
936936
}
937937

938+
void Call::ComputeSpreadPosition() {
939+
int arguments_length = arguments_.length();
940+
int first_spread_index = 0;
941+
for (; first_spread_index < arguments_length; first_spread_index++) {
942+
if (arguments_.at(first_spread_index)->IsSpread()) break;
943+
}
944+
SpreadPosition position;
945+
if (first_spread_index == arguments_length - 1) {
946+
position = kHasFinalSpread;
947+
} else {
948+
DCHECK_LT(first_spread_index, arguments_length - 1);
949+
position = kHasNonFinalSpread;
950+
}
951+
bit_field_ |= SpreadPositionField::encode(position);
952+
}
953+
938954
CaseClause::CaseClause(Zone* zone, Expression* label,
939955
const ScopedPtrList<Statement>& statements)
940956
: label_(label), statements_(statements.ToConstVector(), zone) {}

deps/v8/src/ast/ast.h

+19-5
Original file line numberDiff line numberDiff line change
@@ -1635,6 +1635,12 @@ class Call final : public Expression {
16351635
return IsOptionalChainLinkField::decode(bit_field_);
16361636
}
16371637

1638+
enum SpreadPosition { kNoSpread, kHasFinalSpread, kHasNonFinalSpread };
1639+
SpreadPosition spread_position() const {
1640+
return SpreadPositionField::decode(bit_field_);
1641+
}
1642+
1643+
// TODO(syg): Remove this and its users.
16381644
bool only_last_arg_is_spread() {
16391645
return !arguments_.is_empty() && arguments_.last()->IsSpread();
16401646
}
@@ -1669,15 +1675,17 @@ class Call final : public Expression {
16691675
friend Zone;
16701676

16711677
Call(Zone* zone, Expression* expression,
1672-
const ScopedPtrList<Expression>& arguments, int pos,
1678+
const ScopedPtrList<Expression>& arguments, int pos, bool has_spread,
16731679
PossiblyEval possibly_eval, bool optional_chain)
16741680
: Expression(pos, kCall),
16751681
expression_(expression),
16761682
arguments_(arguments.ToConstVector(), zone) {
16771683
bit_field_ |=
16781684
IsPossiblyEvalField::encode(possibly_eval == IS_POSSIBLY_EVAL) |
16791685
IsTaggedTemplateField::encode(false) |
1680-
IsOptionalChainLinkField::encode(optional_chain);
1686+
IsOptionalChainLinkField::encode(optional_chain) |
1687+
SpreadPositionField::encode(kNoSpread);
1688+
if (has_spread) ComputeSpreadPosition();
16811689
}
16821690

16831691
Call(Zone* zone, Expression* expression,
@@ -1688,12 +1696,17 @@ class Call final : public Expression {
16881696
arguments_(arguments.ToConstVector(), zone) {
16891697
bit_field_ |= IsPossiblyEvalField::encode(false) |
16901698
IsTaggedTemplateField::encode(true) |
1691-
IsOptionalChainLinkField::encode(false);
1699+
IsOptionalChainLinkField::encode(false) |
1700+
SpreadPositionField::encode(kNoSpread);
16921701
}
16931702

1703+
// Only valid to be called if there is a spread in arguments_.
1704+
void ComputeSpreadPosition();
1705+
16941706
using IsPossiblyEvalField = Expression::NextBitField<bool, 1>;
16951707
using IsTaggedTemplateField = IsPossiblyEvalField::Next<bool, 1>;
16961708
using IsOptionalChainLinkField = IsTaggedTemplateField::Next<bool, 1>;
1709+
using SpreadPositionField = IsOptionalChainLinkField::Next<SpreadPosition, 2>;
16971710

16981711
Expression* expression_;
16991712
ZonePtrList<Expression> arguments_;
@@ -3064,11 +3077,12 @@ class AstNodeFactory final {
30643077

30653078
Call* NewCall(Expression* expression,
30663079
const ScopedPtrList<Expression>& arguments, int pos,
3080+
bool has_spread,
30673081
Call::PossiblyEval possibly_eval = Call::NOT_EVAL,
30683082
bool optional_chain = false) {
30693083
DCHECK_IMPLIES(possibly_eval == Call::IS_POSSIBLY_EVAL, !optional_chain);
3070-
return zone_->New<Call>(zone_, expression, arguments, pos, possibly_eval,
3071-
optional_chain);
3084+
return zone_->New<Call>(zone_, expression, arguments, pos, has_spread,
3085+
possibly_eval, optional_chain);
30723086
}
30733087

30743088
Call* NewTaggedTemplate(Expression* expression,

deps/v8/src/interpreter/bytecode-generator.cc

+76-30
Original file line numberDiff line numberDiff line change
@@ -3106,6 +3106,8 @@ void BytecodeGenerator::BuildCreateArrayLiteral(
31063106
.StoreAccumulatorInRegister(index);
31073107
}
31083108
} else {
3109+
// TODO(v8:11582): Support allocating boilerplates here.
3110+
31093111
// In other cases, we prepare an empty array to be filled in below.
31103112
DCHECK(!elements->is_empty());
31113113
int literal_index = feedback_index(feedback_spec()->AddLiteralSlot());
@@ -5022,17 +5024,30 @@ void BytecodeGenerator::VisitCall(Call* expr) {
50225024
return VisitCallSuper(expr);
50235025
}
50245026

5027+
// We compile the call differently depending on the presence of spreads and
5028+
// their positions.
5029+
//
5030+
// If there is only one spread and it is the final argument, there is a
5031+
// special CallWithSpread bytecode.
5032+
//
5033+
// If there is a non-final spread, we rewrite calls like
5034+
// callee(1, ...x, 2)
5035+
// to
5036+
// %reflect_apply(callee, receiver, [1, ...x, 2])
5037+
const Call::SpreadPosition spread_position = expr->spread_position();
5038+
50255039
// Grow the args list as we visit receiver / arguments to avoid allocating all
50265040
// the registers up-front. Otherwise these registers are unavailable during
50275041
// receiver / argument visiting and we can end up with memory leaks due to
50285042
// registers keeping objects alive.
5029-
Register callee = register_allocator()->NewRegister();
50305043
RegisterList args = register_allocator()->NewGrowableRegisterList();
50315044

5045+
// The callee is the first register in args for ease of calling %reflect_apply
5046+
// if we have a non-final spread. For all other cases it is popped from args
5047+
// before emitting the call below.
5048+
Register callee = register_allocator()->GrowRegisterList(&args);
5049+
50325050
bool implicit_undefined_receiver = false;
5033-
// When a call contains a spread, a Call AST node is only created if there is
5034-
// exactly one spread, and it is the last argument.
5035-
bool is_spread_call = expr->only_last_arg_is_spread();
50365051
bool optimize_as_one_shot = ShouldOptimizeAsOneShot();
50375052

50385053
// TODO(petermarshall): We have a lot of call bytecodes that are very similar,
@@ -5052,7 +5067,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
50525067
}
50535068
case Call::GLOBAL_CALL: {
50545069
// Receiver is undefined for global calls.
5055-
if (!is_spread_call && !optimize_as_one_shot) {
5070+
if (spread_position == Call::kNoSpread && !optimize_as_one_shot) {
50565071
implicit_undefined_receiver = true;
50575072
} else {
50585073
// TODO(leszeks): There's no special bytecode for tail calls or spread
@@ -5088,7 +5103,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
50885103
}
50895104
case Call::OTHER_CALL: {
50905105
// Receiver is undefined for other calls.
5091-
if (!is_spread_call && !optimize_as_one_shot) {
5106+
if (spread_position == Call::kNoSpread && !optimize_as_one_shot) {
50925107
implicit_undefined_receiver = true;
50935108
} else {
50945109
// TODO(leszeks): There's no special bytecode for tail calls or spread
@@ -5137,25 +5152,51 @@ void BytecodeGenerator::VisitCall(Call* expr) {
51375152
BuildIncrementBlockCoverageCounterIfEnabled(right_range);
51385153
}
51395154

5140-
// Evaluate all arguments to the function call and store in sequential args
5141-
// registers.
5142-
VisitArguments(expr->arguments(), &args);
5143-
int receiver_arg_count = implicit_undefined_receiver ? 0 : 1;
5144-
CHECK_EQ(receiver_arg_count + expr->arguments()->length(),
5145-
args.register_count());
5155+
int receiver_arg_count = -1;
5156+
if (spread_position == Call::kHasNonFinalSpread) {
5157+
// If we're building %reflect_apply, build the array literal and put it in
5158+
// the 3rd argument.
5159+
DCHECK(!implicit_undefined_receiver);
5160+
DCHECK_EQ(args.register_count(), 2);
5161+
BuildCreateArrayLiteral(expr->arguments(), nullptr);
5162+
builder()->StoreAccumulatorInRegister(
5163+
register_allocator()->GrowRegisterList(&args));
5164+
} else {
5165+
// If we're not building %reflect_apply and don't need to build an array
5166+
// literal, pop the callee and evaluate all arguments to the function call
5167+
// and store in sequential args registers.
5168+
args = args.PopLeft();
5169+
VisitArguments(expr->arguments(), &args);
5170+
receiver_arg_count = implicit_undefined_receiver ? 0 : 1;
5171+
CHECK_EQ(receiver_arg_count + expr->arguments()->length(),
5172+
args.register_count());
5173+
}
51465174

51475175
// Resolve callee for a potential direct eval call. This block will mutate the
51485176
// callee value.
51495177
if (expr->is_possibly_eval() && expr->arguments()->length() > 0) {
51505178
RegisterAllocationScope inner_register_scope(this);
5179+
RegisterList runtime_call_args = register_allocator()->NewRegisterList(6);
51515180
// Set up arguments for ResolvePossiblyDirectEval by copying callee, source
51525181
// strings and function closure, and loading language and
51535182
// position.
5154-
Register first_arg = args[receiver_arg_count];
5155-
RegisterList runtime_call_args = register_allocator()->NewRegisterList(6);
5183+
5184+
// Move the first arg.
5185+
if (spread_position == Call::kHasNonFinalSpread) {
5186+
int feedback_slot_index =
5187+
feedback_index(feedback_spec()->AddKeyedLoadICSlot());
5188+
Register args_array = args[2];
5189+
builder()
5190+
->LoadLiteral(Smi::FromInt(0))
5191+
.LoadKeyedProperty(args_array, feedback_slot_index)
5192+
.StoreAccumulatorInRegister(runtime_call_args[1]);
5193+
} else {
5194+
// FIXME(v8:5690): Support final spreads for eval.
5195+
DCHECK_GE(receiver_arg_count, 0);
5196+
builder()->MoveRegister(args[receiver_arg_count], runtime_call_args[1]);
5197+
}
51565198
builder()
51575199
->MoveRegister(callee, runtime_call_args[0])
5158-
.MoveRegister(first_arg, runtime_call_args[1])
51595200
.MoveRegister(Register::function_closure(), runtime_call_args[2])
51605201
.LoadLiteral(Smi::FromEnum(language_mode()))
51615202
.StoreAccumulatorInRegister(runtime_call_args[3])
@@ -5172,10 +5213,12 @@ void BytecodeGenerator::VisitCall(Call* expr) {
51725213

51735214
builder()->SetExpressionPosition(expr);
51745215

5175-
if (is_spread_call) {
5216+
if (spread_position == Call::kHasFinalSpread) {
51765217
DCHECK(!implicit_undefined_receiver);
51775218
builder()->CallWithSpread(callee, args,
51785219
feedback_index(feedback_spec()->AddCallICSlot()));
5220+
} else if (spread_position == Call::kHasNonFinalSpread) {
5221+
builder()->CallJSRuntime(Context::REFLECT_APPLY_INDEX, args);
51795222
} else if (optimize_as_one_shot) {
51805223
DCHECK(!implicit_undefined_receiver);
51815224
builder()->CallNoFeedback(callee, args);
@@ -5198,10 +5241,20 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
51985241
SuperCallReference* super = expr->expression()->AsSuperCallReference();
51995242
const ZonePtrList<Expression>* args = expr->arguments();
52005243

5201-
int first_spread_index = 0;
5202-
for (; first_spread_index < args->length(); first_spread_index++) {
5203-
if (args->at(first_spread_index)->IsSpread()) break;
5204-
}
5244+
// We compile the super call differently depending on the presence of spreads
5245+
// and their positions.
5246+
//
5247+
// If there is only one spread and it is the final argument, there is a
5248+
// special ConstructWithSpread bytecode.
5249+
//
5250+
// It there is a non-final spread, we rewrite something like
5251+
// super(1, ...x, 2)
5252+
// to
5253+
// %reflect_construct(constructor, [1, ...x, 2], new_target)
5254+
//
5255+
// That is, we implement (non-last-arg) spreads in super calls via our
5256+
// mechanism for spreads in array literals.
5257+
const Call::SpreadPosition spread_position = expr->spread_position();
52055258

52065259
// Prepare the constructor to the super call.
52075260
Register this_function = VisitForRegisterValue(super->this_function_var());
@@ -5210,14 +5263,7 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
52105263
->LoadAccumulatorWithRegister(this_function)
52115264
.GetSuperConstructor(constructor);
52125265

5213-
if (first_spread_index < expr->arguments()->length() - 1) {
5214-
// We rewrite something like
5215-
// super(1, ...x, 2)
5216-
// to
5217-
// %reflect_construct(constructor, [1, ...x, 2], new_target)
5218-
// That is, we implement (non-last-arg) spreads in super calls via our
5219-
// mechanism for spreads in array literals.
5220-
5266+
if (spread_position == Call::kHasNonFinalSpread) {
52215267
// First generate the array containing all arguments.
52225268
BuildCreateArrayLiteral(args, nullptr);
52235269

@@ -5244,11 +5290,11 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
52445290

52455291
int feedback_slot_index = feedback_index(feedback_spec()->AddCallICSlot());
52465292

5247-
if (first_spread_index == expr->arguments()->length() - 1) {
5293+
if (spread_position == Call::kHasFinalSpread) {
52485294
builder()->ConstructWithSpread(constructor, args_regs,
52495295
feedback_slot_index);
52505296
} else {
5251-
DCHECK_EQ(first_spread_index, expr->arguments()->length());
5297+
DCHECK_EQ(spread_position, Call::kNoSpread);
52525298
// Call construct.
52535299
// TODO(turbofan): For now we do gather feedback on super constructor
52545300
// calls, utilizing the existing machinery to inline the actual call

deps/v8/src/parsing/parser-base.h

+4-12
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,7 @@ class ParserBase {
11861186
BlockT ParseClassStaticBlock(ClassInfo* class_info);
11871187
ObjectLiteralPropertyT ParseObjectPropertyDefinition(
11881188
ParsePropertyInfo* prop_info, bool* has_seen_proto);
1189+
// TODO(syg): Remove has_spread once SpreadCallNew is removed.
11891190
void ParseArguments(
11901191
ExpressionListT* args, bool* has_spread,
11911192
ParsingArrowHeadFlag maybe_arrow = kCertainlyNotArrowHead);
@@ -3392,11 +3393,7 @@ ParserBase<Impl>::ParseLeftHandSideContinuation(ExpressionT result) {
33923393
return result;
33933394
}
33943395

3395-
if (has_spread) {
3396-
result = impl()->SpreadCall(result, args, pos, Call::NOT_EVAL, false);
3397-
} else {
3398-
result = factory()->NewCall(result, args, pos, Call::NOT_EVAL);
3399-
}
3396+
result = factory()->NewCall(result, args, pos, has_spread);
34003397

34013398
maybe_arrow.ValidateExpression();
34023399

@@ -3490,13 +3487,8 @@ ParserBase<Impl>::ParseLeftHandSideContinuation(ExpressionT result) {
34903487
Call::PossiblyEval is_possibly_eval =
34913488
CheckPossibleEvalCall(result, is_optional, scope());
34923489

3493-
if (has_spread) {
3494-
result = impl()->SpreadCall(result, args, pos, is_possibly_eval,
3495-
is_optional);
3496-
} else {
3497-
result = factory()->NewCall(result, args, pos, is_possibly_eval,
3498-
is_optional);
3499-
}
3490+
result = factory()->NewCall(result, args, pos, has_spread,
3491+
is_possibly_eval, is_optional);
35003492

35013493
fni_.RemoveLastFunction();
35023494
break;

deps/v8/src/parsing/parser.cc

+3-39
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name,
6969

7070
args.Add(spread_args);
7171
Expression* super_call_ref = NewSuperCallReference(pos);
72-
call = factory()->NewCall(super_call_ref, args, pos);
72+
constexpr bool has_spread = true;
73+
call = factory()->NewCall(super_call_ref, args, pos, has_spread);
7374
}
7475
body.Add(factory()->NewReturnStatement(call, pos));
7576
}
@@ -3371,47 +3372,10 @@ ArrayLiteral* Parser::ArrayLiteralFromListWithSpread(
33713372
return factory()->NewArrayLiteral(list, first_spread, kNoSourcePosition);
33723373
}
33733374

3374-
Expression* Parser::SpreadCall(Expression* function,
3375-
const ScopedPtrList<Expression>& args_list,
3376-
int pos, Call::PossiblyEval is_possibly_eval,
3377-
bool optional_chain) {
3378-
// Handle this case in BytecodeGenerator.
3379-
if (OnlyLastArgIsSpread(args_list) || function->IsSuperCallReference()) {
3380-
return factory()->NewCall(function, args_list, pos, Call::NOT_EVAL,
3381-
optional_chain);
3382-
}
3383-
3384-
ScopedPtrList<Expression> args(pointer_buffer());
3385-
if (function->IsProperty()) {
3386-
// Method calls
3387-
if (function->AsProperty()->IsSuperAccess()) {
3388-
Expression* home = ThisExpression();
3389-
args.Add(function);
3390-
args.Add(home);
3391-
} else {
3392-
Variable* temp = NewTemporary(ast_value_factory()->empty_string());
3393-
VariableProxy* obj = factory()->NewVariableProxy(temp);
3394-
Assignment* assign_obj = factory()->NewAssignment(
3395-
Token::ASSIGN, obj, function->AsProperty()->obj(), kNoSourcePosition);
3396-
function =
3397-
factory()->NewProperty(assign_obj, function->AsProperty()->key(),
3398-
kNoSourcePosition, optional_chain);
3399-
args.Add(function);
3400-
obj = factory()->NewVariableProxy(temp);
3401-
args.Add(obj);
3402-
}
3403-
} else {
3404-
// Non-method calls
3405-
args.Add(function);
3406-
args.Add(factory()->NewUndefinedLiteral(kNoSourcePosition));
3407-
}
3408-
args.Add(ArrayLiteralFromListWithSpread(args_list));
3409-
return factory()->NewCallRuntime(Context::REFLECT_APPLY_INDEX, args, pos);
3410-
}
3411-
34123375
Expression* Parser::SpreadCallNew(Expression* function,
34133376
const ScopedPtrList<Expression>& args_list,
34143377
int pos) {
3378+
// TODO(syg): Handle all spread cases in BytecodeGenerator.
34153379
if (OnlyLastArgIsSpread(args_list)) {
34163380
// Handle in BytecodeGenerator.
34173381
return factory()->NewCallNew(function, args_list, pos);

deps/v8/src/parsing/parser.h

-4
Original file line numberDiff line numberDiff line change
@@ -493,10 +493,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
493493

494494
ArrayLiteral* ArrayLiteralFromListWithSpread(
495495
const ScopedPtrList<Expression>& list);
496-
Expression* SpreadCall(Expression* function,
497-
const ScopedPtrList<Expression>& args, int pos,
498-
Call::PossiblyEval is_possibly_eval,
499-
bool optional_chain);
500496
Expression* SpreadCallNew(Expression* function,
501497
const ScopedPtrList<Expression>& args, int pos);
502498
Expression* RewriteSuperCall(Expression* call_expression);

0 commit comments

Comments
 (0)