Commit 07f1c362 authored by nikolaos's avatar nikolaos Committed by Commit bot

Add spread rewriting

In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions.  E.g.

    [1, 2, 3, ...x, 4, ...y, 5]

is roughly rewritten as:

    do {
      $R = [1, 2, 3];
      for ($i of x) %AppendElement($R, $i);
      %AppendElement($R, 4);
      for ($j of y) %AppendElement($R, $j);
      %AppendElement($R, 5);
      $R
    }

where $R, $i and $j are fresh temporary variables.

R=rossberg@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1564083002

Cr-Commit-Position: refs/heads/master@{#33307}
parent 0c1430ac
...@@ -1683,6 +1683,19 @@ class ArrayLiteral final : public MaterializedLiteral { ...@@ -1683,6 +1683,19 @@ class ArrayLiteral final : public MaterializedLiteral {
return flags; return flags;
} }
// Provide a mechanism for iterating through values to rewrite spreads.
ZoneList<Expression*>::iterator FirstSpread() const {
return (first_spread_index_ >= 0) ? values_->begin() + first_spread_index_
: values_->end();
}
ZoneList<Expression*>::iterator EndValue() const { return values_->end(); }
// Rewind an array literal omitting everything from the first spread on.
void RewindSpreads() {
values_->Rewind(first_spread_index_);
first_spread_index_ = -1;
}
enum Flags { enum Flags {
kNoFlags = 0, kNoFlags = 0,
kShallowElements = 1, kShallowElements = 1,
...@@ -2372,17 +2385,20 @@ class Spread final : public Expression { ...@@ -2372,17 +2385,20 @@ class Spread final : public Expression {
Expression* expression() const { return expression_; } Expression* expression() const { return expression_; }
void set_expression(Expression* e) { expression_ = e; } void set_expression(Expression* e) { expression_ = e; }
int expression_position() const { return expr_pos_; }
static int num_ids() { return parent_num_ids(); } static int num_ids() { return parent_num_ids(); }
protected: protected:
Spread(Zone* zone, Expression* expression, int pos) Spread(Zone* zone, Expression* expression, int pos, int expr_pos)
: Expression(zone, pos), expression_(expression) {} : Expression(zone, pos), expression_(expression), expr_pos_(expr_pos) {}
static int parent_num_ids() { return Expression::num_ids(); } static int parent_num_ids() { return Expression::num_ids(); }
private: private:
int local_id(int n) const { return base_id() + parent_num_ids() + n; } int local_id(int n) const { return base_id() + parent_num_ids() + n; }
Expression* expression_; Expression* expression_;
int expr_pos_;
}; };
...@@ -3389,8 +3405,8 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3389,8 +3405,8 @@ class AstNodeFactory final BASE_EMBEDDED {
CompareOperation(local_zone_, op, left, right, pos); CompareOperation(local_zone_, op, left, right, pos);
} }
Spread* NewSpread(Expression* expression, int pos) { Spread* NewSpread(Expression* expression, int pos, int expr_pos) {
return new (local_zone_) Spread(local_zone_, expression, pos); return new (local_zone_) Spread(local_zone_, expression, pos, expr_pos);
} }
Conditional* NewConditional(Expression* condition, Conditional* NewConditional(Expression* condition,
......
...@@ -1339,6 +1339,7 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier, ...@@ -1339,6 +1339,7 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
// (...x)=>x. The continuation that looks for the => is in // (...x)=>x. The continuation that looks for the => is in
// ParseAssignmentExpression. // ParseAssignmentExpression.
int ellipsis_pos = position(); int ellipsis_pos = position();
int expr_pos = peek_position();
classifier->RecordExpressionError(scanner()->location(), classifier->RecordExpressionError(scanner()->location(),
MessageTemplate::kUnexpectedToken, MessageTemplate::kUnexpectedToken,
Token::String(Token::ELLIPSIS)); Token::String(Token::ELLIPSIS));
...@@ -1352,7 +1353,7 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier, ...@@ -1352,7 +1353,7 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
return this->EmptyExpression(); return this->EmptyExpression();
} }
Expect(Token::RPAREN, CHECK_OK); Expect(Token::RPAREN, CHECK_OK);
return factory()->NewSpread(expr, ellipsis_pos); return factory()->NewSpread(expr, ellipsis_pos, expr_pos);
} }
// Heuristically try to detect immediately called functions before // Heuristically try to detect immediately called functions before
// seeing the call parentheses. // seeing the call parentheses.
...@@ -1469,10 +1470,10 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression( ...@@ -1469,10 +1470,10 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
Consume(Token::ELLIPSIS); Consume(Token::ELLIPSIS);
seen_rest = is_rest = true; seen_rest = is_rest = true;
} }
int pos = position(); int pos = position(), expr_pos = peek_position();
ExpressionT right = this->ParseAssignmentExpression( ExpressionT right = this->ParseAssignmentExpression(
accept_IN, flags, &binding_classifier, CHECK_OK); accept_IN, flags, &binding_classifier, CHECK_OK);
if (is_rest) right = factory()->NewSpread(right, pos); if (is_rest) right = factory()->NewSpread(right, pos, expr_pos);
is_simple_parameter_list = is_simple_parameter_list =
is_simple_parameter_list && this->IsIdentifier(right); is_simple_parameter_list && this->IsIdentifier(right);
classifier->Accumulate(binding_classifier, classifier->Accumulate(binding_classifier,
...@@ -1511,9 +1512,10 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral( ...@@ -1511,9 +1512,10 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
} else if (peek() == Token::ELLIPSIS) { } else if (peek() == Token::ELLIPSIS) {
int start_pos = peek_position(); int start_pos = peek_position();
Consume(Token::ELLIPSIS); Consume(Token::ELLIPSIS);
int expr_pos = peek_position();
ExpressionT argument = ExpressionT argument =
this->ParseAssignmentExpression(true, classifier, CHECK_OK); this->ParseAssignmentExpression(true, classifier, CHECK_OK);
elem = factory()->NewSpread(argument, start_pos); elem = factory()->NewSpread(argument, start_pos, expr_pos);
if (first_spread_index < 0) { if (first_spread_index < 0) {
first_spread_index = values->length(); first_spread_index = values->length();
...@@ -1913,6 +1915,7 @@ typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments( ...@@ -1913,6 +1915,7 @@ typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments(
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);
int expr_pos = peek_position();
ExpressionT argument = this->ParseAssignmentExpression( ExpressionT argument = this->ParseAssignmentExpression(
true, classifier, CHECK_OK_CUSTOM(NullExpressionList)); true, classifier, CHECK_OK_CUSTOM(NullExpressionList));
...@@ -1923,7 +1926,7 @@ typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments( ...@@ -1923,7 +1926,7 @@ typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments(
spread_arg.beg_pos = start_pos; spread_arg.beg_pos = start_pos;
spread_arg.end_pos = peek_position(); spread_arg.end_pos = peek_position();
} }
argument = factory()->NewSpread(argument, start_pos); argument = factory()->NewSpread(argument, start_pos, expr_pos);
} }
result->Add(argument, zone_); result->Add(argument, zone_);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "src/api.h" #include "src/api.h"
#include "src/ast/ast.h" #include "src/ast/ast.h"
#include "src/ast/ast-expression-rewriter.h"
#include "src/ast/ast-expression-visitor.h" #include "src/ast/ast-expression-visitor.h"
#include "src/ast/ast-literal-reindexer.h" #include "src/ast/ast-literal-reindexer.h"
#include "src/ast/scopeinfo.h" #include "src/ast/scopeinfo.h"
...@@ -5411,20 +5412,61 @@ ObjectLiteralProperty* ParserTraits::RewriteNonPatternObjectLiteralProperty( ...@@ -5411,20 +5412,61 @@ ObjectLiteralProperty* ParserTraits::RewriteNonPatternObjectLiteralProperty(
} }
class NonPatternRewriter : public AstExpressionRewriter {
public:
NonPatternRewriter(uintptr_t stack_limit, Parser* parser)
: AstExpressionRewriter(stack_limit), parser_(parser) {}
~NonPatternRewriter() override {}
private:
bool RewriteExpression(Expression* expr) override {
// Rewrite only what could have been a pattern but is not.
if (expr->IsArrayLiteral()) {
// Spread rewriting in array literals.
ArrayLiteral* lit = expr->AsArrayLiteral();
VisitExpressions(lit->values());
replacement_ = parser_->RewriteSpreads(lit);
return false;
}
if (expr->IsObjectLiteral()) {
return true;
}
if (expr->IsBinaryOperation() &&
expr->AsBinaryOperation()->op() == Token::COMMA) {
return true;
}
// Everything else does not need rewriting.
return false;
}
Parser* parser_;
};
Expression* Parser::RewriteNonPattern(Expression* expr, Expression* Parser::RewriteNonPattern(Expression* expr,
const ExpressionClassifier* classifier, const ExpressionClassifier* classifier,
bool* ok) { bool* ok) {
// For the time being, this does no rewriting at all.
ValidateExpression(classifier, ok); ValidateExpression(classifier, ok);
return expr; if (!*ok) return expr;
NonPatternRewriter rewriter(stack_limit_, this);
Expression* result = reinterpret_cast<Expression*>(rewriter.Rewrite(expr));
DCHECK_NOT_NULL(result);
return result;
} }
ZoneList<Expression*>* Parser::RewriteNonPatternArguments( ZoneList<Expression*>* Parser::RewriteNonPatternArguments(
ZoneList<Expression*>* args, const ExpressionClassifier* classifier, ZoneList<Expression*>* args, const ExpressionClassifier* classifier,
bool* ok) { bool* ok) {
// For the time being, this does no rewriting at all.
ValidateExpression(classifier, ok); ValidateExpression(classifier, ok);
if (!*ok) return args;
for (int i = 0; i < args->length(); i++) {
NonPatternRewriter rewriter(stack_limit_, this);
Expression* result =
reinterpret_cast<Expression*>(rewriter.Rewrite(args->at(i)));
DCHECK_NOT_NULL(result);
args->Set(i, result);
}
return args; return args;
} }
...@@ -5462,6 +5504,125 @@ void Parser::RewriteDestructuringAssignments() { ...@@ -5462,6 +5504,125 @@ void Parser::RewriteDestructuringAssignments() {
} }
Expression* Parser::RewriteSpreads(ArrayLiteral* lit) {
// Array literals containing spreads are rewritten using do expressions, e.g.
// [1, 2, 3, ...x, 4, ...y, 5]
// is roughly rewritten as:
// do {
// $R = [1, 2, 3];
// for ($i of x) %AppendElement($R, $i);
// %AppendElement($R, 4);
// for ($j of y) %AppendElement($R, $j);
// %AppendElement($R, 5);
// $R
// }
// where $R, $i and $j are fresh temporary variables.
ZoneList<Expression*>::iterator s = lit->FirstSpread();
if (s == lit->EndValue()) return nullptr; // no spread, no rewriting...
Variable* result =
scope_->NewTemporary(ast_value_factory()->dot_result_string());
// NOTE: The value assigned to R is the whole original array literal,
// spreads included. This will be fixed before the rewritten AST is returned.
// $R = lit
Expression* init_result =
factory()->NewAssignment(Token::INIT, factory()->NewVariableProxy(result),
lit, RelocInfo::kNoPosition);
Block* do_block =
factory()->NewBlock(nullptr, 16, false, RelocInfo::kNoPosition);
do_block->statements()->Add(
factory()->NewExpressionStatement(init_result, RelocInfo::kNoPosition),
zone());
// Traverse the array literal starting from the first spread.
while (s != lit->EndValue()) {
Expression* value = *s++;
Spread* spread = value->AsSpread();
if (spread == nullptr) {
// If the element is not a spread, we're adding a single:
// %AppendElement($R, value)
ZoneList<Expression*>* append_element_args = NewExpressionList(2, zone());
append_element_args->Add(factory()->NewVariableProxy(result), zone());
append_element_args->Add(value, zone());
do_block->statements()->Add(
factory()->NewExpressionStatement(
factory()->NewCallRuntime(Runtime::kAppendElement,
append_element_args,
RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
zone());
} else {
// If it's a spread, we're adding a for/of loop iterating through it.
Variable* each =
scope_->NewTemporary(ast_value_factory()->dot_for_string());
Expression* subject = spread->expression();
Variable* iterator =
scope_->NewTemporary(ast_value_factory()->dot_iterator_string());
Variable* element =
scope_->NewTemporary(ast_value_factory()->dot_result_string());
// iterator = subject[Symbol.iterator]()
Expression* assign_iterator = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(iterator),
GetIterator(subject, factory(), spread->expression_position()),
subject->position());
// !%_IsJSReceiver(element = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(element)
Expression* next_element;
{
// element = iterator.next()
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
next_element = BuildIteratorNextResult(iterator_proxy, element,
spread->expression_position());
}
// element.done
Expression* element_done;
{
Expression* done_literal = factory()->NewStringLiteral(
ast_value_factory()->done_string(), RelocInfo::kNoPosition);
Expression* element_proxy = factory()->NewVariableProxy(element);
element_done = factory()->NewProperty(element_proxy, done_literal,
RelocInfo::kNoPosition);
}
// each = element.value
Expression* assign_each;
{
Expression* value_literal = factory()->NewStringLiteral(
ast_value_factory()->value_string(), RelocInfo::kNoPosition);
Expression* element_proxy = factory()->NewVariableProxy(element);
Expression* element_value = factory()->NewProperty(
element_proxy, value_literal, RelocInfo::kNoPosition);
assign_each = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(each), element_value,
RelocInfo::kNoPosition);
}
// %AppendElement($R, each)
Statement* append_body;
{
ZoneList<Expression*>* append_element_args =
NewExpressionList(2, zone());
append_element_args->Add(factory()->NewVariableProxy(result), zone());
append_element_args->Add(factory()->NewVariableProxy(each), zone());
append_body = factory()->NewExpressionStatement(
factory()->NewCallRuntime(Runtime::kAppendElement,
append_element_args,
RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
}
// for (each of spread) %AppendElement($R, each)
ForEachStatement* loop = factory()->NewForEachStatement(
ForEachStatement::ITERATE, nullptr, RelocInfo::kNoPosition);
ForOfStatement* for_of = loop->AsForOfStatement();
for_of->Initialize(factory()->NewVariableProxy(each), subject,
append_body, assign_iterator, next_element,
element_done, assign_each);
do_block->statements()->Add(for_of, zone());
}
}
// Now, rewind the original array literal to truncate everything from the
// first spread (included) until the end. This fixes $R's initialization.
lit->RewindSpreads();
return factory()->NewDoExpression(do_block, result, lit->position());
}
void ParserTraits::QueueDestructuringAssignmentForRewriting(Expression* expr) { void ParserTraits::QueueDestructuringAssignmentForRewriting(Expression* expr) {
DCHECK(expr->IsRewritableAssignmentExpression()); DCHECK(expr->IsRewritableAssignmentExpression());
parser_->function_state_->AddDestructuringAssignment( parser_->function_state_->AddDestructuringAssignment(
......
...@@ -1013,6 +1013,9 @@ class Parser : public ParserBase<ParserTraits> { ...@@ -1013,6 +1013,9 @@ class Parser : public ParserBase<ParserTraits> {
V8_INLINE void RewriteDestructuringAssignments(); V8_INLINE void RewriteDestructuringAssignments();
friend class NonPatternRewriter;
V8_INLINE Expression* RewriteSpreads(ArrayLiteral* lit);
V8_INLINE Expression* RewriteNonPattern( V8_INLINE Expression* RewriteNonPattern(
Expression* expr, const ExpressionClassifier* classifier, bool* ok); Expression* expr, const ExpressionClassifier* classifier, bool* ok);
V8_INLINE ZoneList<Expression*>* RewriteNonPatternArguments( V8_INLINE ZoneList<Expression*>* RewriteNonPatternArguments(
......
...@@ -550,7 +550,8 @@ class PreParserFactory { ...@@ -550,7 +550,8 @@ class PreParserFactory {
return PreParserExpression::Default(); return PreParserExpression::Default();
} }
PreParserExpression NewSpread(PreParserExpression expression, int pos) { PreParserExpression NewSpread(PreParserExpression expression, int pos,
int expr_pos) {
return PreParserExpression::Spread(expression); return PreParserExpression::Spread(expression);
} }
......
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