Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(526)

Side by Side Diff: src/parser.cc

Issue 1130623004: [destructuring] Implement basic binding destructuring infrastructure (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Patch for landing Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/parser.h ('k') | src/pattern-rewriter.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/v8.h" 5 #include "src/v8.h"
6 6
7 #include "src/api.h" 7 #include "src/api.h"
8 #include "src/ast.h" 8 #include "src/ast.h"
9 #include "src/bailout-reason.h" 9 #include "src/bailout-reason.h"
10 #include "src/base/platform/platform.h" 10 #include "src/base/platform/platform.h"
11 #include "src/bootstrapper.h" 11 #include "src/bootstrapper.h"
12 #include "src/char-predicates-inl.h" 12 #include "src/char-predicates-inl.h"
13 #include "src/codegen.h" 13 #include "src/codegen.h"
14 #include "src/compiler.h" 14 #include "src/compiler.h"
15 #include "src/messages.h" 15 #include "src/messages.h"
16 #include "src/parser.h" 16 #include "src/parser.h"
17 #include "src/pattern-rewriter.h"
17 #include "src/preparser.h" 18 #include "src/preparser.h"
18 #include "src/runtime/runtime.h" 19 #include "src/runtime/runtime.h"
19 #include "src/scanner-character-streams.h" 20 #include "src/scanner-character-streams.h"
20 #include "src/scopeinfo.h" 21 #include "src/scopeinfo.h"
21 #include "src/string-stream.h" 22 #include "src/string-stream.h"
22 23
23 namespace v8 { 24 namespace v8 {
24 namespace internal { 25 namespace internal {
25 26
26 ScriptData::ScriptData(const byte* data, int length) 27 ScriptData::ScriptData(const byte* data, int length)
(...skipping 2282 matching lines...) Expand 10 before | Expand all | Expand 10 after
2309 // 2310 //
2310 // ConstDeclaration :: 2311 // ConstDeclaration ::
2311 // const ConstBinding (',' ConstBinding)* ';' 2312 // const ConstBinding (',' ConstBinding)* ';'
2312 // ConstBinding :: 2313 // ConstBinding ::
2313 // Identifier '=' AssignmentExpression 2314 // Identifier '=' AssignmentExpression
2314 // 2315 //
2315 // TODO(ES6): 2316 // TODO(ES6):
2316 // ConstBinding :: 2317 // ConstBinding ::
2317 // BindingPattern '=' AssignmentExpression 2318 // BindingPattern '=' AssignmentExpression
2318 2319
2319 int pos = peek_position(); 2320 PatternRewriter::DeclarationDescriptor decl;
2320 VariableMode mode = VAR; 2321 decl.parser = this;
2322 decl.pos = peek_position();
2323 decl.mode = VAR;
2321 // True if the binding needs initialization. 'let' and 'const' declared 2324 // True if the binding needs initialization. 'let' and 'const' declared
2322 // bindings are created uninitialized by their declaration nodes and 2325 // bindings are created uninitialized by their declaration nodes and
2323 // need initialization. 'var' declared bindings are always initialized 2326 // need initialization. 'var' declared bindings are always initialized
2324 // immediately by their declaration nodes. 2327 // immediately by their declaration nodes.
2325 bool needs_init = false; 2328 decl.needs_init = false;
2326 bool is_const = false; 2329 decl.is_const = false;
2327 Token::Value init_op = Token::INIT_VAR; 2330 decl.init_op = Token::INIT_VAR;
2331 decl.names = names;
2328 if (peek() == Token::VAR) { 2332 if (peek() == Token::VAR) {
2329 if (is_strong(language_mode())) { 2333 if (is_strong(language_mode())) {
2330 Scanner::Location location = scanner()->peek_location(); 2334 Scanner::Location location = scanner()->peek_location();
2331 ReportMessageAt(location, "strong_var"); 2335 ReportMessageAt(location, "strong_var");
2332 *ok = false; 2336 *ok = false;
2333 return NULL; 2337 return NULL;
2334 } 2338 }
2335 Consume(Token::VAR); 2339 Consume(Token::VAR);
2336 } else if (peek() == Token::CONST) { 2340 } else if (peek() == Token::CONST) {
2337 Consume(Token::CONST); 2341 Consume(Token::CONST);
2338 if (is_sloppy(language_mode())) { 2342 if (is_sloppy(language_mode())) {
2339 mode = CONST_LEGACY; 2343 decl.mode = CONST_LEGACY;
2340 init_op = Token::INIT_CONST_LEGACY; 2344 decl.init_op = Token::INIT_CONST_LEGACY;
2341 ++use_counts_[v8::Isolate::kLegacyConst]; 2345 ++use_counts_[v8::Isolate::kLegacyConst];
2342 } else { 2346 } else {
2343 DCHECK(var_context != kStatement); 2347 DCHECK(var_context != kStatement);
2344 mode = CONST; 2348 decl.mode = CONST;
2345 init_op = Token::INIT_CONST; 2349 decl.init_op = Token::INIT_CONST;
2346 } 2350 }
2347 is_const = true; 2351 decl.is_const = true;
2348 needs_init = true; 2352 decl.needs_init = true;
2349 } else if (peek() == Token::LET && is_strict(language_mode())) { 2353 } else if (peek() == Token::LET && is_strict(language_mode())) {
2350 Consume(Token::LET); 2354 Consume(Token::LET);
2351 DCHECK(var_context != kStatement); 2355 DCHECK(var_context != kStatement);
2352 mode = LET; 2356 decl.mode = LET;
2353 needs_init = true; 2357 decl.needs_init = true;
2354 init_op = Token::INIT_LET; 2358 decl.init_op = Token::INIT_LET;
2355 } else { 2359 } else {
2356 UNREACHABLE(); // by current callers 2360 UNREACHABLE(); // by current callers
2357 } 2361 }
2358 2362
2359 Scope* declaration_scope = DeclarationScope(mode); 2363 decl.declaration_scope = DeclarationScope(decl.mode);
2364 decl.scope = scope_;
2365
2360 2366
2361 // The scope of a var/const declared variable anywhere inside a function 2367 // The scope of a var/const declared variable anywhere inside a function
2362 // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can 2368 // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can
2363 // transform a source-level var/const declaration into a (Function) 2369 // transform a source-level var/const declaration into a (Function)
2364 // Scope declaration, and rewrite the source-level initialization into an 2370 // Scope declaration, and rewrite the source-level initialization into an
2365 // assignment statement. We use a block to collect multiple assignments. 2371 // assignment statement. We use a block to collect multiple assignments.
2366 // 2372 //
2367 // We mark the block as initializer block because we don't want the 2373 // We mark the block as initializer block because we don't want the
2368 // rewriter to add a '.result' assignment to such a block (to get compliant 2374 // rewriter to add a '.result' assignment to such a block (to get compliant
2369 // behavior for code such as print(eval('var x = 7')), and for cosmetic 2375 // behavior for code such as print(eval('var x = 7')), and for cosmetic
2370 // reasons when pretty-printing. Also, unless an assignment (initialization) 2376 // reasons when pretty-printing. Also, unless an assignment (initialization)
2371 // is inside an initializer block, it is ignored. 2377 // is inside an initializer block, it is ignored.
2372 // 2378 //
2373 // Create new block with one expected declaration. 2379 // Create new block with one expected declaration.
2374 Block* block = factory()->NewBlock(NULL, 1, true, pos); 2380 decl.block = factory()->NewBlock(NULL, 1, true, decl.pos);
2375 int nvars = 0; // the number of variables declared 2381 int nvars = 0; // the number of variables declared
2376 int bindings_start = peek_position(); 2382 int bindings_start = peek_position();
2377 const AstRawString* name = NULL;
2378 const AstRawString* first_name = NULL; 2383 const AstRawString* first_name = NULL;
2379 bool is_for_iteration_variable; 2384 bool is_for_iteration_variable;
2380 do { 2385 do {
2381 if (fni_ != NULL) fni_->Enter(); 2386 if (fni_ != NULL) fni_->Enter();
2382 2387
2383 // Parse variable name. 2388 // Parse variable name.
2384 if (nvars > 0) Consume(Token::COMMA); 2389 if (nvars > 0) Consume(Token::COMMA);
2385 2390
2391 PatternRewriter pattern_rewriter;
2386 { 2392 {
2387 ExpressionClassifier pattern_classifier; 2393 ExpressionClassifier pattern_classifier;
2388 Token::Value next = peek(); 2394 Token::Value next = peek();
2389 Expression* pattern = 2395 Expression* pattern =
2390 ParsePrimaryExpression(&pattern_classifier, CHECK_OK); 2396 ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
2391 ValidateBindingPattern(&pattern_classifier, CHECK_OK); 2397 ValidateBindingPattern(&pattern_classifier, CHECK_OK);
2392 if (pattern->IsVariableProxy() && 2398 pattern_rewriter = PatternRewriter(&decl, pattern);
2393 pattern->AsVariableProxy()->IsValidReferenceExpression()) { 2399 if (!allow_harmony_destructuring() &&
2394 scope_->RemoveUnresolved(pattern->AsVariableProxy()); 2400 !pattern_rewriter.IsSingleVariableBinding()) {
2395 name = pattern->AsVariableProxy()->raw_name();
2396 } else if (allow_harmony_destructuring()) {
2397 // TODO(dslomov): really destructure.
2398 name = ast_value_factory()->GetOneByteString(".temp.variable");
2399 } else {
2400 ReportUnexpectedToken(next); 2401 ReportUnexpectedToken(next);
2401 *ok = false; 2402 *ok = false;
2402 return nullptr; 2403 return nullptr;
2403 } 2404 }
2405
2406 // TODO(dslomov): unify
2404 } 2407 }
2405 2408
2406 if (!first_name) first_name = name;
2407 Scanner::Location variable_loc = scanner()->location(); 2409 Scanner::Location variable_loc = scanner()->location();
2408 if (fni_ != NULL) fni_->PushVariableName(name); 2410 const bool single_name = pattern_rewriter.IsSingleVariableBinding();
2411 if (single_name) {
2412 if (!first_name) first_name = pattern_rewriter.SingleName();
2413 if (fni_ != NULL) fni_->PushVariableName(pattern_rewriter.SingleName());
2414 }
2409 2415
2410 // Declare variable.
2411 // Note that we *always* must treat the initial value via a separate init
2412 // assignment for variables and constants because the value must be assigned
2413 // when the variable is encountered in the source. But the variable/constant
2414 // is declared (and set to 'undefined') upon entering the function within
2415 // which the variable or constant is declared. Only function variables have
2416 // an initial value in the declaration (because they are initialized upon
2417 // entering the function).
2418 //
2419 // If we have a const declaration, in an inner scope, the proxy is always
2420 // bound to the declared variable (independent of possibly surrounding with
2421 // statements).
2422 // For let/const declarations in harmony mode, we can also immediately
2423 // pre-resolve the proxy because it resides in the same scope as the
2424 // declaration.
2425 is_for_iteration_variable = 2416 is_for_iteration_variable =
2426 var_context == kForStatement && 2417 var_context == kForStatement &&
2427 (peek() == Token::IN || PeekContextualKeyword(CStrVector("of"))); 2418 (peek() == Token::IN || PeekContextualKeyword(CStrVector("of")));
2428 if (is_for_iteration_variable && mode == CONST) { 2419 if (is_for_iteration_variable && decl.mode == CONST) {
2429 needs_init = false; 2420 decl.needs_init = false;
2430 } 2421 }
2431 2422
2432 VariableProxy* proxy = NewUnresolved(name, mode);
2433 Declaration* declaration =
2434 factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
2435 Variable* var = Declare(declaration, mode != VAR, CHECK_OK);
2436 DCHECK_NOT_NULL(var);
2437 DCHECK(!proxy->is_resolved() || proxy->var() == var);
2438 nvars++;
2439 if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
2440 ReportMessage("too_many_variables");
2441 *ok = false;
2442 return NULL;
2443 }
2444 if (names) names->Add(name, zone());
2445
2446 // Parse initialization expression if present and/or needed. A
2447 // declaration of the form:
2448 //
2449 // var v = x;
2450 //
2451 // is syntactic sugar for:
2452 //
2453 // var v; v = x;
2454 //
2455 // In particular, we need to re-lookup 'v' (in scope_, not
2456 // declaration_scope) as it may be a different 'v' than the 'v' in the
2457 // declaration (e.g., if we are inside a 'with' statement or 'catch'
2458 // block).
2459 //
2460 // However, note that const declarations are different! A const
2461 // declaration of the form:
2462 //
2463 // const c = x;
2464 //
2465 // is *not* syntactic sugar for:
2466 //
2467 // const c; c = x;
2468 //
2469 // The "variable" c initialized to x is the same as the declared
2470 // one - there is no re-lookup (see the last parameter of the
2471 // Declare() call above).
2472
2473 Scope* initialization_scope = is_const ? declaration_scope : scope_;
2474 Expression* value = NULL; 2423 Expression* value = NULL;
2475 int pos = -1; 2424 decl.pos = -1;
2425 decl.initializer_position = -1;
2476 // Harmony consts have non-optional initializers. 2426 // Harmony consts have non-optional initializers.
2477 if (peek() == Token::ASSIGN || 2427 if (peek() == Token::ASSIGN ||
2478 (mode == CONST && !is_for_iteration_variable)) { 2428 (decl.mode == CONST && !is_for_iteration_variable)) {
2479 Expect(Token::ASSIGN, CHECK_OK); 2429 Expect(Token::ASSIGN, CHECK_OK);
2480 pos = position(); 2430 decl.pos = position();
2481 ExpressionClassifier classifier; 2431 ExpressionClassifier classifier;
2482 value = ParseAssignmentExpression(var_context != kForStatement, 2432 value = ParseAssignmentExpression(var_context != kForStatement,
2483 &classifier, CHECK_OK); 2433 &classifier, CHECK_OK);
2484 ValidateExpression(&classifier, CHECK_OK); 2434 ValidateExpression(&classifier, CHECK_OK);
2485 variable_loc.end_pos = scanner()->location().end_pos; 2435 variable_loc.end_pos = scanner()->location().end_pos;
2486 2436
2487 if (first_initializer_loc && !first_initializer_loc->IsValid()) { 2437 if (first_initializer_loc && !first_initializer_loc->IsValid()) {
2488 *first_initializer_loc = variable_loc; 2438 *first_initializer_loc = variable_loc;
2489 } 2439 }
2490 2440
2491 // Don't infer if it is "a = function(){...}();"-like expression. 2441 // Don't infer if it is "a = function(){...}();"-like expression.
2492 if (fni_ != NULL && 2442 if (single_name) {
2493 value->AsCall() == NULL && 2443 if (fni_ != NULL && value->AsCall() == NULL &&
2494 value->AsCallNew() == NULL) { 2444 value->AsCallNew() == NULL) {
2495 fni_->Infer(); 2445 fni_->Infer();
2496 } else { 2446 } else {
2497 fni_->RemoveLastFunction(); 2447 fni_->RemoveLastFunction();
2448 }
2498 } 2449 }
2499 // End position of the initializer is after the assignment expression. 2450 // End position of the initializer is after the assignment expression.
2500 var->set_initializer_position(scanner()->location().end_pos); 2451 decl.initializer_position = scanner()->location().end_pos;
2501 } else { 2452 } else {
2502 // End position of the initializer is after the variable. 2453 // End position of the initializer is after the variable.
2503 var->set_initializer_position(position()); 2454 decl.initializer_position = position();
2504 } 2455 }
2505 2456
2506 // Make sure that 'const x' and 'let x' initialize 'x' to undefined. 2457 // Make sure that 'const x' and 'let x' initialize 'x' to undefined.
2507 if (value == NULL && needs_init) { 2458 if (value == NULL && decl.needs_init) {
2508 value = GetLiteralUndefined(position()); 2459 value = GetLiteralUndefined(position());
2509 } 2460 }
2510 2461
2511 // Global variable declarations must be compiled in a specific 2462 pattern_rewriter.DeclareAndInitializeVariables(value, &nvars, CHECK_OK);
2512 // way. When the script containing the global variable declaration
2513 // is entered, the global variable must be declared, so that if it
2514 // doesn't exist (on the global object itself, see ES5 errata) it
2515 // gets created with an initial undefined value. This is handled
2516 // by the declarations part of the function representing the
2517 // top-level global code; see Runtime::DeclareGlobalVariable. If
2518 // it already exists (in the object or in a prototype), it is
2519 // *not* touched until the variable declaration statement is
2520 // executed.
2521 //
2522 // Executing the variable declaration statement will always
2523 // guarantee to give the global object an own property.
2524 // This way, global variable declarations can shadow
2525 // properties in the prototype chain, but only after the variable
2526 // declaration statement has been executed. This is important in
2527 // browsers where the global object (window) has lots of
2528 // properties defined in prototype objects.
2529 if (initialization_scope->is_script_scope() &&
2530 !IsLexicalVariableMode(mode)) {
2531 // Compute the arguments for the runtime call.
2532 ZoneList<Expression*>* arguments =
2533 new(zone()) ZoneList<Expression*>(3, zone());
2534 // We have at least 1 parameter.
2535 arguments->Add(factory()->NewStringLiteral(name, pos), zone());
2536 CallRuntime* initialize;
2537 2463
2538 if (is_const) { 2464 if (single_name && fni_ != NULL) fni_->Leave();
2539 arguments->Add(value, zone());
2540 value = NULL; // zap the value to avoid the unnecessary assignment
2541
2542 // Construct the call to Runtime_InitializeConstGlobal
2543 // and add it to the initialization statement block.
2544 // Note that the function does different things depending on
2545 // the number of arguments (1 or 2).
2546 initialize = factory()->NewCallRuntime(
2547 ast_value_factory()->initialize_const_global_string(),
2548 Runtime::FunctionForId(Runtime::kInitializeConstGlobal), arguments,
2549 pos);
2550 } else {
2551 // Add language mode.
2552 // We may want to pass singleton to avoid Literal allocations.
2553 LanguageMode language_mode = initialization_scope->language_mode();
2554 arguments->Add(factory()->NewNumberLiteral(language_mode, pos), zone());
2555
2556 // Be careful not to assign a value to the global variable if
2557 // we're in a with. The initialization value should not
2558 // necessarily be stored in the global object in that case,
2559 // which is why we need to generate a separate assignment node.
2560 if (value != NULL && !inside_with()) {
2561 arguments->Add(value, zone());
2562 value = NULL; // zap the value to avoid the unnecessary assignment
2563 // Construct the call to Runtime_InitializeVarGlobal
2564 // and add it to the initialization statement block.
2565 initialize = factory()->NewCallRuntime(
2566 ast_value_factory()->initialize_var_global_string(),
2567 Runtime::FunctionForId(Runtime::kInitializeVarGlobal), arguments,
2568 pos);
2569 } else {
2570 initialize = NULL;
2571 }
2572 }
2573
2574 if (initialize != NULL) {
2575 block->AddStatement(factory()->NewExpressionStatement(
2576 initialize, RelocInfo::kNoPosition),
2577 zone());
2578 }
2579 } else if (needs_init) {
2580 // Constant initializations always assign to the declared constant which
2581 // is always at the function scope level. This is only relevant for
2582 // dynamically looked-up variables and constants (the start context for
2583 // constant lookups is always the function context, while it is the top
2584 // context for var declared variables). Sigh...
2585 // For 'let' and 'const' declared variables in harmony mode the
2586 // initialization also always assigns to the declared variable.
2587 DCHECK(proxy != NULL);
2588 DCHECK(proxy->var() != NULL);
2589 DCHECK(value != NULL);
2590 Assignment* assignment =
2591 factory()->NewAssignment(init_op, proxy, value, pos);
2592 block->AddStatement(
2593 factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
2594 zone());
2595 value = NULL;
2596 }
2597
2598 // Add an assignment node to the initialization statement block if we still
2599 // have a pending initialization value.
2600 if (value != NULL) {
2601 DCHECK(mode == VAR);
2602 // 'var' initializations are simply assignments (with all the consequences
2603 // if they are inside a 'with' statement - they may change a 'with' object
2604 // property).
2605 VariableProxy* proxy =
2606 initialization_scope->NewUnresolved(factory(), name);
2607 Assignment* assignment =
2608 factory()->NewAssignment(init_op, proxy, value, pos);
2609 block->AddStatement(
2610 factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
2611 zone());
2612 }
2613
2614 if (fni_ != NULL) fni_->Leave();
2615 } while (peek() == Token::COMMA); 2465 } while (peek() == Token::COMMA);
2616 2466
2617 if (bindings_loc) { 2467 if (bindings_loc) {
2618 *bindings_loc = 2468 *bindings_loc =
2619 Scanner::Location(bindings_start, scanner()->location().end_pos); 2469 Scanner::Location(bindings_start, scanner()->location().end_pos);
2620 } 2470 }
2621 2471
2622 if (num_decl) *num_decl = nvars; 2472 if (num_decl) *num_decl = nvars;
2623 *out = first_name; 2473 *out = first_name;
2624 2474
2625 return block; 2475 return decl.block;
2626 } 2476 }
2627 2477
2628 2478
2629 static bool ContainsLabel(ZoneList<const AstRawString*>* labels, 2479 static bool ContainsLabel(ZoneList<const AstRawString*>* labels,
2630 const AstRawString* label) { 2480 const AstRawString* label) {
2631 DCHECK(label != NULL); 2481 DCHECK(label != NULL);
2632 if (labels != NULL) { 2482 if (labels != NULL) {
2633 for (int i = labels->length(); i-- > 0; ) { 2483 for (int i = labels->length(); i-- > 0; ) {
2634 if (labels->at(i) == label) { 2484 if (labels->at(i) == label) {
2635 return true; 2485 return true;
(...skipping 3289 matching lines...) Expand 10 before | Expand all | Expand 10 after
5925 5775
5926 Expression* Parser::SpreadCallNew(Expression* function, 5776 Expression* Parser::SpreadCallNew(Expression* function,
5927 ZoneList<v8::internal::Expression*>* args, 5777 ZoneList<v8::internal::Expression*>* args,
5928 int pos) { 5778 int pos) {
5929 args->InsertAt(0, function, zone()); 5779 args->InsertAt(0, function, zone());
5930 5780
5931 return factory()->NewCallRuntime( 5781 return factory()->NewCallRuntime(
5932 ast_value_factory()->reflect_construct_string(), NULL, args, pos); 5782 ast_value_factory()->reflect_construct_string(), NULL, args, pos);
5933 } 5783 }
5934 } } // namespace v8::internal 5784 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/parser.h ('k') | src/pattern-rewriter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698