[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:
parent
77774035d8
commit
b634a61d84
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
430
test/mjsunit/harmony/destructuring-assignment.js
Normal file
430
test/mjsunit/harmony/destructuring-assignment.js
Normal 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);
|
||||
})();
|
Loading…
Reference in New Issue
Block a user