[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}
This commit is contained in:
caitpotter88 2015-12-04 09:20:10 -08:00 committed by Commit bot
parent 77774035d8
commit b634a61d84
23 changed files with 1495 additions and 99 deletions

View File

@ -394,5 +394,12 @@ void AstExpressionVisitor::VisitSuperCallReference(SuperCallReference* expr) {
}
void AstExpressionVisitor::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* expr) {
VisitExpression(expr);
RECURSE(Visit(expr->expression()));
}
} // namespace internal
} // namespace v8

View File

@ -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());
}

View File

@ -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());

View File

@ -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,

View File

@ -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

View File

@ -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") \

View File

@ -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[] = {

View File

@ -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.

View File

@ -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));

View File

@ -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) {

View File

@ -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);

View File

@ -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) \

View File

@ -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.

View File

@ -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();

View File

@ -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") \

View File

@ -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_;
};

View File

@ -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

View File

@ -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

View File

@ -11,13 +11,15 @@ namespace v8 {
namespace internal {
void Parser::PatternRewriter::DeclareAndInitializeVariables(
Block* block, const DeclarationDescriptor* declaration_descriptor,
const DeclarationParsingResult::Declaration* declaration,
ZoneList<const AstRawString*>* names, bool* ok) {
PatternRewriter rewriter;
rewriter.scope_ = declaration_descriptor->scope;
rewriter.parser_ = declaration_descriptor->parser;
rewriter.context_ = BINDING;
rewriter.pattern_ = declaration->pattern;
rewriter.initializer_position_ = declaration->initializer_position;
rewriter.block_ = block;
@ -29,8 +31,85 @@ void Parser::PatternRewriter::DeclareAndInitializeVariables(
}
void Parser::PatternRewriter::RewriteDestructuringAssignment(
Parser* parser, RewritableAssignmentExpression* to_rewrite, Scope* scope,
bool* ok) {
PatternRewriter rewriter;
DCHECK(!to_rewrite->is_rewritten());
rewriter.scope_ = scope;
rewriter.parser_ = parser;
rewriter.context_ = ASSIGNMENT;
rewriter.pattern_ = to_rewrite;
rewriter.block_ = nullptr;
rewriter.descriptor_ = nullptr;
rewriter.names_ = nullptr;
rewriter.ok_ = ok;
rewriter.RecurseIntoSubpattern(rewriter.pattern_, nullptr);
}
bool Parser::PatternRewriter::IsAssignmentContext(PatternContext c) const {
return c == ASSIGNMENT || c == ASSIGNMENT_INITIALIZER;
}
bool Parser::PatternRewriter::IsBindingContext(PatternContext c) const {
return c == BINDING || c == INITIALIZER;
}
Parser::PatternRewriter::PatternContext
Parser::PatternRewriter::SetAssignmentContextIfNeeded(Expression* node) {
PatternContext old_context = context();
if (node->IsAssignment() && node->AsAssignment()->op() == Token::ASSIGN) {
set_context(ASSIGNMENT);
}
return old_context;
}
Parser::PatternRewriter::PatternContext
Parser::PatternRewriter::SetInitializerContextIfNeeded(Expression* node) {
// Set appropriate initializer context for BindingElement and
// AssignmentElement nodes
PatternContext old_context = context();
bool is_destructuring_assignment =
node->IsRewritableAssignmentExpression() &&
!node->AsRewritableAssignmentExpression()->is_rewritten();
bool is_assignment =
node->IsAssignment() && node->AsAssignment()->op() == Token::ASSIGN;
if (is_destructuring_assignment || is_assignment) {
switch (old_context) {
case BINDING:
set_context(INITIALIZER);
break;
case ASSIGNMENT:
set_context(ASSIGNMENT_INITIALIZER);
break;
default:
break;
}
}
return old_context;
}
void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
Expression* value = current_value_;
if (IsAssignmentContext()) {
// In an assignment context, simply perform the assignment
Assignment* assignment = factory()->NewAssignment(
Token::ASSIGN, pattern, value, pattern->position());
block_->statements()->Add(
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
zone());
return;
}
descriptor_->scope->RemoveUnresolved(pattern);
// Declare variable.
@ -48,15 +127,14 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
// For let/const declarations in harmony mode, we can also immediately
// pre-resolve the proxy because it resides in the same scope as the
// declaration.
Parser* parser = descriptor_->parser;
const AstRawString* name = pattern->raw_name();
VariableProxy* proxy = parser->NewUnresolved(name, descriptor_->mode);
VariableProxy* proxy = parser_->NewUnresolved(name, descriptor_->mode);
Declaration* declaration = factory()->NewVariableDeclaration(
proxy, descriptor_->mode, descriptor_->scope,
descriptor_->declaration_pos);
Variable* var = parser->Declare(declaration, descriptor_->declaration_kind,
descriptor_->mode != VAR, ok_,
descriptor_->hoist_scope);
Variable* var =
parser_->Declare(declaration, descriptor_->declaration_kind,
descriptor_->mode != VAR, ok_, descriptor_->hoist_scope);
if (!*ok_) return;
DCHECK_NOT_NULL(var);
DCHECK(!proxy->is_resolved() || proxy->var() == var);
@ -68,7 +146,7 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
? descriptor_->scope
: descriptor_->scope->DeclarationScope();
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
parser->ReportMessage(MessageTemplate::kTooManyVariables);
parser_->ReportMessage(MessageTemplate::kTooManyVariables);
*ok_ = false;
return;
}
@ -216,8 +294,7 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
Variable* Parser::PatternRewriter::CreateTempVar(Expression* value) {
auto temp = descriptor_->parser->scope_->NewTemporary(
ast_value_factory()->empty_string());
auto temp = scope()->NewTemporary(ast_value_factory()->empty_string());
if (value != nullptr) {
auto assignment = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(temp), value,
@ -231,28 +308,97 @@ Variable* Parser::PatternRewriter::CreateTempVar(Expression* value) {
}
void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern) {
auto temp = CreateTempVar(current_value_);
void Parser::PatternRewriter::VisitRewritableAssignmentExpression(
RewritableAssignmentExpression* node) {
if (!IsAssignmentContext()) {
// Mark the assignment as rewritten to prevent redundant rewriting, and
// perform BindingPattern rewriting
DCHECK(!node->is_rewritten());
node->Rewrite(node->expression());
return node->expression()->Accept(this);
}
block_->statements()->Add(descriptor_->parser->BuildAssertIsCoercible(temp),
zone());
if (node->is_rewritten()) return;
DCHECK(IsAssignmentContext());
Assignment* assign = node->expression()->AsAssignment();
DCHECK_NOT_NULL(assign);
DCHECK_EQ(Token::ASSIGN, assign->op());
auto initializer = assign->value();
auto value = initializer;
if (IsInitializerContext()) {
// let {<pattern> = <init>} = <value>
// becomes
// temp = <value>;
// <pattern> = temp === undefined ? <init> : temp;
auto temp_var = CreateTempVar(current_value_);
Expression* is_undefined = factory()->NewCompareOperation(
Token::EQ_STRICT, factory()->NewVariableProxy(temp_var),
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
value = factory()->NewConditional(is_undefined, initializer,
factory()->NewVariableProxy(temp_var),
RelocInfo::kNoPosition);
}
PatternContext old_context = SetAssignmentContextIfNeeded(initializer);
int pos = assign->position();
Block* old_block = block_;
block_ = factory()->NewBlock(nullptr, 8, false, pos);
Variable* temp = nullptr;
Expression* pattern = assign->target();
Expression* old_value = current_value_;
current_value_ = value;
if (pattern->IsObjectLiteral()) {
VisitObjectLiteral(pattern->AsObjectLiteral(), &temp);
} else {
DCHECK(pattern->IsArrayLiteral());
VisitArrayLiteral(pattern->AsArrayLiteral(), &temp);
}
DCHECK_NOT_NULL(temp);
current_value_ = old_value;
Expression* expr = factory()->NewDoExpression(block_, temp, pos);
node->Rewrite(expr);
block_ = old_block;
if (block_) {
block_->statements()->Add(factory()->NewExpressionStatement(expr, pos),
zone());
}
return set_context(old_context);
}
void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern,
Variable** temp_var) {
auto temp = *temp_var = CreateTempVar(current_value_);
block_->statements()->Add(parser_->BuildAssertIsCoercible(temp), zone());
for (ObjectLiteralProperty* property : *pattern->properties()) {
PatternContext context = SetInitializerContextIfNeeded(property->value());
RecurseIntoSubpattern(
property->value(),
factory()->NewProperty(factory()->NewVariableProxy(temp),
property->key(), RelocInfo::kNoPosition));
set_context(context);
}
}
void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) {
auto temp = CreateTempVar(current_value_);
void Parser::PatternRewriter::VisitObjectLiteral(ObjectLiteral* node) {
Variable* temp_var = nullptr;
VisitObjectLiteral(node, &temp_var);
}
block_->statements()->Add(descriptor_->parser->BuildAssertIsCoercible(temp),
zone());
auto iterator = CreateTempVar(descriptor_->parser->GetIterator(
void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
Variable** temp_var) {
auto temp = *temp_var = CreateTempVar(current_value_);
block_->statements()->Add(parser_->BuildAssertIsCoercible(temp), zone());
auto iterator = CreateTempVar(parser_->GetIterator(
factory()->NewVariableProxy(temp), factory(), RelocInfo::kNoPosition));
auto done = CreateTempVar(
factory()->NewBooleanLiteral(false, RelocInfo::kNoPosition));
@ -266,19 +412,19 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) {
break;
}
PatternContext context = SetInitializerContextIfNeeded(value);
// if (!done) {
// result = IteratorNext(iterator);
// v = (done = result.done) ? undefined : result.value;
// }
auto next_block =
factory()->NewBlock(nullptr, 2, true, RelocInfo::kNoPosition);
next_block->statements()->Add(
factory()->NewExpressionStatement(
descriptor_->parser->BuildIteratorNextResult(
factory()->NewVariableProxy(iterator), result,
RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
zone());
next_block->statements()->Add(factory()->NewExpressionStatement(
parser_->BuildIteratorNextResult(
factory()->NewVariableProxy(iterator),
result, RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
zone());
auto assign_to_done = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(done),
@ -315,6 +461,7 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) {
if (!(value->IsLiteral() && value->AsLiteral()->raw_value()->IsTheHole())) {
RecurseIntoSubpattern(value, factory()->NewVariableProxy(v));
}
set_context(context);
}
if (spread != nullptr) {
@ -325,7 +472,7 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) {
empty_exprs,
// Reuse pattern's literal index - it is unused since there is no
// actual literal allocated.
node->literal_index(), is_strong(descriptor_->parser->language_mode()),
node->literal_index(), is_strong(scope()->language_mode()),
RelocInfo::kNoPosition));
auto arguments = new (zone()) ZoneList<Expression*>(2, zone());
@ -351,29 +498,58 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) {
}
void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) {
Variable* temp_var = nullptr;
VisitArrayLiteral(node, &temp_var);
}
void Parser::PatternRewriter::VisitAssignment(Assignment* node) {
// let {<pattern> = <init>} = <value>
// becomes
// temp = <value>;
// <pattern> = temp === undefined ? <init> : temp;
DCHECK(node->op() == Token::ASSIGN);
DCHECK_EQ(Token::ASSIGN, node->op());
auto initializer = node->value();
auto value = initializer;
auto temp = CreateTempVar(current_value_);
Expression* is_undefined = factory()->NewCompareOperation(
Token::EQ_STRICT, factory()->NewVariableProxy(temp),
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
Expression* initializer = node->value();
if (descriptor_->declaration_kind == DeclarationDescriptor::PARAMETER &&
descriptor_->scope->is_arrow_scope()) {
// TODO(adamk): Only call this if necessary.
RewriteParameterInitializerScope(
descriptor_->parser->stack_limit(), initializer,
descriptor_->scope->outer_scope(), descriptor_->scope);
if (IsInitializerContext()) {
Expression* is_undefined = factory()->NewCompareOperation(
Token::EQ_STRICT, factory()->NewVariableProxy(temp),
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
RelocInfo::kNoPosition);
value = factory()->NewConditional(is_undefined, initializer,
factory()->NewVariableProxy(temp),
RelocInfo::kNoPosition);
}
Expression* value = factory()->NewConditional(
is_undefined, initializer, factory()->NewVariableProxy(temp),
RelocInfo::kNoPosition);
if (IsBindingContext() &&
descriptor_->declaration_kind == DeclarationDescriptor::PARAMETER &&
scope()->is_arrow_scope()) {
RewriteParameterInitializerScope(parser_->stack_limit(), initializer,
scope()->outer_scope(), scope());
}
PatternContext old_context = SetAssignmentContextIfNeeded(initializer);
RecurseIntoSubpattern(node->target(), value);
set_context(old_context);
}
// =============== AssignmentPattern only ==================
void Parser::PatternRewriter::VisitProperty(v8::internal::Property* node) {
DCHECK(IsAssignmentContext());
auto value = current_value_;
Assignment* assignment =
factory()->NewAssignment(Token::ASSIGN, node, value, node->position());
block_->statements()->Add(
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
zone());
}
@ -414,7 +590,6 @@ NOT_A_PATTERN(IfStatement)
NOT_A_PATTERN(ImportDeclaration)
NOT_A_PATTERN(Literal)
NOT_A_PATTERN(NativeFunctionLiteral)
NOT_A_PATTERN(Property)
NOT_A_PATTERN(RegExpLiteral)
NOT_A_PATTERN(ReturnStatement)
NOT_A_PATTERN(SloppyBlockFunctionStatement)

View File

@ -114,6 +114,7 @@ class ParserBase : public Traits {
allow_harmony_rest_parameters_(false),
allow_harmony_default_parameters_(false),
allow_harmony_destructuring_bind_(false),
allow_harmony_destructuring_assignment_(false),
allow_strong_mode_(false),
allow_legacy_const_(true),
allow_harmony_do_expressions_(false) {}
@ -130,6 +131,7 @@ class ParserBase : public Traits {
ALLOW_ACCESSORS(harmony_rest_parameters);
ALLOW_ACCESSORS(harmony_default_parameters);
ALLOW_ACCESSORS(harmony_destructuring_bind);
ALLOW_ACCESSORS(harmony_destructuring_assignment);
ALLOW_ACCESSORS(strong_mode);
ALLOW_ACCESSORS(legacy_const);
ALLOW_ACCESSORS(harmony_do_expressions);
@ -175,6 +177,15 @@ class ParserBase : public Traits {
Scope* outer_scope_;
};
struct DestructuringAssignment {
public:
DestructuringAssignment(ExpressionT expression, Scope* scope)
: assignment(expression), scope(scope) {}
ExpressionT assignment;
Scope* scope;
};
class FunctionState BASE_EMBEDDED {
public:
FunctionState(FunctionState** function_state_stack, Scope** scope_stack,
@ -227,6 +238,15 @@ class ParserBase : public Traits {
typename Traits::Type::Factory* factory() { return factory_; }
const List<DestructuringAssignment>& destructuring_assignments_to_rewrite()
const {
return destructuring_assignments_to_rewrite_;
}
void AddDestructuringAssignment(DestructuringAssignment pair) {
destructuring_assignments_to_rewrite_.Add(pair);
}
private:
// Used to assign an index to each literal that needs materialization in
// the function. Includes regexp literals, and boilerplate for object and
@ -255,6 +275,11 @@ class ParserBase : public Traits {
FunctionState* outer_function_state_;
Scope** scope_stack_;
Scope* outer_scope_;
List<DestructuringAssignment> destructuring_assignments_to_rewrite_;
void RewriteDestructuringAssignments();
typename Traits::Type::Factory* factory_;
friend class ParserTraits;
@ -460,6 +485,10 @@ class ParserBase : public Traits {
ok);
}
void CheckDestructuringElement(ExpressionT element,
ExpressionClassifier* classifier, int beg_pos,
int end_pos);
// Checking the name of a function literal. This has to be done after parsing
// the function, since the function can declare itself strict.
void CheckFunctionName(LanguageMode language_mode, IdentifierT function_name,
@ -538,12 +567,20 @@ class ParserBase : public Traits {
void ReportClassifierError(const ExpressionClassifier::Error& error) {
Traits::ReportMessageAt(error.location, error.message, error.arg,
kSyntaxError);
error.type);
}
void ValidateExpression(const ExpressionClassifier* classifier, bool* ok) {
if (!classifier->is_valid_expression()) {
ReportClassifierError(classifier->expression_error());
if (!classifier->is_valid_expression() ||
classifier->has_cover_initialized_name()) {
const Scanner::Location& a = classifier->expression_error().location;
const Scanner::Location& b =
classifier->cover_initialized_name_error().location;
if (a.beg_pos < 0 || (b.beg_pos >= 0 && a.beg_pos > b.beg_pos)) {
ReportClassifierError(classifier->cover_initialized_name_error());
} else {
ReportClassifierError(classifier->expression_error());
}
*ok = false;
}
}
@ -692,6 +729,8 @@ class ParserBase : public Traits {
ExpressionT ParseExpression(bool accept_IN, bool* ok);
ExpressionT ParseExpression(bool accept_IN, ExpressionClassifier* classifier,
bool* ok);
ExpressionT ParseExpression(bool accept_IN, int flags,
ExpressionClassifier* classifier, bool* ok);
ExpressionT ParseArrayLiteral(ExpressionClassifier* classifier, bool* ok);
ExpressionT ParsePropertyName(IdentifierT* name, bool* is_get, bool* is_set,
bool* is_static, bool* is_computed_name,
@ -705,9 +744,23 @@ class ParserBase : public Traits {
typename Traits::Type::ExpressionList ParseArguments(
Scanner::Location* first_spread_pos, ExpressionClassifier* classifier,
bool* ok);
ExpressionT ParseAssignmentExpression(bool accept_IN,
enum AssignmentExpressionFlags {
kIsLeftHandSide = 0,
kIsRightHandSide = 1 << 0,
kIsPatternElement = 1 << 1,
kIsPossibleArrowFormals = 1 << 2
};
ExpressionT ParseAssignmentExpression(bool accept_IN, int flags,
ExpressionClassifier* classifier,
bool* ok);
ExpressionT ParseAssignmentExpression(bool accept_IN,
ExpressionClassifier* classifier,
bool* ok) {
return ParseAssignmentExpression(accept_IN, kIsLeftHandSide, classifier,
ok);
}
ExpressionT ParseYieldExpression(ExpressionClassifier* classifier, bool* ok);
ExpressionT ParseConditionalExpression(bool accept_IN,
ExpressionClassifier* classifier,
@ -755,10 +808,29 @@ class ParserBase : public Traits {
ExpressionT CheckAndRewriteReferenceExpression(
ExpressionT expression, int beg_pos, int end_pos,
MessageTemplate::Template message, bool* ok);
ExpressionT ClassifyAndRewriteReferenceExpression(
ExpressionClassifier* classifier, ExpressionT expression, int beg_pos,
int end_pos, MessageTemplate::Template message,
ParseErrorType type = kSyntaxError);
ExpressionT CheckAndRewriteReferenceExpression(
ExpressionT expression, int beg_pos, int end_pos,
MessageTemplate::Template message, ParseErrorType type, bool* ok);
bool IsValidReferenceExpression(ExpressionT expression);
bool IsAssignableIdentifier(ExpressionT expression) {
if (!Traits::IsIdentifier(expression)) return false;
if (is_strict(language_mode()) &&
Traits::IsEvalOrArguments(Traits::AsIdentifier(expression))) {
return false;
}
if (is_strong(language_mode()) &&
Traits::IsUndefined(Traits::AsIdentifier(expression))) {
return false;
}
return true;
}
// Used to validate property names in object literals and class literals
enum PropertyKind {
kAccessorProperty,
@ -847,6 +919,7 @@ class ParserBase : public Traits {
bool allow_harmony_rest_parameters_;
bool allow_harmony_default_parameters_;
bool allow_harmony_destructuring_bind_;
bool allow_harmony_destructuring_assignment_;
bool allow_strong_mode_;
bool allow_legacy_const_;
bool allow_harmony_do_expressions_;
@ -959,6 +1032,11 @@ class PreParserExpression {
right->IsSpreadExpression()));
}
static PreParserExpression AssignmentPattern() {
return PreParserExpression(TypeField::encode(kExpression) |
ExpressionTypeField::encode(kAssignmentPattern));
}
static PreParserExpression ObjectLiteral() {
return PreParserExpression(TypeField::encode(kObjectLiteralExpression));
}
@ -1024,6 +1102,11 @@ class PreParserExpression {
return PreParserIdentifier(IdentifierTypeField::decode(code_));
}
bool IsAssignmentPattern() const {
return TypeField::decode(code_) == kExpression &&
ExpressionTypeField::decode(code_) == kAssignmentPattern;
}
bool IsObjectLiteral() const {
return TypeField::decode(code_) == kObjectLiteralExpression;
}
@ -1131,7 +1214,8 @@ class PreParserExpression {
kPropertyExpression,
kCallExpression,
kSuperCallReference,
kNoTemplateTagExpression
kNoTemplateTagExpression,
kAssignmentPattern
};
explicit PreParserExpression(uint32_t expression_code)
@ -1148,6 +1232,7 @@ class PreParserExpression {
typedef BitField<PreParserIdentifier::Type, TypeField::kNext, 10>
IdentifierTypeField;
typedef BitField<bool, TypeField::kNext, 1> HasRestField;
typedef BitField<bool, TypeField::kNext, 1> HasCoverInitializedNameField;
uint32_t code_;
};
@ -1312,12 +1397,21 @@ class PreParserFactory {
PreParserExpression right, int pos) {
return PreParserExpression::Default();
}
PreParserExpression NewRewritableAssignmentExpression(
PreParserExpression expression) {
return expression;
}
PreParserExpression NewAssignment(Token::Value op,
PreParserExpression left,
PreParserExpression right,
int pos) {
return PreParserExpression::Default();
}
PreParserExpression NewAssignmentPattern(PreParserExpression pattern,
int pos) {
DCHECK(pattern->IsObjectLiteral() || pattern->IsArrayLiteral());
return PreParserExpression::AssignmentPattern();
}
PreParserExpression NewYield(PreParserExpression generator_object,
PreParserExpression expression,
Yield::Kind yield_kind,
@ -1749,6 +1843,10 @@ class PreParserTraits {
PreParserExpressionList args,
int pos);
inline void RewriteDestructuringAssignments() {}
inline void QueueDestructuringAssignmentForRewriting(PreParserExpression) {}
private:
PreParser* pre_parser_;
};
@ -2394,8 +2492,12 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
// Heuristically try to detect immediately called functions before
// seeing the call parentheses.
parenthesized_function_ = (peek() == Token::FUNCTION);
ExpressionT expr = this->ParseExpression(true, classifier, CHECK_OK);
ExpressionT expr = this->ParseExpression(true, kIsPossibleArrowFormals,
classifier, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
if (peek() != Token::ARROW) {
ValidateExpression(classifier, CHECK_OK);
}
return expr;
}
@ -2463,17 +2565,23 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
}
// Precedence = 1
template <class Traits>
typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
bool accept_IN, ExpressionClassifier* classifier, bool* ok) {
return ParseExpression(accept_IN, kIsLeftHandSide, classifier, ok);
}
template <class Traits>
typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
bool accept_IN, int flags, ExpressionClassifier* classifier, bool* ok) {
// Expression ::
// AssignmentExpression
// Expression ',' AssignmentExpression
ExpressionClassifier binding_classifier;
ExpressionT result =
this->ParseAssignmentExpression(accept_IN, &binding_classifier, CHECK_OK);
ExpressionT result = this->ParseAssignmentExpression(
accept_IN, flags, &binding_classifier, CHECK_OK);
classifier->Accumulate(binding_classifier,
ExpressionClassifier::AllProductions);
bool is_simple_parameter_list = this->IsIdentifier(result);
@ -2498,7 +2606,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
}
int pos = position();
ExpressionT right = this->ParseAssignmentExpression(
accept_IN, &binding_classifier, CHECK_OK);
accept_IN, flags, &binding_classifier, CHECK_OK);
if (is_rest) right = factory()->NewSpread(right, pos);
is_simple_parameter_list =
is_simple_parameter_list && this->IsIdentifier(right);
@ -2509,6 +2617,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression(
if (!is_simple_parameter_list || seen_rest) {
classifier->RecordNonSimpleParameter();
}
return result;
}
@ -2525,7 +2634,6 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
int first_spread_index = -1;
Expect(Token::LBRACK, CHECK_OK);
while (peek() != Token::RBRACK) {
bool seen_spread = false;
ExpressionT elem = this->EmptyExpression();
if (peek() == Token::COMMA) {
if (is_strong(language_mode())) {
@ -2541,18 +2649,31 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
ExpressionT argument =
this->ParseAssignmentExpression(true, classifier, CHECK_OK);
elem = factory()->NewSpread(argument, start_pos);
seen_spread = true;
if (first_spread_index < 0) {
first_spread_index = values->length();
}
CheckDestructuringElement(argument, classifier, start_pos,
scanner()->location().end_pos);
if (peek() == Token::COMMA) {
classifier->RecordPatternError(
Scanner::Location(start_pos, scanner()->location().end_pos),
MessageTemplate::kElementAfterRest);
}
} else {
elem = this->ParseAssignmentExpression(true, classifier, CHECK_OK);
elem = this->ParseAssignmentExpression(true, kIsPatternElement,
classifier, CHECK_OK);
if (!this->IsValidReferenceExpression(elem) &&
!classifier->is_valid_assignment_pattern()) {
classifier->RecordPatternError(
Scanner::Location(pos, scanner()->location().end_pos),
MessageTemplate::kInvalidDestructuringTarget);
}
}
values->Add(elem, zone_);
if (peek() != Token::RBRACK) {
if (seen_spread) {
BindingPatternUnexpectedToken(classifier);
}
Expect(Token::COMMA, CHECK_OK);
}
}
@ -2676,8 +2797,18 @@ ParserBase<Traits>::ParsePropertyDefinition(
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
}
Consume(Token::COLON);
int pos = peek_position();
value = this->ParseAssignmentExpression(
true, classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
true, kIsPatternElement, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
if (!this->IsValidReferenceExpression(value) &&
!classifier->is_valid_assignment_pattern()) {
classifier->RecordPatternError(
Scanner::Location(pos, scanner()->location().end_pos),
MessageTemplate::kInvalidDestructuringTarget);
}
return factory()->NewObjectLiteralProperty(name_expression, value, false,
*is_computed_name);
}
@ -2712,7 +2843,6 @@ ParserBase<Traits>::ParsePropertyDefinition(
name, next_beg_pos, next_end_pos, scope_, factory());
if (peek() == Token::ASSIGN) {
this->ExpressionUnexpectedToken(classifier);
Consume(Token::ASSIGN);
ExpressionClassifier rhs_classifier;
ExpressionT rhs = this->ParseAssignmentExpression(
@ -2721,6 +2851,9 @@ ParserBase<Traits>::ParsePropertyDefinition(
ExpressionClassifier::ExpressionProductions);
value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs,
RelocInfo::kNoPosition);
classifier->RecordCoverInitializedNameError(
Scanner::Location(next_beg_pos, scanner()->location().end_pos),
MessageTemplate::kInvalidCoverInitializedName);
} else {
value = lhs;
}
@ -2737,7 +2870,10 @@ ParserBase<Traits>::ParsePropertyDefinition(
return this->EmptyObjectLiteralProperty();
}
BindingPatternUnexpectedToken(classifier);
// Method definitions are never valid in patterns.
classifier->RecordPatternError(
Scanner::Location(next_beg_pos, scanner()->location().end_pos),
MessageTemplate::kInvalidDestructuringTarget);
if (is_generator || peek() == Token::LPAREN) {
// MethodDefinition
@ -2961,7 +3097,7 @@ typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments(
// Precedence = 2
template <class Traits>
typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN, int flags,
ExpressionClassifier* classifier,
bool* ok) {
// AssignmentExpression ::
@ -2969,7 +3105,10 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
// ArrowFunction
// YieldExpression
// LeftHandSideExpression AssignmentOperator AssignmentExpression
bool is_rhs = flags & kIsRightHandSide;
bool is_pattern_element = flags & kIsPatternElement;
bool is_destructuring_assignment = false;
bool is_arrow_formals = flags & kIsPossibleArrowFormals;
int lhs_beg_pos = peek_position();
if (peek() == Token::YIELD && is_generator()) {
@ -3015,18 +3154,43 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
}
expression = this->ParseArrowFunctionLiteral(
accept_IN, parameters, arrow_formals_classifier, CHECK_OK);
if (is_pattern_element) {
classifier->RecordPatternError(
Scanner::Location(lhs_beg_pos, scanner()->location().end_pos),
MessageTemplate::kInvalidDestructuringTarget);
}
return expression;
}
if (this->IsValidReferenceExpression(expression)) {
arrow_formals_classifier.ForgiveAssignmentPatternError();
}
// "expression" was not itself an arrow function parameter list, but it might
// form part of one. Propagate speculative formal parameter error locations.
classifier->Accumulate(arrow_formals_classifier,
ExpressionClassifier::StandardProductions |
ExpressionClassifier::FormalParametersProductions);
classifier->Accumulate(
arrow_formals_classifier,
ExpressionClassifier::StandardProductions |
ExpressionClassifier::FormalParametersProductions |
ExpressionClassifier::CoverInitializedNameProduction);
bool maybe_pattern =
expression->IsObjectLiteral() || expression->IsArrayLiteral();
// bool binding_pattern =
// allow_harmony_destructuring_bind() && maybe_pattern && !is_rhs;
if (!Token::IsAssignmentOp(peek())) {
if (fni_ != NULL) fni_->Leave();
// Parsed conditional expression only (no assignment).
if (is_pattern_element && !this->IsValidReferenceExpression(expression) &&
!maybe_pattern) {
classifier->RecordPatternError(
Scanner::Location(lhs_beg_pos, scanner()->location().end_pos),
MessageTemplate::kInvalidDestructuringTarget);
} else if (is_rhs && maybe_pattern) {
ValidateExpression(classifier, CHECK_OK);
}
return expression;
}
@ -3035,9 +3199,21 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
BindingPatternUnexpectedToken(classifier);
}
expression = this->CheckAndRewriteReferenceExpression(
expression, lhs_beg_pos, scanner()->location().end_pos,
MessageTemplate::kInvalidLhsInAssignment, CHECK_OK);
if (allow_harmony_destructuring_assignment() && maybe_pattern &&
peek() == Token::ASSIGN) {
classifier->ForgiveCoverInitializedNameError();
ValidateAssignmentPattern(classifier, CHECK_OK);
is_destructuring_assignment = true;
} else if (is_arrow_formals) {
expression = this->ClassifyAndRewriteReferenceExpression(
classifier, expression, lhs_beg_pos, scanner()->location().end_pos,
MessageTemplate::kInvalidLhsInAssignment);
} else {
expression = this->CheckAndRewriteReferenceExpression(
expression, lhs_beg_pos, scanner()->location().end_pos,
MessageTemplate::kInvalidLhsInAssignment, CHECK_OK);
}
expression = this->MarkExpressionAsAssigned(expression);
Token::Value op = Next(); // Get assignment operator.
@ -3049,10 +3225,15 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
int pos = position();
ExpressionClassifier rhs_classifier;
ExpressionT right =
this->ParseAssignmentExpression(accept_IN, &rhs_classifier, CHECK_OK);
classifier->Accumulate(rhs_classifier,
ExpressionClassifier::ExpressionProductions);
int rhs_flags = flags;
rhs_flags &= ~(kIsPatternElement | kIsPossibleArrowFormals);
rhs_flags |= kIsRightHandSide;
ExpressionT right = this->ParseAssignmentExpression(
accept_IN, rhs_flags, &rhs_classifier, CHECK_OK);
classifier->Accumulate(
rhs_classifier, ExpressionClassifier::ExpressionProductions |
ExpressionClassifier::CoverInitializedNameProduction);
// TODO(1231235): We try to estimate the set of properties set by
// constructors. We define a new property whenever there is an
@ -3078,7 +3259,14 @@ ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN,
fni_->Leave();
}
return factory()->NewAssignment(op, expression, right, pos);
ExpressionT result = factory()->NewAssignment(op, expression, right, pos);
if (is_destructuring_assignment) {
result = factory()->NewRewritableAssignmentExpression(result);
Traits::QueueDestructuringAssignmentForRewriting(result);
}
return result;
}
template <class Traits>
@ -4041,6 +4229,8 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(
if (is_strict(language_mode()) || allow_harmony_sloppy()) {
this->CheckConflictingVarDeclarations(formal_parameters.scope, CHECK_OK);
}
Traits::RewriteDestructuringAssignments();
}
FunctionLiteralT function_literal = factory()->NewFunctionLiteral(
@ -4172,21 +4362,33 @@ typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::CheckAndRewriteReferenceExpression(
ExpressionT expression, int beg_pos, int end_pos,
MessageTemplate::Template message, ParseErrorType type, bool* ok) {
ExpressionClassifier classifier;
ExpressionT result = ClassifyAndRewriteReferenceExpression(
&classifier, expression, beg_pos, end_pos, message, type);
ValidateExpression(&classifier, ok);
if (!*ok) return this->EmptyExpression();
return result;
}
template <typename Traits>
typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::ClassifyAndRewriteReferenceExpression(
ExpressionClassifier* classifier, ExpressionT expression, int beg_pos,
int end_pos, MessageTemplate::Template message, ParseErrorType type) {
Scanner::Location location(beg_pos, end_pos);
if (this->IsIdentifier(expression)) {
if (is_strict(language_mode()) &&
this->IsEvalOrArguments(this->AsIdentifier(expression))) {
this->ReportMessageAt(location, MessageTemplate::kStrictEvalArguments,
kSyntaxError);
*ok = false;
return this->EmptyExpression();
classifier->RecordExpressionError(
location, MessageTemplate::kStrictEvalArguments, kSyntaxError);
return expression;
}
if (is_strong(language_mode()) &&
this->IsUndefined(this->AsIdentifier(expression))) {
this->ReportMessageAt(location, MessageTemplate::kStrongUndefined,
kSyntaxError);
*ok = false;
return this->EmptyExpression();
classifier->RecordExpressionError(
location, MessageTemplate::kStrongUndefined, kSyntaxError);
return expression;
}
}
if (expression->IsValidReferenceExpression()) {
@ -4198,9 +4400,31 @@ ParserBase<Traits>::CheckAndRewriteReferenceExpression(
ExpressionT error = this->NewThrowReferenceError(message, pos);
return factory()->NewProperty(expression, error, pos);
} else {
this->ReportMessageAt(location, message, type);
*ok = false;
return this->EmptyExpression();
classifier->RecordExpressionError(location, message, type);
return expression;
}
}
template <typename Traits>
bool ParserBase<Traits>::IsValidReferenceExpression(ExpressionT expression) {
return this->IsAssignableIdentifier(expression) || expression->IsProperty();
}
template <typename Traits>
void ParserBase<Traits>::CheckDestructuringElement(
ExpressionT expression, ExpressionClassifier* classifier, int begin,
int end) {
static const MessageTemplate::Template message =
MessageTemplate::kInvalidDestructuringTarget;
if (!this->IsAssignableIdentifier(expression)) {
const Scanner::Location location(begin, end);
classifier->RecordBindingPatternError(location, message);
if (!expression->IsProperty() &&
!(expression->IsObjectLiteral() || expression->IsArrayLiteral())) {
classifier->RecordAssignmentPatternError(location, message);
}
}
}

View File

@ -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

View File

@ -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));

View File

@ -0,0 +1,430 @@
// Copyright 2015 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-destructuring-assignment --harmony-destructuring-bind
// script-level tests
var ox, oy = {}, oz;
({
x: ox,
y: oy.value,
y2: oy["value2"],
z: ({ set v(val) { oz = val; } }).v
} = {
x: "value of x",
y: "value of y1",
y2: "value of y2",
z: "value of z"
});
assertEquals("value of x", ox);
assertEquals("value of y1", oy.value);
assertEquals("value of y2", oy.value2);
assertEquals("value of z", oz);
[ox, oy.value, oy["value2"], ...{ set v(val) { oz = val; } }.v] = [
1007,
798432,
555,
1, 2, 3, 4, 5
];
assertEquals(ox, 1007);
assertEquals(oy.value, 798432);
assertEquals(oy.value2, 555);
assertEquals(oz, [1, 2, 3, 4, 5]);
(function testInFunction() {
var x, y = {}, z;
({
x: x,
y: y.value,
y2: y["value2"],
z: ({ set v(val) { z = val; } }).v
} = {
x: "value of x",
y: "value of y1",
y2: "value of y2",
z: "value of z"
});
assertEquals("value of x", x);
assertEquals("value of y1", y.value);
assertEquals("value of y2", y.value2);
assertEquals("value of z", z);
[x, y.value, y["value2"], ...{ set v(val) { z = val; } }.v] = [
1007,
798432,
555,
1, 2, 3, 4, 5
];
assertEquals(x, 1007);
assertEquals(y.value, 798432);
assertEquals(y.value2, 555);
assertEquals(z, [1, 2, 3, 4, 5]);
})();
(function testArrowFunctionInitializers() {
var fn = (config = {
value: defaults.value,
nada: { nada: defaults.nada } = { nada: "nothing" }
} = { value: "BLAH" }) => config;
var defaults = {};
assertEquals({ value: "BLAH" }, fn());
assertEquals("BLAH", defaults.value);
assertEquals("nothing", defaults.nada);
})();
(function testArrowFunctionInitializers2() {
var fn = (config = [
defaults.value,
{ nada: defaults.nada } = { nada: "nothing" }
] = ["BLAH"]) => config;
var defaults = {};
assertEquals(["BLAH"], fn());
assertEquals("BLAH", defaults.value);
assertEquals("nothing", defaults.nada);
})();
(function testFunctionInitializers() {
function fn(config = {
value: defaults.value,
nada: { nada: defaults.nada } = { nada: "nothing" }
} = { value: "BLAH" }) {
return config;
}
var defaults = {};
assertEquals({ value: "BLAH" }, fn());
assertEquals("BLAH", defaults.value);
assertEquals("nothing", defaults.nada);
})();
(function testFunctionInitializers2() {
function fn(config = [
defaults.value,
{ nada: defaults.nada } = { nada: "nothing" }
] = ["BLAH"]) { return config; }
var defaults = {};
assertEquals(["BLAH"], fn());
assertEquals("BLAH", defaults.value);
assertEquals("nothing", defaults.nada);
})();
(function testDeclarationInitializers() {
var defaults = {};
var { value } = { value: defaults.value } = { value: "BLAH" };
assertEquals("BLAH", value);
assertEquals("BLAH", defaults.value);
})();
(function testDeclarationInitializers2() {
var defaults = {};
var [value] = [defaults.value] = ["BLAH"];
assertEquals("BLAH", value);
assertEquals("BLAH", defaults.value);
})();
(function testObjectLiteralProperty() {
var ext = {};
var obj = {
a: { b: ext.b, c: ext["c"], d: { set v(val) { ext.d = val; } }.v } = {
b: "b", c: "c", d: "d" }
};
assertEquals({ b: "b", c: "c", d: "d" }, ext);
assertEquals({ a: { b: "b", c: "c", d: "d" } }, obj);
})();
(function testArrayLiteralProperty() {
var ext = {};
var obj = [
...[ ext.b, ext["c"], { set v(val) { ext.d = val; } }.v ] = [
"b", "c", "d" ]
];
assertEquals({ b: "b", c: "c", d: "d" }, ext);
assertEquals([ "b", "c", "d" ], obj);
})();
// TODO(caitp): add similar test for ArrayPatterns, once Proxies support
// delegating symbol-keyed get/set.
(function testObjectPatternOperationOrder() {
var steps = [];
var store = {};
function computePropertyName(name) {
steps.push("compute name: " + name);
return name;
}
function loadValue(descr, value) {
steps.push("load: " + descr + " > " + value);
return value;
}
function storeValue(descr, name, value) {
steps.push("store: " + descr + " = " + value);
store[name] = value;
}
var result = {
get a() { assertUnreachable(); },
set a(value) { storeValue("result.a", "a", value); },
get b() { assertUnreachable(); },
set b(value) { storeValue("result.b", "b", value); }
};
({
obj: {
x: result.a = 10,
[computePropertyName("y")]: result.b = false,
} = {}
} = { obj: {
get x() { return loadValue(".temp.obj.x", undefined); },
set x(value) { assertUnreachable(); },
get y() { return loadValue(".temp.obj.y", undefined); },
set y(value) { assertUnreachable(); }
}});
assertPropertiesEqual({
a: 10,
b: false
}, store);
assertArrayEquals([
"load: .temp.obj.x > undefined",
"store: result.a = 10",
"compute name: y",
"load: .temp.obj.y > undefined",
"store: result.b = false"
], steps);
steps = [];
({
obj: {
x: result.a = 50,
[computePropertyName("y")]: result.b = "hello",
} = {}
} = { obj: {
get x() { return loadValue(".temp.obj.x", 20); },
set x(value) { assertUnreachable(); },
get y() { return loadValue(".temp.obj.y", true); },
set y(value) { assertUnreachable(); }
}});
assertPropertiesEqual({
a: 20,
b: true
}, store);
assertArrayEquals([
"load: .temp.obj.x > 20",
"store: result.a = 20",
"compute name: y",
"load: .temp.obj.y > true",
"store: result.b = true",
], steps);
})();
// Credit to Mike Pennisi and other Test262 contributors for originally writing
// the testse the following are based on.
(function testArrayElision() {
var value = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var a, obj = {};
var result = [, a, , obj.b, , ...obj["rest"]] = value;
assertEquals(result, value);
assertEquals(2, a);
assertEquals(4, obj.b);
assertArrayEquals([6, 7, 8, 9], obj.rest);
})();
(function testArrayElementInitializer() {
function test(value, initializer, expected) {
var a, obj = {};
var initialized = false;
var shouldBeInitialized = value[0] === undefined;
assertEquals(value, [ a = (initialized = true, initializer) ] = value);
assertEquals(expected, a);
assertEquals(shouldBeInitialized, initialized);
var initialized2 = false;
assertEquals(value, [ obj.a = (initialized2 = true, initializer) ] = value);
assertEquals(expected, obj.a);
assertEquals(shouldBeInitialized, initialized2);
}
test([], "BAM!", "BAM!");
test([], "BOOP!", "BOOP!");
test([null], 123, null);
test([undefined], 456, 456);
test([,], "PUPPIES", "PUPPIES");
(function accept_IN() {
var value = [], x;
assertEquals(value, [ x = 'x' in {} ] = value);
assertEquals(false, x);
})();
(function ordering() {
var x = 0, a, b, value = [];
assertEquals(value, [ a = x += 1, b = x *= 2 ] = value);
assertEquals(1, a);
assertEquals(2, b);
assertEquals(2, x);
})();
(function yieldExpression() {
var value = [], it, result, x;
it = (function*() {
result = [ x = yield ] = value;
})();
var next = it.next();
assertEquals(undefined, result);
assertEquals(undefined, next.value);
assertEquals(false, next.done);
assertEquals(undefined, x);
next = it.next(86);
assertEquals(value, result);
assertEquals(undefined, next.value);
assertEquals(true, next.done);
assertEquals(86, x);
})();
(function yieldIdentifier() {
var value = [], yield = "BOOP!", x;
assertEquals(value, [ x = yield ] = value);
assertEquals("BOOP!", x);
})();
assertThrows(function let_TDZ() {
"use strict";
var x;
[ x = y ] = [];
let y;
}, ReferenceError);
})();
(function testArrayElementNestedPattern() {
assertThrows(function nestedArrayRequireObjectCoercibleNull() {
var x; [ [ x ] ] = [ null ];
}, TypeError);
assertThrows(function nestedArrayRequireObjectCoercibleUndefined() {
var x; [ [ x ] ] = [ undefined ];
}, TypeError);
assertThrows(function nestedArrayRequireObjectCoercibleUndefined2() {
var x; [ [ x ] ] = [ ];
}, TypeError);
assertThrows(function nestedArrayRequireObjectCoercibleUndefined3() {
var x; [ [ x ] ] = [ , ];
}, TypeError);
assertThrows(function nestedObjectRequireObjectCoercibleNull() {
var x; [ { x } ] = [ null ];
}, TypeError);
assertThrows(function nestedObjectRequireObjectCoercibleUndefined() {
var x; [ { x } ] = [ undefined ];
}, TypeError);
assertThrows(function nestedObjectRequireObjectCoercibleUndefined2() {
var x; [ { x } ] = [ ];
}, TypeError);
assertThrows(function nestedObjectRequireObjectCoercibleUndefined3() {
var x; [ { x } ] = [ , ];
}, TypeError);
(function nestedArray() {
var x, value = [ [ "zap", "blonk" ] ];
assertEquals(value, [ [ , x ] ] = value);
assertEquals("blonk", x);
})();
(function nestedObject() {
var x, value = [ { a: "zap", b: "blonk" } ];
assertEquals(value, [ { b: x } ] = value);
assertEquals("blonk", x);
})();
})();
(function testArrayRestElement() {
(function testBasic() {
var x, rest, array = [1, 2, 3];
assertEquals(array, [x, ...rest] = array);
assertEquals(1, x);
assertEquals([2, 3], rest);
array = [4, 5, 6];
assertEquals(array, [, ...rest] = array);
assertEquals([5, 6], rest);
})();
(function testNestedRestObject() {
var value = [1, 2, 3], x;
assertEquals(value, [...{ 1: x }] = value);
assertEquals(2, x);
})();
(function iterable() {
var count = 0;
var x, y, z;
function* g() {
count++;
yield;
count++;
yield;
count++;
yield;
}
var it = g();
assertEquals(it, [...x] = it);
assertEquals([undefined, undefined, undefined], x);
assertEquals(3, count);
it = [g()];
assertEquals(it, [ [...y] ] = it);
assertEquals([undefined, undefined, undefined], y);
assertEquals(6, count);
it = { a: g() };
assertEquals(it, { a: [...z] } = it);
assertEquals([undefined, undefined, undefined], z);
assertEquals(9, count);
})();
})();
(function testRequireObjectCoercible() {
assertThrows(() => ({} = undefined), TypeError);
assertThrows(() => ({} = null), TypeError);
assertThrows(() => [] = undefined, TypeError);
assertThrows(() => [] = null, TypeError);
assertEquals("test", ({} = "test"));
assertEquals("test", [] = "test");
assertEquals(123, ({} = 123));
})();
(function testConstReassignment() {
"use strict";
const c = "untouchable";
assertThrows(() => { [ c ] = [ "nope!" ]; }, TypeError);
assertThrows(() => { [ [ c ] ] = [ [ "nope!" ] ]; }, TypeError);
assertThrows(() => { [ { c } ] = [ { c: "nope!" } ]; }, TypeError);
assertThrows(() => { ({ c } = { c: "nope!" }); }, TypeError);
assertThrows(() => { ({ a: { c } } = { a: { c: "nope!" } }); }, TypeError);
assertThrows(() => { ({ a: [ c ] } = { a: [ "nope!" ] }); }, TypeError);
assertEquals("untouchable", c);
})();