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

Side by Side Diff: runtime/vm/parser.cc

Issue 54713003: VM: Fix initialization of statics in presence of exceptions. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 1 month 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/parser.h" 5 #include "vm/parser.h"
6 6
7 #include "lib/invocation_mirror.h" 7 #include "lib/invocation_mirror.h"
8 #include "vm/bigint_operations.h" 8 #include "vm/bigint_operations.h"
9 #include "vm/bootstrap.h" 9 #include "vm/bootstrap.h"
10 #include "vm/class_finalizer.h" 10 #include "vm/class_finalizer.h"
(...skipping 964 matching lines...) Expand 10 before | Expand all | Expand 10 after
975 SequenceNode* initialize_field = new SequenceNode(ident_pos, NULL); 975 SequenceNode* initialize_field = new SequenceNode(ident_pos, NULL);
976 initialize_field->Add( 976 initialize_field->Add(
977 new StoreStaticFieldNode( 977 new StoreStaticFieldNode(
978 ident_pos, 978 ident_pos,
979 field, 979 field,
980 new LiteralNode(ident_pos, Object::transition_sentinel()))); 980 new LiteralNode(ident_pos, Object::transition_sentinel())));
981 // TODO(hausner): If evaluation of the field value throws an exception, 981 // TODO(hausner): If evaluation of the field value throws an exception,
982 // we leave the field value as 'transition_sentinel', which is wrong. 982 // we leave the field value as 'transition_sentinel', which is wrong.
983 // A second reference to the field later throws a circular dependency 983 // A second reference to the field later throws a circular dependency
984 // exception. The field should instead be set to null after an exception. 984 // exception. The field should instead be set to null after an exception.
985 const String& init_name = 985 const String& init_name = String::Handle(
986 String::Handle(Symbols::New(String::Handle( 986 Symbols::New(String::Handle(String::Concat(
987 String::Concat(Symbols::InitPrefix(), String::Handle(field.name()))))); 987 Symbols::InitPrefix(), String::Handle(field.name())))));
988 const Function& init_function = Function::ZoneHandle( 988 const Function& init_function = Function::ZoneHandle(
989 field_class.LookupStaticFunction(init_name)); 989 field_class.LookupStaticFunction(init_name));
990 ASSERT(!init_function.IsNull()); 990 ASSERT(!init_function.IsNull());
991 ArgumentListNode* arguments = new ArgumentListNode(expr_pos); 991 ArgumentListNode* arguments = new ArgumentListNode(expr_pos);
992 StaticCallNode* init_call = 992 StaticCallNode* init_call =
993 new StaticCallNode(expr_pos, init_function, arguments); 993 new StaticCallNode(expr_pos, init_function, arguments);
994 initialize_field->Add(init_call);
994 995
995 initialize_field->Add(new StoreStaticFieldNode(ident_pos,
996 field,
997 init_call));
998 AstNode* uninitialized_check = 996 AstNode* uninitialized_check =
999 new IfNode(ident_pos, compare_uninitialized, initialize_field, NULL); 997 new IfNode(ident_pos, compare_uninitialized, initialize_field, NULL);
1000 current_block_->statements->Add(uninitialized_check); 998 current_block_->statements->Add(uninitialized_check);
1001 999
1002 // Generate code returning the field value. 1000 // Generate code returning the field value.
1003 ReturnNode* return_node = 1001 ReturnNode* return_node =
1004 new ReturnNode(ident_pos, 1002 new ReturnNode(ident_pos,
1005 new LoadStaticFieldNode(ident_pos, field)); 1003 new LoadStaticFieldNode(ident_pos, field));
1006 current_block_->statements->Add(return_node); 1004 current_block_->statements->Add(return_node);
1007 } 1005 }
1008 return CloseBlock(); 1006 return CloseBlock();
1009 } 1007 }
1010 1008
1011 1009
1012 SequenceNode* Parser::ParseStaticInitializer(const Function& func) { 1010 SequenceNode* Parser::ParseStaticInitializer(const Function& func) {
1013 TRACE_PARSER("ParseStaticInitializer"); 1011 TRACE_PARSER("ParseStaticInitializer");
1014 ParamList params; 1012 ParamList params;
1015 ASSERT(func.num_fixed_parameters() == 0); // static. 1013 ASSERT(func.num_fixed_parameters() == 0); // static.
1016 ASSERT(!func.HasOptionalParameters()); 1014 ASSERT(!func.HasOptionalParameters());
1017 ASSERT(AbstractType::Handle(func.result_type()).IsResolved()); 1015 ASSERT(AbstractType::Handle(func.result_type()).IsResolved());
1018 1016
1019 // Build local scope for function and populate with the formal parameters. 1017 // Build local scope for function and populate with the formal parameters.
1020 OpenFunctionBlock(func); 1018 OpenFunctionBlock(func);
1021 AddFormalParamsToScope(&params, current_block_->scope); 1019 AddFormalParamsToScope(&params, current_block_->scope);
1022 1020
1023 intptr_t expr_pos = func.token_pos(); 1021 // Move forward to the start of the initializer expression.
1022 ExpectIdentifier("identifier expected");
1023 ExpectToken(Token::kASSIGN);
1024 intptr_t token_pos = TokenPos();
1025
1026 // Synthesize a try-catch block to wrap the initializer expression.
1027 LocalVariable* context_var =
1028 current_block_->scope->LocalLookupVariable(Symbols::SavedTryContextVar());
1029 if (context_var == NULL) {
1030 context_var = new LocalVariable(token_pos,
1031 Symbols::SavedTryContextVar(),
1032 Type::ZoneHandle(Type::DynamicType()));
1033 current_block_->scope->AddVariable(context_var);
1034 }
1035 LocalVariable* catch_excp_var =
1036 current_block_->scope->LocalLookupVariable(Symbols::ExceptionVar());
1037 if (catch_excp_var == NULL) {
1038 catch_excp_var = new LocalVariable(token_pos,
1039 Symbols::ExceptionVar(),
1040 Type::ZoneHandle(Type::DynamicType()));
1041 current_block_->scope->AddVariable(catch_excp_var);
1042 }
1043 LocalVariable* catch_trace_var =
1044 current_block_->scope->LocalLookupVariable(Symbols::StacktraceVar());
1045 if (catch_trace_var == NULL) {
1046 catch_trace_var = new LocalVariable(token_pos,
1047 Symbols::StacktraceVar(),
1048 Type::ZoneHandle(Type::DynamicType()));
1049 current_block_->scope->AddVariable(catch_trace_var);
1050 }
1051
1052 OpenBlock(); // Start try block.
1024 AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades); 1053 AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
1025 ReturnNode* return_node = new ReturnNode(expr_pos, expr); 1054 const Field& field = Field::ZoneHandle(func.saved_static_field());
1026 current_block_->statements->Add(return_node); 1055 ASSERT(!field.is_const());
1056 StoreStaticFieldNode* store = new StoreStaticFieldNode(field.token_pos(),
1057 field,
1058 expr);
1059 current_block_->statements->Add(store);
1060 SequenceNode* try_block = CloseBlock(); // End try block.
1061
1062 OpenBlock(); // Start catch handler list.
1063 SourceLabel* end_catch_label =
1064 SourceLabel::New(token_pos, NULL, SourceLabel::kCatch);
1065 current_block_->scope->AddLabel(end_catch_label);
1066
1067 OpenBlock(); // Start catch clause.
1068 AstNode* compare_transition_sentinel = new ComparisonNode(
1069 token_pos,
1070 Token::kEQ_STRICT,
1071 new LoadStaticFieldNode(token_pos, field),
1072 new LiteralNode(field.token_pos(), Object::transition_sentinel()));
1073
1074 SequenceNode* store_null = new SequenceNode(token_pos, NULL);
1075 store_null->Add(new StoreStaticFieldNode(
1076 field.token_pos(),
1077 field,
1078 new LiteralNode(token_pos, Instance::ZoneHandle())));
1079 AstNode* transition_sentinel_check =
1080 new IfNode(token_pos, compare_transition_sentinel, store_null, NULL);
1081 current_block_->statements->Add(transition_sentinel_check);
1082
1083 current_block_->statements->Add(
1084 new ThrowNode(token_pos,
1085 new LoadLocalNode(token_pos, catch_excp_var),
1086 new LoadLocalNode(token_pos, catch_trace_var)));
1087 current_block_->statements->Add(
1088 new JumpNode(token_pos, Token::kCONTINUE, end_catch_label));
1089 SequenceNode* catch_clause = CloseBlock(); // End catch clause.
1090
1091 current_block_->statements->Add(catch_clause);
1092 SequenceNode* catch_handler_list = CloseBlock(); // End catch handler list.
1093 CatchClauseNode* catch_block =
1094 new CatchClauseNode(token_pos,
1095 catch_handler_list,
1096 Array::ZoneHandle(Object::empty_array().raw()),
1097 context_var,
1098 catch_excp_var,
1099 catch_trace_var,
1100 CatchClauseNode::kInvalidTryIndex,
1101 false); // No stack trace needed.
1102
1103 AstNode* try_catch_node = new TryCatchNode(token_pos,
1104 try_block,
1105 end_catch_label,
1106 context_var,
1107 catch_block,
1108 NULL, // No finally block.
1109 AllocateTryIndex());
1110 current_block_->statements->Add(try_catch_node);
1027 return CloseBlock(); 1111 return CloseBlock();
1028 } 1112 }
1029 1113
1030 1114
1031 // Create AstNodes for an implicit instance getter method: 1115 // Create AstNodes for an implicit instance getter method:
1032 // LoadLocalNode 0 ('this'); 1116 // LoadLocalNode 0 ('this');
1033 // LoadInstanceFieldNode (field_name); 1117 // LoadInstanceFieldNode (field_name);
1034 // ReturnNode (field's value); 1118 // ReturnNode (field's value);
1035 SequenceNode* Parser::ParseInstanceGetter(const Function& func) { 1119 SequenceNode* Parser::ParseInstanceGetter(const Function& func) {
1036 TRACE_PARSER("ParseInstanceGetter"); 1120 TRACE_PARSER("ParseInstanceGetter");
(...skipping 2227 matching lines...) Expand 10 before | Expand all | Expand 10 after
3264 if (field->has_factory) { 3348 if (field->has_factory) {
3265 ErrorMsg("keyword 'factory' not allowed in field declaration"); 3349 ErrorMsg("keyword 'factory' not allowed in field declaration");
3266 } 3350 }
3267 if (!field->has_static && field->has_const) { 3351 if (!field->has_static && field->has_const) {
3268 ErrorMsg(field->name_pos, "instance field may not be 'const'"); 3352 ErrorMsg(field->name_pos, "instance field may not be 'const'");
3269 } 3353 }
3270 Function& getter = Function::Handle(); 3354 Function& getter = Function::Handle();
3271 Function& setter = Function::Handle(); 3355 Function& setter = Function::Handle();
3272 Field& class_field = Field::ZoneHandle(); 3356 Field& class_field = Field::ZoneHandle();
3273 Instance& init_value = Instance::Handle(); 3357 Instance& init_value = Instance::Handle();
3274 intptr_t expr_pos = -1;
3275 while (true) { 3358 while (true) {
3276 bool has_initializer = CurrentToken() == Token::kASSIGN; 3359 bool has_initializer = CurrentToken() == Token::kASSIGN;
3277 bool has_simple_literal = false; 3360 bool has_simple_literal = false;
3278 if (has_initializer) { 3361 if (has_initializer) {
3279 ConsumeToken(); 3362 ConsumeToken();
3280 init_value = Object::sentinel().raw(); 3363 init_value = Object::sentinel().raw();
3281 // For static const fields and static final non-const fields, the 3364 // For static const fields and static final non-const fields, the
3282 // initialization expression will be parsed through the 3365 // initialization expression will be parsed through the
3283 // kImplicitStaticFinalGetter method invocation/compilation. 3366 // kImplicitStaticFinalGetter method invocation/compilation.
3284 // For instance fields, the expression is parsed when a constructor 3367 // For instance fields, the expression is parsed when a constructor
3285 // is compiled. 3368 // is compiled.
3286 // For static const fields and static final non-const fields with very 3369 // For static const fields and static final non-const fields with very
3287 // simple initializer expressions (e.g. a literal number or string), we 3370 // simple initializer expressions (e.g. a literal number or string), we
3288 // optimize away the kImplicitStaticFinalGetter and initialize the field 3371 // optimize away the kImplicitStaticFinalGetter and initialize the field
3289 // here. However, the class finalizer will check the value type for 3372 // here. However, the class finalizer will check the value type for
3290 // assignability once the declared field type can be resolved. If the 3373 // assignability once the declared field type can be resolved. If the
3291 // value is not assignable (assuming checked mode and disregarding actual 3374 // value is not assignable (assuming checked mode and disregarding actual
3292 // mode), the field value is reset and a kImplicitStaticFinalGetter is 3375 // mode), the field value is reset and a kImplicitStaticFinalGetter is
3293 // created at finalization time. 3376 // created at finalization time.
3294 3377
3295 if (field->has_static && (field->has_const || field->has_final) && 3378 if (field->has_static && (field->has_const || field->has_final) &&
3296 (LookaheadToken(1) == Token::kSEMICOLON)) { 3379 (LookaheadToken(1) == Token::kSEMICOLON)) {
3297 has_simple_literal = IsSimpleLiteral(*field->type, &init_value); 3380 has_simple_literal = IsSimpleLiteral(*field->type, &init_value);
3298 } 3381 }
3299 expr_pos = TokenPos();
3300 SkipExpr(); 3382 SkipExpr();
3301 } else { 3383 } else {
3302 // Static const and static final fields must have an initializer. 3384 // Static const and static final fields must have an initializer.
3303 // Static const fields are implicitly final. 3385 // Static const fields are implicitly final.
3304 if (field->has_static && field->has_final) { 3386 if (field->has_static && field->has_final) {
3305 ErrorMsg(field->name_pos, 3387 ErrorMsg(field->name_pos,
3306 "static %s field '%s' must have an initializer expression", 3388 "static %s field '%s' must have an initializer expression",
3307 field->has_const ? "const" : "final", 3389 field->has_const ? "const" : "final",
3308 field->name->ToCString()); 3390 field->name->ToCString());
3309 } 3391 }
(...skipping 24 matching lines...) Expand all
3334 RawFunction::kImplicitStaticFinalGetter, 3416 RawFunction::kImplicitStaticFinalGetter,
3335 field->has_static, 3417 field->has_static,
3336 field->has_const, 3418 field->has_const,
3337 /* is_abstract = */ false, 3419 /* is_abstract = */ false,
3338 /* is_external = */ false, 3420 /* is_external = */ false,
3339 current_class(), 3421 current_class(),
3340 field->name_pos); 3422 field->name_pos);
3341 getter.set_result_type(*field->type); 3423 getter.set_result_type(*field->type);
3342 members->AddFunction(getter); 3424 members->AddFunction(getter);
3343 3425
3344 // Create initializer function. 3426 // Create initializer function for non-const fields.
3345 const Function& init_function = Function::ZoneHandle( 3427 if (!class_field.is_const()) {
3346 Function::NewStaticInitializer(*field->name, 3428 const Function& init_function = Function::ZoneHandle(
3347 *field->type, 3429 Function::NewStaticInitializer(class_field));
3348 current_class(), 3430 members->AddFunction(init_function);
3349 expr_pos)); 3431 }
3350 members->AddFunction(init_function);
3351 } 3432 }
3352 } 3433 }
3353 3434
3354 // For instance fields, we create implicit getter and setter methods. 3435 // For instance fields, we create implicit getter and setter methods.
3355 if (!field->has_static) { 3436 if (!field->has_static) {
3356 String& getter_name = String::Handle(Field::GetterSymbol(*field->name)); 3437 String& getter_name = String::Handle(Field::GetterSymbol(*field->name));
3357 getter = Function::New(getter_name, RawFunction::kImplicitGetter, 3438 getter = Function::New(getter_name, RawFunction::kImplicitGetter,
3358 field->has_static, 3439 field->has_static,
3359 field->has_final, 3440 field->has_final,
3360 /* is_abstract = */ false, 3441 /* is_abstract = */ false,
(...skipping 1089 matching lines...) Expand 10 before | Expand all | Expand 10 after
4450 if (metadata_pos >= 0) { 4531 if (metadata_pos >= 0) {
4451 library_.AddFieldMetadata(field, metadata_pos); 4532 library_.AddFieldMetadata(field, metadata_pos);
4452 } 4533 }
4453 if (CurrentToken() == Token::kASSIGN) { 4534 if (CurrentToken() == Token::kASSIGN) {
4454 ConsumeToken(); 4535 ConsumeToken();
4455 Instance& field_value = Instance::Handle(Object::sentinel().raw()); 4536 Instance& field_value = Instance::Handle(Object::sentinel().raw());
4456 bool has_simple_literal = false; 4537 bool has_simple_literal = false;
4457 if ((is_const || is_final) && (LookaheadToken(1) == Token::kSEMICOLON)) { 4538 if ((is_const || is_final) && (LookaheadToken(1) == Token::kSEMICOLON)) {
4458 has_simple_literal = IsSimpleLiteral(type, &field_value); 4539 has_simple_literal = IsSimpleLiteral(type, &field_value);
4459 } 4540 }
4460 const intptr_t expr_pos = TokenPos();
4461 SkipExpr(); 4541 SkipExpr();
4462 field.set_value(field_value); 4542 field.set_value(field_value);
4463 if (!has_simple_literal) { 4543 if (!has_simple_literal) {
4464 // Create a static final getter. 4544 // Create a static final getter.
4465 String& getter_name = String::Handle(Field::GetterSymbol(var_name)); 4545 String& getter_name = String::Handle(Field::GetterSymbol(var_name));
4466 getter = Function::New(getter_name, 4546 getter = Function::New(getter_name,
4467 RawFunction::kImplicitStaticFinalGetter, 4547 RawFunction::kImplicitStaticFinalGetter,
4468 is_static, 4548 is_static,
4469 is_const, 4549 is_const,
4470 /* is_abstract = */ false, 4550 /* is_abstract = */ false,
4471 /* is_external = */ false, 4551 /* is_external = */ false,
4472 current_class(), 4552 current_class(),
4473 name_pos); 4553 name_pos);
4474 getter.set_result_type(type); 4554 getter.set_result_type(type);
4475 top_level->functions.Add(getter); 4555 top_level->functions.Add(getter);
4476 4556
4477 // Create initializer function. 4557 // Create initializer function.
4478 const Function& init_function = Function::ZoneHandle( 4558 if (!field.is_const()) {
4479 Function::NewStaticInitializer(var_name, 4559 const Function& init_function = Function::ZoneHandle(
4480 type, 4560 Function::NewStaticInitializer(field));
4481 current_class(), 4561 top_level->functions.Add(init_function);
4482 expr_pos)); 4562 }
4483 top_level->functions.Add(init_function);
4484 } 4563 }
4485 } else if (is_final) { 4564 } else if (is_final) {
4486 ErrorMsg(name_pos, "missing initializer for final or const variable"); 4565 ErrorMsg(name_pos, "missing initializer for final or const variable");
4487 } 4566 }
4488 4567
4489 if (CurrentToken() == Token::kCOMMA) { 4568 if (CurrentToken() == Token::kCOMMA) {
4490 ConsumeToken(); 4569 ConsumeToken();
4491 } else if (CurrentToken() == Token::kSEMICOLON) { 4570 } else if (CurrentToken() == Token::kSEMICOLON) {
4492 ConsumeToken(); 4571 ConsumeToken();
4493 break; 4572 break;
(...skipping 2206 matching lines...) Expand 10 before | Expand all | Expand 10 after
6700 // Now create a label for the end of catch block processing so that we can 6779 // Now create a label for the end of catch block processing so that we can
6701 // jump over the catch block code after executing the try block. 6780 // jump over the catch block code after executing the try block.
6702 SourceLabel* end_catch_label = 6781 SourceLabel* end_catch_label =
6703 SourceLabel::New(TokenPos(), NULL, SourceLabel::kCatch); 6782 SourceLabel::New(TokenPos(), NULL, SourceLabel::kCatch);
6704 6783
6705 // Now parse the 'catch' blocks if any and merge all of them into 6784 // Now parse the 'catch' blocks if any and merge all of them into
6706 // an if-then sequence of the different types specified using the 'is' 6785 // an if-then sequence of the different types specified using the 'is'
6707 // operator. 6786 // operator.
6708 bool catch_seen = false; 6787 bool catch_seen = false;
6709 bool generic_catch_seen = false; 6788 bool generic_catch_seen = false;
6710 SequenceNode* catch_handler_list = NULL;
6711 const intptr_t handler_pos = TokenPos(); 6789 const intptr_t handler_pos = TokenPos();
6712 OpenBlock(); // Start the catch block sequence. 6790 OpenBlock(); // Start the catch block sequence.
6713 current_block_->scope->AddLabel(end_catch_label); 6791 current_block_->scope->AddLabel(end_catch_label);
6714 const GrowableObjectArray& handler_types = 6792 const GrowableObjectArray& handler_types =
6715 GrowableObjectArray::Handle(GrowableObjectArray::New()); 6793 GrowableObjectArray::Handle(GrowableObjectArray::New());
6716 bool needs_stacktrace = false; 6794 bool needs_stacktrace = false;
6717 while ((CurrentToken() == Token::kCATCH) || IsLiteral("on")) { 6795 while ((CurrentToken() == Token::kCATCH) || IsLiteral("on")) {
6718 const intptr_t catch_pos = TokenPos(); 6796 const intptr_t catch_pos = TokenPos();
6719 CatchParamDesc exception_param; 6797 CatchParamDesc exception_param;
6720 CatchParamDesc stack_trace_param; 6798 CatchParamDesc stack_trace_param;
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
6824 // This catch clause will handle all exceptions. We can safely forget 6902 // This catch clause will handle all exceptions. We can safely forget
6825 // all previous catch clause types. 6903 // all previous catch clause types.
6826 handler_types.SetLength(0); 6904 handler_types.SetLength(0);
6827 handler_types.Add(*exception_param.type); 6905 handler_types.Add(*exception_param.type);
6828 } 6906 }
6829 SequenceNode* catch_clause = CloseBlock(); 6907 SequenceNode* catch_clause = CloseBlock();
6830 6908
6831 // Add this individual catch handler to the catch handlers list. 6909 // Add this individual catch handler to the catch handlers list.
6832 current_block_->statements->Add(catch_clause); 6910 current_block_->statements->Add(catch_clause);
6833 } 6911 }
6834 catch_handler_list = CloseBlock(); 6912 SequenceNode* catch_handler_list = CloseBlock();
6835 TryBlocks* inner_try_block = PopTryBlock(); 6913 TryBlocks* inner_try_block = PopTryBlock();
6836 const intptr_t try_index = inner_try_block->try_index(); 6914 const intptr_t try_index = inner_try_block->try_index();
6837 TryBlocks* outer_try_block = try_blocks_list_; 6915 TryBlocks* outer_try_block = try_blocks_list_;
6838 const intptr_t outer_try_index = (outer_try_block != NULL) 6916 const intptr_t outer_try_index = (outer_try_block != NULL)
6839 ? outer_try_block->try_index() 6917 ? outer_try_block->try_index()
6840 : CatchClauseNode::kInvalidTryIndex; 6918 : CatchClauseNode::kInvalidTryIndex;
6841 6919
6842 // Finally parse the 'finally' block. 6920 // Finally parse the 'finally' block.
6843 SequenceNode* finally_block = NULL; 6921 SequenceNode* finally_block = NULL;
6844 if (CurrentToken() == Token::kFINALLY) { 6922 if (CurrentToken() == Token::kFINALLY) {
(...skipping 3786 matching lines...) Expand 10 before | Expand all | Expand 10 after
10631 void Parser::SkipQualIdent() { 10709 void Parser::SkipQualIdent() {
10632 ASSERT(IsIdentifier()); 10710 ASSERT(IsIdentifier());
10633 ConsumeToken(); 10711 ConsumeToken();
10634 if (CurrentToken() == Token::kPERIOD) { 10712 if (CurrentToken() == Token::kPERIOD) {
10635 ConsumeToken(); // Consume the kPERIOD token. 10713 ConsumeToken(); // Consume the kPERIOD token.
10636 ExpectIdentifier("identifier expected after '.'"); 10714 ExpectIdentifier("identifier expected after '.'");
10637 } 10715 }
10638 } 10716 }
10639 10717
10640 } // namespace dart 10718 } // namespace dart
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698