Commit b634a61d authored by caitpotter88's avatar caitpotter88 Committed by Commit bot

[es6] implement destructuring assignment

Attempt #<really big number>

Parses, and lazily rewrites Destructuring Assignment expressions. The rewriting strategy involves inserting a placeholder RewritableAssignmentExpression into the AST, whose content expression can be completely rewritten at a later time.

Lazy rewriting ensures that errors do not occur due to eagerly rewriting nodes which form part of a binding pattern, thus breaking the meaning of the pattern --- or by eagerly rewriting ambiguous constructs that are not immediately known

BUG=v8:811
LOG=Y
R=adamk@chromium.org, bmeurer@chromium.org, rossberg@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#32623}
parent 77774035
......@@ -394,5 +394,12 @@ void AstExpressionVisitor::VisitSuperCallReference(SuperCallReference* expr) {
}
void AstExpressionVisitor::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* expr) {
VisitExpression(expr);
RECURSE(Visit(expr->expression()));
}
} // namespace internal
} // namespace v8
......@@ -76,6 +76,12 @@ void AstLiteralReindexer::VisitSuperCallReference(SuperCallReference* node) {
}
void AstLiteralReindexer::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* node) {
Visit(node->expression());
}
void AstLiteralReindexer::VisitImportDeclaration(ImportDeclaration* node) {
VisitVariableProxy(node->proxy());
}
......
......@@ -348,6 +348,7 @@ void AstNumberingVisitor::VisitProperty(Property* node) {
void AstNumberingVisitor::VisitAssignment(Assignment* node) {
IncrementNodeCount();
node->set_base_id(ReserveIdRange(Assignment::num_ids()));
if (node->is_compound()) VisitBinaryOperation(node->binary_operation());
VisitReference(node->target());
Visit(node->value());
......@@ -556,6 +557,14 @@ void AstNumberingVisitor::VisitFunctionLiteral(FunctionLiteral* node) {
}
void AstNumberingVisitor::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* node) {
IncrementNodeCount();
node->set_base_id(ReserveIdRange(RewritableAssignmentExpression::num_ids()));
Visit(node->expression());
}
bool AstNumberingVisitor::Finish(FunctionLiteral* node) {
node->set_ast_properties(&properties_);
node->set_dont_optimize_reason(dont_optimize_reason());
......
......@@ -91,7 +91,8 @@ namespace internal {
V(SuperCallReference) \
V(CaseClause) \
V(EmptyParentheses) \
V(DoExpression)
V(DoExpression) \
V(RewritableAssignmentExpression)
#define AST_NODE_LIST(V) \
DECLARATION_NODE_LIST(V) \
......@@ -2334,6 +2335,7 @@ class Assignment final : public Expression {
Token::Value op() const { return TokenField::decode(bit_field_); }
Expression* target() const { return target_; }
Expression* value() const { return value_; }
BinaryOperation* binary_operation() const { return binary_operation_; }
// This check relies on the definition order of token in token.h.
......@@ -2381,9 +2383,12 @@ class Assignment final : public Expression {
int local_id(int n) const { return base_id() + parent_num_ids() + n; }
class IsUninitializedField : public BitField16<bool, 0, 1> {};
class KeyTypeField : public BitField16<IcCheckType, 1, 1> {};
class StoreModeField : public BitField16<KeyedAccessStoreMode, 2, 3> {};
class TokenField : public BitField16<Token::Value, 5, 8> {};
class KeyTypeField
: public BitField16<IcCheckType, IsUninitializedField::kNext, 1> {};
class StoreModeField
: public BitField16<KeyedAccessStoreMode, KeyTypeField::kNext, 3> {};
class TokenField : public BitField16<Token::Value, StoreModeField::kNext, 8> {
};
// Starts with 16-bit field, which should get packed together with
// Expression's trailing 16-bit field.
......@@ -2396,6 +2401,36 @@ class Assignment final : public Expression {
};
class RewritableAssignmentExpression : public Expression {
public:
DECLARE_NODE_TYPE(RewritableAssignmentExpression)
Expression* expression() { return expr_; }
bool is_rewritten() const { return is_rewritten_; }
void Rewrite(Expression* new_expression) {
DCHECK(!is_rewritten());
DCHECK_NOT_NULL(new_expression);
expr_ = new_expression;
is_rewritten_ = true;
}
static int num_ids() { return parent_num_ids(); }
protected:
RewritableAssignmentExpression(Zone* zone, Expression* expression)
: Expression(zone, expression->position()),
is_rewritten_(false),
expr_(expression) {}
private:
int local_id(int n) const { return base_id() + parent_num_ids() + n; }
bool is_rewritten_;
Expression* expr_;
};
class Yield final : public Expression {
public:
DECLARE_NODE_TYPE(Yield)
......@@ -3196,7 +3231,6 @@ class AstVisitor BASE_EMBEDDED {
#undef DEF_VISIT
};
#define DEFINE_AST_VISITOR_SUBCLASS_MEMBERS() \
public: \
void Visit(AstNode* node) final { \
......@@ -3549,6 +3583,14 @@ class AstNodeFactory final BASE_EMBEDDED {
local_zone_, condition, then_expression, else_expression, position);
}
RewritableAssignmentExpression* NewRewritableAssignmentExpression(
Expression* expression) {
DCHECK_NOT_NULL(expression);
DCHECK(expression->IsAssignment());
return new (local_zone_)
RewritableAssignmentExpression(local_zone_, expression);
}
Assignment* NewAssignment(Token::Value op,
Expression* target,
Expression* value,
......
......@@ -420,6 +420,12 @@ void CallPrinter::VisitSuperCallReference(SuperCallReference* node) {
}
void CallPrinter::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* node) {
Find(node->expression());
}
void CallPrinter::FindStatements(ZoneList<Statement*>* statements) {
if (statements == NULL) return;
for (int i = 0; i < statements->length(); i++) {
......@@ -931,6 +937,12 @@ void PrettyPrinter::VisitSuperCallReference(SuperCallReference* node) {
}
void PrettyPrinter::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* node) {
Visit(node->expression());
}
const char* PrettyPrinter::Print(AstNode* node) {
Init();
Visit(node);
......@@ -1682,6 +1694,12 @@ void AstPrinter::VisitSuperCallReference(SuperCallReference* node) {
}
void AstPrinter::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* node) {
Visit(node->expression());
}
#endif // DEBUG
} // namespace internal
......
......@@ -21,6 +21,7 @@ namespace internal {
"Arguments object value in a test context") \
V(kArrayBoilerplateCreationFailed, "Array boilerplate creation failed") \
V(kArrayIndexConstantValueTooBig, "Array index constant value too big") \
V(kAssignmentPattern, "Destructuring assignment") \
V(kAssignmentToArguments, "Assignment to arguments") \
V(kAssignmentToLetVariableBeforeInitialization, \
"Assignment to let variable before initialization") \
......
......@@ -1978,6 +1978,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_sloppy_let)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_rest_parameters)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_default_parameters)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_destructuring_bind)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_destructuring_assignment)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object_observe)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexps)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode_regexps)
......@@ -2469,6 +2470,7 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_reflect_natives[] = {"native harmony-reflect.js",
nullptr};
static const char* harmony_destructuring_bind_natives[] = {nullptr};
static const char* harmony_destructuring_assignment_natives[] = {nullptr};
static const char* harmony_object_observe_natives[] = {
"native harmony-object-observe.js", nullptr};
static const char* harmony_sharedarraybuffer_natives[] = {
......
......@@ -3068,6 +3068,12 @@ VectorSlotPair AstGraphBuilder::CreateVectorSlotPair(
}
void AstGraphBuilder::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* node) {
Visit(node->expression());
}
namespace {
// Limit of context chain length to which inline check is possible.
......
......@@ -288,6 +288,12 @@ void ALAA::VisitCountOperation(CountOperation* e) {
}
void ALAA::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* expr) {
Visit(expr->expression());
}
void ALAA::AnalyzeAssignment(Variable* var) {
if (!loop_stack_.empty() && var->IsStackAllocated()) {
loop_stack_.back()->Add(GetVariableIndex(info()->scope(), var));
......
......@@ -7039,6 +7039,7 @@ void HOptimizedGraphBuilder::VisitAssignment(Assignment* expr) {
DCHECK(!HasStackOverflow());
DCHECK(current_block() != NULL);
DCHECK(current_block()->HasPredecessor());
VariableProxy* proxy = expr->target()->AsVariableProxy();
Property* prop = expr->target()->AsProperty();
DCHECK(proxy == NULL || prop == NULL);
......@@ -12133,6 +12134,12 @@ void HOptimizedGraphBuilder::VisitExportDeclaration(
}
void HOptimizedGraphBuilder::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* node) {
CHECK_ALIVE(Visit(node->expression()));
}
// Generators for inline runtime functions.
// Support for types.
void HOptimizedGraphBuilder::GenerateIsSmi(CallRuntime* call) {
......
......@@ -770,6 +770,12 @@ void AstTyper::VisitSuperPropertyReference(SuperPropertyReference* expr) {}
void AstTyper::VisitSuperCallReference(SuperCallReference* expr) {}
void AstTyper::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* expr) {
Visit(expr->expression());
}
void AstTyper::VisitDeclarations(ZoneList<Declaration*>* decls) {
for (int i = 0; i < decls->length(); ++i) {
Declaration* decl = decls->at(i);
......
......@@ -203,7 +203,8 @@ DEFINE_IMPLICATION(es_staging, harmony_destructuring_bind)
V(harmony_simd, "harmony simd") \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_regexp_subclass, "harmony regexp subclassing") \
V(harmony_regexp_lookbehind, "harmony regexp lookbehind")
V(harmony_regexp_lookbehind, "harmony regexp lookbehind") \
V(harmony_destructuring_assignment, "harmony destructuring assignment")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \
......
......@@ -1487,6 +1487,12 @@ void FullCodeGenerator::VisitEmptyParentheses(EmptyParentheses* expr) {
}
void FullCodeGenerator::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* expr) {
Visit(expr->expression());
}
FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
int* stack_depth, int* context_length) {
// The macros used here must preserve the result register.
......
......@@ -2036,6 +2036,12 @@ void BytecodeGenerator::VisitLogicalAndExpression(BinaryOperation* binop) {
}
void BytecodeGenerator::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* expr) {
Visit(expr->expression());
}
void BytecodeGenerator::VisitNewLocalFunctionContext() {
AccumulatorResultScope accumulator_execution_result(this);
Scope* scope = this->scope();
......
......@@ -328,6 +328,8 @@ class CallSite {
T(IllegalReturn, "Illegal return statement") \
T(InvalidEscapedReservedWord, "Keyword must not contain escaped characters") \
T(InvalidLhsInAssignment, "Invalid left-hand side in assignment") \
T(InvalidCoverInitializedName, "Invalid shorthand property initializer") \
T(InvalidDestructuringTarget, "Invalid destructuring assignment target") \
T(InvalidLhsInFor, "Invalid left-hand side in for-loop") \
T(InvalidLhsInPostfixOp, \
"Invalid left-hand side expression in postfix operation") \
......@@ -348,6 +350,7 @@ class CallSite {
T(PushPastSafeLength, \
"Pushing % elements on an array-like of length % " \
"is disallowed, as the total surpasses 2**53-1") \
T(ElementAfterRest, "Rest element must be last element in array") \
T(BadSetterRestParameter, \
"Setter function argument must not be a rest parameter") \
T(ParamDupe, "Duplicate parameter name not allowed in this context") \
......
......@@ -19,10 +19,12 @@ class ExpressionClassifier {
Error()
: location(Scanner::Location::invalid()),
message(MessageTemplate::kNone),
type(kSyntaxError),
arg(nullptr) {}
Scanner::Location location;
MessageTemplate::Template message;
MessageTemplate::Template message : 30;
ParseErrorType type : 2;
const char* arg;
};
......@@ -36,6 +38,7 @@ class ExpressionClassifier {
StrongModeFormalParametersProduction = 1 << 6,
ArrowFormalParametersProduction = 1 << 7,
LetPatternProduction = 1 << 8,
CoverInitializedNameProduction = 1 << 9,
ExpressionProductions =
(ExpressionProduction | FormalParameterInitializerProduction),
......@@ -45,8 +48,9 @@ class ExpressionClassifier {
StrictModeFormalParametersProduction |
StrongModeFormalParametersProduction),
StandardProductions = ExpressionProductions | PatternProductions,
AllProductions = (StandardProductions | FormalParametersProductions |
ArrowFormalParametersProduction)
AllProductions =
(StandardProductions | FormalParametersProductions |
ArrowFormalParametersProduction | CoverInitializedNameProduction)
};
enum FunctionProperties { NonSimpleParameter = 1 << 0 };
......@@ -133,6 +137,13 @@ class ExpressionClassifier {
const Error& let_pattern_error() const { return let_pattern_error_; }
bool has_cover_initialized_name() const {
return !is_valid(CoverInitializedNameProduction);
}
const Error& cover_initialized_name_error() const {
return cover_initialized_name_error_;
}
bool is_simple_parameter_list() const {
return !(function_properties_ & NonSimpleParameter);
}
......@@ -151,6 +162,17 @@ class ExpressionClassifier {
expression_error_.arg = arg;
}
void RecordExpressionError(const Scanner::Location& loc,
MessageTemplate::Template message,
ParseErrorType type, const char* arg = nullptr) {
if (!is_valid_expression()) return;
invalid_productions_ |= ExpressionProduction;
expression_error_.location = loc;
expression_error_.message = message;
expression_error_.arg = arg;
expression_error_.type = type;
}
void RecordFormalParameterInitializerError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
......@@ -181,6 +203,13 @@ class ExpressionClassifier {
assignment_pattern_error_.arg = arg;
}
void RecordPatternError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
RecordBindingPatternError(loc, message, arg);
RecordAssignmentPatternError(loc, message, arg);
}
void RecordArrowFormalParametersError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
......@@ -232,6 +261,26 @@ class ExpressionClassifier {
let_pattern_error_.arg = arg;
}
void RecordCoverInitializedNameError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
if (has_cover_initialized_name()) return;
invalid_productions_ |= CoverInitializedNameProduction;
cover_initialized_name_error_.location = loc;
cover_initialized_name_error_.message = message;
cover_initialized_name_error_.arg = arg;
}
void ForgiveCoverInitializedNameError() {
invalid_productions_ &= ~CoverInitializedNameProduction;
cover_initialized_name_error_ = Error();
}
void ForgiveAssignmentPatternError() {
invalid_productions_ &= ~AssignmentPatternProduction;
assignment_pattern_error_ = Error();
}
void Accumulate(const ExpressionClassifier& inner,
unsigned productions = StandardProductions) {
// Propagate errors from inner, but don't overwrite already recorded
......@@ -266,6 +315,8 @@ class ExpressionClassifier {
inner.strong_mode_formal_parameter_error_;
if (errors & LetPatternProduction)
let_pattern_error_ = inner.let_pattern_error_;
if (errors & CoverInitializedNameProduction)
cover_initialized_name_error_ = inner.cover_initialized_name_error_;
}
// As an exception to the above, the result continues to be a valid arrow
......@@ -295,6 +346,7 @@ class ExpressionClassifier {
Error strict_mode_formal_parameter_error_;
Error strong_mode_formal_parameter_error_;
Error let_pattern_error_;
Error cover_initialized_name_error_;
DuplicateFinder* duplicate_finder_;
};
......
......@@ -6,6 +6,7 @@
#include "src/api.h"
#include "src/ast/ast.h"
#include "src/ast/ast-expression-visitor.h"
#include "src/ast/ast-literal-reindexer.h"
#include "src/ast/scopeinfo.h"
#include "src/bailout-reason.h"
......@@ -923,6 +924,8 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_rest_parameters(FLAG_harmony_rest_parameters);
set_allow_harmony_default_parameters(FLAG_harmony_default_parameters);
set_allow_harmony_destructuring_bind(FLAG_harmony_destructuring_bind);
set_allow_harmony_destructuring_assignment(
FLAG_harmony_destructuring_assignment);
set_allow_strong_mode(FLAG_strong_mode);
set_allow_legacy_const(FLAG_legacy_const);
set_allow_harmony_do_expressions(FLAG_harmony_do_expressions);
......@@ -1093,6 +1096,7 @@ FunctionLiteral* Parser::DoParseProgram(ParseInfo* info) {
}
if (ok) {
ParserTraits::RewriteDestructuringAssignments();
result = factory()->NewFunctionLiteral(
ast_value_factory()->empty_string(), ast_value_factory(), scope_,
body, function_state.materialized_literal_count(),
......@@ -4404,6 +4408,11 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
allow_harmony_destructuring_bind()) {
CheckConflictingVarDeclarations(scope, CHECK_OK);
}
if (body) {
// If body can be inspected, rewrite queued destructuring assignments
ParserTraits::RewriteDestructuringAssignments();
}
}
bool has_duplicate_parameters =
......@@ -4532,6 +4541,38 @@ Statement* Parser::BuildAssertIsCoercible(Variable* var) {
}
class InitializerRewriter : public AstExpressionVisitor {
public:
InitializerRewriter(uintptr_t stack_limit, Expression* root, Parser* parser,
Scope* scope)
: AstExpressionVisitor(stack_limit, root),
parser_(parser),
scope_(scope) {}
private:
void VisitExpression(Expression* expr) {
RewritableAssignmentExpression* to_rewrite =
expr->AsRewritableAssignmentExpression();
if (to_rewrite == nullptr || to_rewrite->is_rewritten()) return;
bool ok = true;
Parser::PatternRewriter::RewriteDestructuringAssignment(parser_, to_rewrite,
scope_, &ok);
DCHECK(ok);
}
private:
Parser* parser_;
Scope* scope_;
};
void Parser::RewriteParameterInitializer(Expression* expr, Scope* scope) {
InitializerRewriter rewriter(stack_limit_, expr, this, scope);
rewriter.Run();
}
Block* Parser::BuildParameterInitializationBlock(
const ParserFormalParameters& parameters, bool* ok) {
DCHECK(!parameters.is_simple);
......@@ -4562,6 +4603,10 @@ Block* Parser::BuildParameterInitializationBlock(
if (parameter.initializer != nullptr) {
// IS_UNDEFINED($param) ? initializer : $param
DCHECK(!parameter.is_rest);
// Ensure initializer is rewritten
RewriteParameterInitializer(parameter.initializer, scope_);
auto condition = factory()->NewCompareOperation(
Token::EQ_STRICT,
factory()->NewVariableProxy(parameters.scope->parameter(i)),
......@@ -6465,5 +6510,41 @@ void Parser::RaiseLanguageMode(LanguageMode mode) {
static_cast<LanguageMode>(scope_->language_mode() | mode));
}
void ParserTraits::RewriteDestructuringAssignments() {
parser_->RewriteDestructuringAssignments();
}
void Parser::RewriteDestructuringAssignments() {
FunctionState* func = function_state_;
if (!allow_harmony_destructuring_assignment()) return;
const List<DestructuringAssignment>& assignments =
func->destructuring_assignments_to_rewrite();
for (int i = assignments.length() - 1; i >= 0; --i) {
// Rewrite list in reverse, so that nested assignment patterns are rewritten
// correctly.
DestructuringAssignment pair = assignments.at(i);
RewritableAssignmentExpression* to_rewrite =
pair.assignment->AsRewritableAssignmentExpression();
Scope* scope = pair.scope;
DCHECK_NOT_NULL(to_rewrite);
if (!to_rewrite->is_rewritten()) {
bool ok = true;
PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite, scope,
&ok);
DCHECK(ok);
}
}
}
void ParserTraits::QueueDestructuringAssignmentForRewriting(Expression* expr) {
DCHECK(expr->IsRewritableAssignmentExpression());
parser_->function_state_->AddDestructuringAssignment(
Parser::DestructuringAssignment(expr, parser_->scope_));
}
} // namespace internal
} // namespace v8
......@@ -899,6 +899,12 @@ class ParserTraits {
ZoneList<v8::internal::Expression*>* args,
int pos);
// Rewrite all DestructuringAssignments in the current FunctionState.
V8_INLINE void RewriteDestructuringAssignments();
V8_INLINE void QueueDestructuringAssignmentForRewriting(
Expression* assignment);
private:
Parser* parser_;
};
......@@ -1039,6 +1045,10 @@ class Parser : public ParserBase<ParserTraits> {
const DeclarationParsingResult::Declaration* declaration,
ZoneList<const AstRawString*>* names, bool* ok);
static void RewriteDestructuringAssignment(
Parser* parser, RewritableAssignmentExpression* expr, Scope* Scope,
bool* ok);
void set_initializer_position(int pos) { initializer_position_ = pos; }
private:
......@@ -1050,6 +1060,16 @@ class Parser : public ParserBase<ParserTraits> {
#undef DECLARE_VISIT
void Visit(AstNode* node) override;
enum PatternContext {
BINDING,
INITIALIZER,
ASSIGNMENT,
ASSIGNMENT_INITIALIZER
};
PatternContext context() const { return context_; }
void set_context(PatternContext context) { context_ = context; }
void RecurseIntoSubpattern(AstNode* pattern, Expression* value) {
Expression* old_value = current_value_;
current_value_ = value;
......@@ -1057,14 +1077,29 @@ class Parser : public ParserBase<ParserTraits> {
current_value_ = old_value;
}
void VisitObjectLiteral(ObjectLiteral* node, Variable** temp_var);
void VisitArrayLiteral(ArrayLiteral* node, Variable** temp_var);
bool IsBindingContext() const { return IsBindingContext(context_); }
bool IsInitializerContext() const { return context_ != ASSIGNMENT; }
bool IsAssignmentContext() const { return IsAssignmentContext(context_); }
bool IsAssignmentContext(PatternContext c) const;
bool IsBindingContext(PatternContext c) const;
PatternContext SetAssignmentContextIfNeeded(Expression* node);
PatternContext SetInitializerContextIfNeeded(Expression* node);
Variable* CreateTempVar(Expression* value = nullptr);
AstNodeFactory* factory() const { return descriptor_->parser->factory(); }
AstNodeFactory* factory() const { return parser_->factory(); }
AstValueFactory* ast_value_factory() const {
return descriptor_->parser->ast_value_factory();
return parser_->ast_value_factory();
}
Zone* zone() const { return descriptor_->parser->zone(); }
Zone* zone() const { return parser_->zone(); }
Scope* scope() const { return scope_; }
Scope* scope_;
Parser* parser_;
PatternContext context_;
Expression* pattern_;
int initializer_position_;
Block* block_;
......@@ -1214,6 +1249,11 @@ class Parser : public ParserBase<ParserTraits> {
void SetLanguageMode(Scope* scope, LanguageMode mode);
void RaiseLanguageMode(LanguageMode mode);
V8_INLINE void RewriteDestructuringAssignments();
friend class InitializerRewriter;
void RewriteParameterInitializer(Expression* expr, Scope* scope);
Scanner scanner_;
PreParser* reusable_preparser_;
Scope* original_scope_; // for ES5 function declarations in sloppy eval
......
This diff is collapsed.
This diff is collapsed.
......@@ -1376,5 +1376,13 @@ void AsmTyper::VisitWithExpectation(Expression* expr, Type* expected_type,
}
expected_type_ = save;
}
void AsmTyper::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* expr) {
RECURSE(Visit(expr->expression()));
}
} // namespace internal
} // namespace v8
......@@ -1511,6 +1511,7 @@ enum ParserFlag {
kAllowHarmonySloppy,
kAllowHarmonySloppyLet,
kAllowHarmonyDestructuring,
kAllowHarmonyDestructuringAssignment,
kAllowHarmonyNewTarget,
kAllowStrongMode,
kNoLegacyConst
......@@ -1536,6 +1537,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
parser->set_allow_harmony_sloppy_let(flags.Contains(kAllowHarmonySloppyLet));
parser->set_allow_harmony_destructuring_bind(
flags.Contains(kAllowHarmonyDestructuring));
parser->set_allow_harmony_destructuring_assignment(
flags.Contains(kAllowHarmonyDestructuringAssignment));
parser->set_allow_strong_mode(flags.Contains(kAllowStrongMode));
parser->set_allow_legacy_const(!flags.Contains(kNoLegacyConst));
}
......@@ -6820,6 +6823,264 @@ TEST(DestructuringNegativeTests) {
}
TEST(DestructuringAssignmentPositiveTests) {
const char* context_data[][2] = {
{"'use strict'; let x, y, z; (", " = {});"},
{"var x, y, z; (", " = {});"},
{"'use strict'; let x, y, z; for (x in ", " = {});"},
{"'use strict'; let x, y, z; for (x of ", " = {});"},
{"var x, y, z; for (x in ", " = {});"},
{"var x, y, z; for (x of ", " = {});"},
{NULL, NULL}};
// clang-format off
const char* data[] = {
"x",
"{ x : y }",
"{ x : foo().y }",
"{ x : foo()[y] }",
"{ x : y.z }",
"{ x : y[z] }",
"{ x : { y } }",
"{ x : { foo: y } }",
"{ x : { foo: foo().y } }",
"{ x : { foo: foo()[y] } }",
"{ x : { foo: y.z } }",
"{ x : { foo: y[z] } }",
"{ x : [ y ] }",
"{ x : [ foo().y ] }",
"{ x : [ foo()[y] ] }",
"{ x : [ y.z ] }",
"{ x : [ y[z] ] }",
"{ x : y = 10 }",
"{ x : foo().y = 10 }",
"{ x : foo()[y] = 10 }",
"{ x : y.z = 10 }",
"{ x : y[z] = 10 }",
"{ x : { y = 10 } = {} }",
"{ x : { foo: y = 10 } = {} }",
"{ x : { foo: foo().y = 10 } = {} }",
"{ x : { foo: foo()[y] = 10 } = {} }",
"{ x : { foo: y.z = 10 } = {} }",
"{ x : { foo: y[z] = 10 } = {} }",
"{ x : [ y = 10 ] = {} }",
"{ x : [ foo().y = 10 ] = {} }",
"{ x : [ foo()[y] = 10 ] = {} }",
"{ x : [ y.z = 10 ] = {} }",
"{ x : [ y[z] = 10 ] = {} }",
"[ x ]",
"[ foo().x ]",
"[ foo()[x] ]",
"[ x.y ]",
"[ x[y] ]",
"[ { x } ]",
"[ { x : y } ]",
"[ { x : foo().y } ]",
"[ { x : foo()[y] } ]",
"[ { x : x.y } ]",
"[ { x : x[y] } ]",
"[ [ x ] ]",
"[ [ foo().x ] ]",
"[ [ foo()[x] ] ]",
"[ [ x.y ] ]",
"[ [ x[y] ] ]",
"[ x = 10 ]",
"[ foo().x = 10 ]",
"[ foo()[x] = 10 ]",
"[ x.y = 10 ]",
"[ x[y] = 10 ]",
"[ { x = 10 } = {} ]",
"[ { x : y = 10 } = {} ]",
"[ { x : foo().y = 10 } = {} ]",
"[ { x : foo()[y] = 10 } = {} ]",
"[ { x : x.y = 10 } = {} ]",
"[ { x : x[y] = 10 } = {} ]",
"[ [ x = 10 ] = {} ]",
"[ [ foo().x = 10 ] = {} ]",
"[ [ foo()[x] = 10 ] = {} ]",
"[ [ x.y = 10 ] = {} ]",
"[ [ x[y] = 10 ] = {} ]",
"{ x : y }",
"{ x : y = 1 }",
"{ x }",
"{ x, y, z }",
"{ x = 1, y: z, z: y }",
"{x = 42, y = 15}",
"[x]",
"[x = 1]",
"[x,y,z]",
"[x, y = 42, z]",
"{ x : x, y : y }",
"{ x : x = 1, y : y }",
"{ x : x, y : y = 42 }",
"[]",
"{}",
"[{x:x, y:y}, [,x,z,]]",
"[{x:x = 1, y:y = 2}, [z = 3, z = 4, z = 5]]",
"[x,,y]",
"[(x),,(y)]",
"[(x)]",
"{42 : x}",
"{42 : x = 42}",
"{42e-2 : x}",
"{42e-2 : x = 42}",
"{'hi' : x}",
"{'hi' : x = 42}",
"{var: x}",
"{var: x = 42}",
"{var: (x) = 42}",
"{[x] : z}",
"{[1+1] : z}",
"{[1+1] : (z)}",
"{[foo()] : z}",
"{[foo()] : (z)}",
"{[foo()] : foo().bar}",
"{[foo()] : foo()['bar']}",
"{[foo()] : this.bar}",
"{[foo()] : this['bar']}",
"{[foo()] : 'foo'.bar}",
"{[foo()] : 'foo'['bar']}",
"[...x]",
"[x,y,...z]",
"[x,,...z]",
"{ x: y } = z",
"[x, y] = z",
"{ x: y } = { z }",
"[x, y] = { z }",
"{ x: y } = [ z ]",
"[x, y] = [ z ]",
"[((x, y) => z).x]",
"{x: ((y, z) => z).x}",
"[((x, y) => z)['x']]",
"{x: ((y, z) => z)['x']}",
"{x: { y = 10 } }",
"[(({ x } = { x: 1 }) => x).a]",
NULL};
// clang-format on
static const ParserFlag always_flags[] = {
kAllowHarmonyDestructuringAssignment, kAllowHarmonyDestructuring,
kAllowHarmonyDefaultParameters};
RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
const char* empty_context_data[][2] = {
{"'use strict';", ""}, {"", ""}, {NULL, NULL}};
// CoverInitializedName ambiguity handling in various contexts
const char* ambiguity_data[] = {
"var foo = { x = 10 } = {};",
"var foo = { q } = { x = 10 } = {};",
"var foo; foo = { x = 10 } = {};",
"var foo; foo = { q } = { x = 10 } = {};",
"var x; ({ x = 10 } = {});",
"var q, x; ({ q } = { x = 10 } = {});",
"var x; [{ x = 10 } = {}]",
"var x; (true ? { x = true } = {} : { x = false } = {})",
"var q, x; (q, { x = 10 } = {});",
"var { x = 10 } = { x = 20 } = {};",
"var { x = 10 } = (o = { x = 20 } = {});",
"var x; (({ x = 10 } = { x = 20 } = {}) => x)({})",
NULL,
};
RunParserSyncTest(empty_context_data, ambiguity_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
TEST(DestructuringAssignmentNegativeTests) {
const char* context_data[][2] = {
{"'use strict'; let x, y, z; (", " = {});"},
{"var x, y, z; (", " = {});"},
{"'use strict'; let x, y, z; for (x in ", " = {});"},
{"'use strict'; let x, y, z; for (x of ", " = {});"},
{"var x, y, z; for (x in ", " = {});"},
{"var x, y, z; for (x of ", " = {});"},
{NULL, NULL}};
// clang-format off
const char* data[] = {
"{ x : ++y }",
"{ x : y * 2 }",
"{ ...x }",
"{ get x() {} }",
"{ set x() {} }",
"{ x: y() }",
"{ this }",
"{ x: this }",
"{ x: this = 1 }",
"{ super }",
"{ x: super }",
"{ x: super = 1 }",
"{ new.target }",
"{ x: new.target }",
"{ x: new.target = 1 }",
"[x--]",
"[--x = 1]",
"[x()]",
"[this]",
"[this = 1]",
"[new.target]",
"[new.target = 1]",
"[super]",
"[super = 1]",
"[function f() {}]",
"[50]",
"[(50)]",
"[(function() {})]",
"[(foo())]",
"{ x: 50 }",
"{ x: (50) }",
"['str']",
"{ x: 'str' }",
"{ x: ('str') }",
"{ x: (foo()) }",
"{ x: (function() {}) }",
"{ x: y } = 'str'",
"[x, y] = 'str'",
"[(x,y) => z]",
"{x: (y) => z}",
"[x, ...y, z]",
"[...x,]",
"[x, y, ...z = 1]",
"[...z = 1]",
NULL};
// clang-format on
static const ParserFlag always_flags[] = {
kAllowHarmonyDestructuringAssignment, kAllowHarmonyDestructuring,
kAllowHarmonyDefaultParameters};
RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
const char* empty_context_data[][2] = {
{"'use strict';", ""}, {"", ""}, {NULL, NULL}};
// CoverInitializedName ambiguity handling in various contexts
const char* ambiguity_data[] = {
"var foo = { x = 10 };",
"var foo = { q } = { x = 10 };",
"var foo; foo = { x = 10 };",
"var foo; foo = { q } = { x = 10 };",
"var x; ({ x = 10 });",
"var q, x; ({ q } = { x = 10 });",
"var x; [{ x = 10 }]",
"var x; (true ? { x = true } : { x = false })",
"var q, x; (q, { x = 10 });",
"var { x = 10 } = { x = 20 };",
"var { x = 10 } = (o = { x = 20 });",
"var x; (({ x = 10 } = { x = 20 }) => x)({})",
NULL,
};
RunParserSyncTest(empty_context_data, ambiguity_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
}
TEST(DestructuringDisallowPatternsInForVarIn) {
i::FLAG_harmony_destructuring_bind = true;
static const ParserFlag always_flags[] = {kAllowHarmonyDestructuring};
......@@ -7013,9 +7274,8 @@ TEST(DefaultParametersYieldInInitializers) {
kSuccess, NULL, 0, always_flags, arraysize(always_flags));
RunParserSyncTest(sloppy_arrow_context_data, parameter_data, kSuccess, NULL,
0, always_flags, arraysize(always_flags));
// TODO(wingo): Will change to kSuccess when destructuring assignment lands.
RunParserSyncTest(sloppy_arrow_context_data, destructuring_assignment_data,
kError, NULL, 0, always_flags, arraysize(always_flags));
kSuccess, NULL, 0, always_flags, arraysize(always_flags));
RunParserSyncTest(strict_function_context_data, parameter_data, kError, NULL,
0, always_flags, arraysize(always_flags));
......
This diff is collapsed.
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