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

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

Powered by Google App Engine
This is Rietveld 408576698