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

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

Issue 471283002: Runtime support for evaluation of static field initializer expressions (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 6 years, 4 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 | 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 "platform/utils.h" 8 #include "platform/utils.h"
9 #include "vm/bootstrap.h" 9 #include "vm/bootstrap.h"
10 #include "vm/class_finalizer.h" 10 #include "vm/class_finalizer.h"
(...skipping 809 matching lines...) Expand 10 before | Expand all | Expand 10 after
820 node_sequence = parser.ParseInstanceGetter(func); 820 node_sequence = parser.ParseInstanceGetter(func);
821 break; 821 break;
822 case RawFunction::kImplicitSetter: 822 case RawFunction::kImplicitSetter:
823 ASSERT(!func.is_static()); 823 ASSERT(!func.is_static());
824 node_sequence = parser.ParseInstanceSetter(func); 824 node_sequence = parser.ParseInstanceSetter(func);
825 break; 825 break;
826 case RawFunction::kImplicitStaticFinalGetter: 826 case RawFunction::kImplicitStaticFinalGetter:
827 node_sequence = parser.ParseStaticFinalGetter(func); 827 node_sequence = parser.ParseStaticFinalGetter(func);
828 CompilerStats::num_implicit_final_getters++; 828 CompilerStats::num_implicit_final_getters++;
829 break; 829 break;
830 case RawFunction::kStaticInitializer:
831 node_sequence = parser.ParseStaticInitializer(func);
832 CompilerStats::num_static_initializer_funcs++;
833 break;
834 case RawFunction::kMethodExtractor: 830 case RawFunction::kMethodExtractor:
835 node_sequence = parser.ParseMethodExtractor(func); 831 node_sequence = parser.ParseMethodExtractor(func);
836 break; 832 break;
837 case RawFunction::kNoSuchMethodDispatcher: 833 case RawFunction::kNoSuchMethodDispatcher:
838 node_sequence = 834 node_sequence =
839 parser.ParseNoSuchMethodDispatcher(func, &default_parameter_values); 835 parser.ParseNoSuchMethodDispatcher(func, &default_parameter_values);
840 break; 836 break;
841 case RawFunction::kInvokeFieldDispatcher: 837 case RawFunction::kInvokeFieldDispatcher:
842 node_sequence = 838 node_sequence =
843 parser.ParseInvokeFieldDispatcher(func, &default_parameter_values); 839 parser.ParseInvokeFieldDispatcher(func, &default_parameter_values);
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
995 if (expr->EvalConstExpr() == NULL) { 991 if (expr->EvalConstExpr() == NULL) {
996 ReportError(expr_pos, "expression must be a compile-time constant"); 992 ReportError(expr_pos, "expression must be a compile-time constant");
997 } 993 }
998 const Instance& val = EvaluateConstExpr(expr_pos, expr); 994 const Instance& val = EvaluateConstExpr(expr_pos, expr);
999 meta_values.Add(val); 995 meta_values.Add(val);
1000 } 996 }
1001 return Array::MakeArray(meta_values); 997 return Array::MakeArray(meta_values);
1002 } 998 }
1003 999
1004 1000
1001 SequenceNode* Parser::ParseStaticInitializer() {
1002 ExpectIdentifier("field name expected");
1003 CheckToken(Token::kASSIGN, "field initialier expected");
1004 ConsumeToken();
1005 OpenFunctionBlock(parsed_function()->function());
1006 intptr_t expr_pos = TokenPos();
1007 AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
1008 ReturnNode* ret = new(I) ReturnNode(expr_pos, expr);
1009 current_block_->statements->Add(ret);
1010 return CloseBlock();
1011 }
1012
1013
1014 ParsedFunction* Parser::ParseStaticFieldInitializer(const Field& field) {
1015 ASSERT(field.is_static());
1016 ASSERT(field.value() == Object::transition_sentinel().raw());
1017 Isolate* isolate = Isolate::Current();
1018
1019 const Class& script_cls = Class::Handle(isolate, field.origin());
1020 const Script& script = Script::Handle(isolate, script_cls.script());
1021
1022 const String& field_name = String::Handle(isolate, field.name());
1023 String& init_name = String::Handle(isolate,
1024 String::Concat(Symbols::InitPrefix(), field_name));
1025 init_name = Symbols::New(init_name);
1026
1027 const Function& initializer = Function::ZoneHandle(isolate,
1028 Function::New(init_name,
1029 RawFunction::kRegularFunction,
1030 true, // static
1031 false, // !const
1032 false, // !abstract
1033 false, // !external
1034 false, // !native
1035 Class::Handle(field.owner()),
1036 field.token_pos()));
1037 initializer.set_result_type(AbstractType::Handle(isolate, field.type()));
1038 // Static initializer functions are hidden from the user.
1039 // Since they are only executed once, we avoid optimizing
1040 // and inlining them. After the field is initialized, the
1041 // compiler can eliminate the call to the static initializer.
1042 initializer.set_is_visible(false);
1043 initializer.SetIsOptimizable(false);
1044 initializer.set_is_inlinable(false);
1045
1046 ParsedFunction* parsed_function = new ParsedFunction(isolate, initializer);
1047 Parser parser(script, parsed_function, field.token_pos());
1048
1049 SequenceNode* body = parser.ParseStaticInitializer();
1050 parsed_function->SetNodeSequence(body);
1051 parsed_function->set_default_parameter_values(Object::null_array());
1052
1053 if (parsed_function->has_expression_temp_var()) {
1054 body->scope()->AddVariable(parsed_function->expression_temp_var());
1055 }
1056 if (parsed_function->has_saved_current_context_var()) {
1057 body->scope()->AddVariable(parsed_function->saved_current_context_var());
1058 }
1059 if (parsed_function->has_finally_return_temp_var()) {
1060 body->scope()->AddVariable(parsed_function->finally_return_temp_var());
1061 }
1062 // The instantiator is not required in a static expression.
1063 ASSERT(!parser.IsInstantiatorRequired());
1064
1065 return parsed_function;
1066 }
1067
1068
1005 SequenceNode* Parser::ParseStaticFinalGetter(const Function& func) { 1069 SequenceNode* Parser::ParseStaticFinalGetter(const Function& func) {
1006 TRACE_PARSER("ParseStaticFinalGetter"); 1070 TRACE_PARSER("ParseStaticFinalGetter");
1007 ParamList params; 1071 ParamList params;
1008 ASSERT(func.num_fixed_parameters() == 0); // static. 1072 ASSERT(func.num_fixed_parameters() == 0); // static.
1009 ASSERT(!func.HasOptionalParameters()); 1073 ASSERT(!func.HasOptionalParameters());
1010 ASSERT(AbstractType::Handle(I, func.result_type()).IsResolved()); 1074 ASSERT(AbstractType::Handle(I, func.result_type()).IsResolved());
1011 1075
1012 // Build local scope for function and populate with the formal parameters. 1076 // Build local scope for function and populate with the formal parameters.
1013 OpenFunctionBlock(func); 1077 OpenFunctionBlock(func);
1014 AddFormalParamsToScope(&params, current_block_->scope); 1078 AddFormalParamsToScope(&params, current_block_->scope);
(...skipping 16 matching lines...) Expand all
1031 // leave the evaluation to the getter function. 1095 // leave the evaluation to the getter function.
1032 AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades); 1096 AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
1033 // This getter will only be called once at compile time. 1097 // This getter will only be called once at compile time.
1034 if (expr->EvalConstExpr() == NULL) { 1098 if (expr->EvalConstExpr() == NULL) {
1035 ReportError(expr_pos, "initializer is not a valid compile-time constant"); 1099 ReportError(expr_pos, "initializer is not a valid compile-time constant");
1036 } 1100 }
1037 ReturnNode* return_node = new ReturnNode(ident_pos, expr); 1101 ReturnNode* return_node = new ReturnNode(ident_pos, expr);
1038 current_block_->statements->Add(return_node); 1102 current_block_->statements->Add(return_node);
1039 } else { 1103 } else {
1040 // This getter may be called each time the static field is accessed. 1104 // This getter may be called each time the static field is accessed.
1041 // The following generated code lazily initializes the field: 1105 // Call runtime support to parse and evaluate the initializer expression.
1042 // if (field.value === transition_sentinel) { 1106 // The runtime function will detect circular dependencies in expressions
1043 // field.value = null; 1107 // and handle errors while evaluating the expression.
1044 // throw("circular dependency in field initialization"); 1108 current_block_->statements->Add(
1045 // } 1109 new (I) InitStaticFieldNode(ident_pos, field));
1046 // if (field.value === sentinel) {
1047 // field.value = transition_sentinel;
1048 // field.value = expr;
1049 // }
1050 // return field.value; // Type check is executed here in checked mode.
1051
1052 // Generate code checking for circular dependency in field initialization.
1053 AstNode* compare_circular = new ComparisonNode(
1054 ident_pos,
1055 Token::kEQ_STRICT,
1056 new LoadStaticFieldNode(ident_pos, field),
1057 new LiteralNode(ident_pos, Object::transition_sentinel()));
1058 // Set field to null prior to throwing exception, so that subsequent
1059 // accesses to the field do not throw again, since initializers should only
1060 // be executed once.
1061 SequenceNode* report_circular = new SequenceNode(ident_pos, NULL);
1062 report_circular->Add(
1063 new StoreStaticFieldNode(
1064 ident_pos,
1065 field,
1066 new LiteralNode(ident_pos, Instance::ZoneHandle(I))));
1067 // Call CyclicInitializationError._throwNew(field_name).
1068 ArgumentListNode* error_arguments = new ArgumentListNode(ident_pos);
1069 error_arguments->Add(new LiteralNode(ident_pos, field_name));
1070 report_circular->Add(
1071 MakeStaticCall(Symbols::CyclicInitializationError(),
1072 Library::PrivateCoreLibName(Symbols::ThrowNew()),
1073 error_arguments));
1074 AstNode* circular_check =
1075 new IfNode(ident_pos, compare_circular, report_circular, NULL);
1076 current_block_->statements->Add(circular_check);
1077
1078 // Generate code checking for uninitialized field.
1079 AstNode* compare_uninitialized = new ComparisonNode(
1080 ident_pos,
1081 Token::kEQ_STRICT,
1082 new LoadStaticFieldNode(ident_pos, field),
1083 new LiteralNode(ident_pos, Object::sentinel()));
1084 SequenceNode* initialize_field = new SequenceNode(ident_pos, NULL);
1085 initialize_field->Add(
1086 new StoreStaticFieldNode(
1087 ident_pos,
1088 field,
1089 new LiteralNode(ident_pos, Object::transition_sentinel())));
1090 const String& init_name = String::Handle(I, Symbols::New(
1091 String::Handle(I, String::Concat(
1092 Symbols::InitPrefix(), String::Handle(I, field.name())))));
1093 const Function& init_function = Function::ZoneHandle(I,
1094 field_class.LookupStaticFunction(init_name));
1095 ASSERT(!init_function.IsNull());
1096 ArgumentListNode* arguments = new ArgumentListNode(expr_pos);
1097 StaticCallNode* init_call =
1098 new StaticCallNode(expr_pos, init_function, arguments);
1099 initialize_field->Add(init_call);
1100
1101 AstNode* uninitialized_check =
1102 new IfNode(ident_pos, compare_uninitialized, initialize_field, NULL);
1103 current_block_->statements->Add(uninitialized_check);
1104
1105 // Generate code returning the field value.
1106 ReturnNode* return_node = 1110 ReturnNode* return_node =
1107 new ReturnNode(ident_pos, new LoadStaticFieldNode(ident_pos, field)); 1111 new ReturnNode(ident_pos,
1112 new LoadStaticFieldNode(ident_pos, field));
1108 current_block_->statements->Add(return_node); 1113 current_block_->statements->Add(return_node);
1109 } 1114 }
1110 return CloseBlock(); 1115 return CloseBlock();
1111 } 1116 }
1112 1117
1113 1118
1114 SequenceNode* Parser::ParseStaticInitializer(const Function& func) {
1115 TRACE_PARSER("ParseStaticInitializer");
1116 ParamList params;
1117 ASSERT(func.num_fixed_parameters() == 0); // static.
1118 ASSERT(!func.HasOptionalParameters());
1119 ASSERT(AbstractType::Handle(I, func.result_type()).IsResolved());
1120
1121 // Build local scope for function and populate with the formal parameters.
1122 OpenFunctionBlock(func);
1123 AddFormalParamsToScope(&params, current_block_->scope);
1124
1125 // Move forward to the start of the initializer expression.
1126 intptr_t ident_pos = TokenPos();
1127 ExpectIdentifier("identifier expected");
1128 ExpectToken(Token::kASSIGN);
1129 intptr_t token_pos = TokenPos();
1130
1131 // Synthesize a try-catch block to wrap the initializer expression.
1132 LocalVariable* context_var =
1133 current_block_->scope->LocalLookupVariable(Symbols::SavedTryContextVar());
1134 if (context_var == NULL) {
1135 context_var = new(I) LocalVariable(
1136 token_pos,
1137 Symbols::SavedTryContextVar(),
1138 Type::ZoneHandle(I, Type::DynamicType()));
1139 current_block_->scope->AddVariable(context_var);
1140 }
1141 LocalVariable* catch_excp_var =
1142 current_block_->scope->LocalLookupVariable(Symbols::ExceptionVar());
1143 if (catch_excp_var == NULL) {
1144 catch_excp_var = new (I) LocalVariable(
1145 token_pos,
1146 Symbols::ExceptionVar(),
1147 Type::ZoneHandle(I, Type::DynamicType()));
1148 current_block_->scope->AddVariable(catch_excp_var);
1149 }
1150 LocalVariable* catch_trace_var =
1151 current_block_->scope->LocalLookupVariable(Symbols::StackTraceVar());
1152 if (catch_trace_var == NULL) {
1153 catch_trace_var = new (I) LocalVariable(
1154 token_pos,
1155 Symbols::StackTraceVar(),
1156 Type::ZoneHandle(I, Type::DynamicType()));
1157 current_block_->scope->AddVariable(catch_trace_var);
1158 }
1159
1160 OpenBlock(); // Start try block.
1161 AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
1162 const Field& field = Field::ZoneHandle(I, func.saved_static_field());
1163 ASSERT(!field.is_const());
1164 if (FLAG_enable_type_checks) {
1165 expr = new AssignableNode(
1166 field.token_pos(),
1167 expr,
1168 AbstractType::ZoneHandle(I, field.type()),
1169 String::ZoneHandle(I, field.name()));
1170 }
1171 StoreStaticFieldNode* store = new StoreStaticFieldNode(field.token_pos(),
1172 field,
1173 expr);
1174 current_block_->statements->Add(store);
1175 SequenceNode* try_block = CloseBlock(); // End try block.
1176
1177 OpenBlock(); // Start catch handler list.
1178 OpenBlock(); // Start catch clause.
1179 AstNode* compare_transition_sentinel = new ComparisonNode(
1180 token_pos,
1181 Token::kEQ_STRICT,
1182 new LoadStaticFieldNode(ident_pos, field),
1183 new LiteralNode(field.token_pos(), Object::transition_sentinel()));
1184
1185 SequenceNode* store_null = new SequenceNode(token_pos, NULL);
1186 store_null->Add(new StoreStaticFieldNode(
1187 field.token_pos(),
1188 field,
1189 new LiteralNode(token_pos, Instance::ZoneHandle(I))));
1190 AstNode* transition_sentinel_check =
1191 new IfNode(token_pos, compare_transition_sentinel, store_null, NULL);
1192 current_block_->statements->Add(transition_sentinel_check);
1193
1194 current_block_->statements->Add(
1195 new ThrowNode(token_pos,
1196 new LoadLocalNode(token_pos, catch_excp_var),
1197 new LoadLocalNode(token_pos, catch_trace_var)));
1198 SequenceNode* catch_clause = CloseBlock(); // End catch clause.
1199
1200 current_block_->statements->Add(catch_clause);
1201 SequenceNode* catch_handler_list = CloseBlock(); // End catch handler list.
1202 CatchClauseNode* catch_block =
1203 new CatchClauseNode(token_pos,
1204 catch_handler_list,
1205 Array::ZoneHandle(I, Object::empty_array().raw()),
1206 context_var,
1207 catch_excp_var,
1208 catch_trace_var,
1209 CatchClauseNode::kInvalidTryIndex,
1210 false); // No stack trace needed.
1211
1212 AstNode* try_catch_node = new TryCatchNode(token_pos,
1213 try_block,
1214 context_var,
1215 catch_block,
1216 NULL, // No finally block.
1217 AllocateTryIndex());
1218 current_block_->statements->Add(try_catch_node);
1219 return CloseBlock();
1220 }
1221
1222
1223 // Create AstNodes for an implicit instance getter method: 1119 // Create AstNodes for an implicit instance getter method:
1224 // LoadLocalNode 0 ('this'); 1120 // LoadLocalNode 0 ('this');
1225 // LoadInstanceFieldNode (field_name); 1121 // LoadInstanceFieldNode (field_name);
1226 // ReturnNode (field's value); 1122 // ReturnNode (field's value);
1227 SequenceNode* Parser::ParseInstanceGetter(const Function& func) { 1123 SequenceNode* Parser::ParseInstanceGetter(const Function& func) {
1228 TRACE_PARSER("ParseInstanceGetter"); 1124 TRACE_PARSER("ParseInstanceGetter");
1229 ParamList params; 1125 ParamList params;
1230 // func.token_pos() points to the name of the field. 1126 // func.token_pos() points to the name of the field.
1231 const intptr_t ident_pos = func.token_pos(); 1127 const intptr_t ident_pos = func.token_pos();
1232 ASSERT(current_class().raw() == func.Owner()); 1128 ASSERT(current_class().raw() == func.Owner());
(...skipping 2440 matching lines...) Expand 10 before | Expand all | Expand 10 after
3673 RawFunction::kImplicitStaticFinalGetter, 3569 RawFunction::kImplicitStaticFinalGetter,
3674 field->has_static, 3570 field->has_static,
3675 field->has_const, 3571 field->has_const,
3676 /* is_abstract = */ false, 3572 /* is_abstract = */ false,
3677 /* is_external = */ false, 3573 /* is_external = */ false,
3678 /* is_native = */ false, 3574 /* is_native = */ false,
3679 current_class(), 3575 current_class(),
3680 field->name_pos); 3576 field->name_pos);
3681 getter.set_result_type(*field->type); 3577 getter.set_result_type(*field->type);
3682 members->AddFunction(getter); 3578 members->AddFunction(getter);
3683
3684 // Create initializer function for non-const fields.
3685 if (!class_field.is_const()) {
3686 const Function& init_function = Function::ZoneHandle(I,
3687 Function::NewStaticInitializer(class_field));
3688 members->AddFunction(init_function);
3689 }
3690 } 3579 }
3691 } 3580 }
3692 3581
3693 // For instance fields, we create implicit getter and setter methods. 3582 // For instance fields, we create implicit getter and setter methods.
3694 if (!field->has_static) { 3583 if (!field->has_static) {
3695 String& getter_name = String::Handle(I, 3584 String& getter_name = String::Handle(I,
3696 Field::GetterSymbol(*field->name)); 3585 Field::GetterSymbol(*field->name));
3697 getter = Function::New(getter_name, RawFunction::kImplicitGetter, 3586 getter = Function::New(getter_name, RawFunction::kImplicitGetter,
3698 field->has_static, 3587 field->has_static,
3699 field->has_final, 3588 field->has_final,
(...skipping 1138 matching lines...) Expand 10 before | Expand all | Expand 10 after
4838 RawFunction::kImplicitStaticFinalGetter, 4727 RawFunction::kImplicitStaticFinalGetter,
4839 is_static, 4728 is_static,
4840 is_const, 4729 is_const,
4841 /* is_abstract = */ false, 4730 /* is_abstract = */ false,
4842 /* is_external = */ false, 4731 /* is_external = */ false,
4843 /* is_native = */ false, 4732 /* is_native = */ false,
4844 current_class(), 4733 current_class(),
4845 name_pos); 4734 name_pos);
4846 getter.set_result_type(type); 4735 getter.set_result_type(type);
4847 top_level->functions.Add(getter); 4736 top_level->functions.Add(getter);
4848
4849 // Create initializer function.
4850 if (!field.is_const()) {
4851 const Function& init_function = Function::ZoneHandle(I,
4852 Function::NewStaticInitializer(field));
4853 top_level->functions.Add(init_function);
4854 }
4855 } 4737 }
4856 } else if (is_final) { 4738 } else if (is_final) {
4857 ReportError(name_pos, "missing initializer for final or const variable"); 4739 ReportError(name_pos, "missing initializer for final or const variable");
4858 } 4740 }
4859 4741
4860 if (CurrentToken() == Token::kCOMMA) { 4742 if (CurrentToken() == Token::kCOMMA) {
4861 ConsumeToken(); 4743 ConsumeToken();
4862 } else if (CurrentToken() == Token::kSEMICOLON) { 4744 } else if (CurrentToken() == Token::kSEMICOLON) {
4863 ConsumeToken(); 4745 ConsumeToken();
4864 break; 4746 break;
(...skipping 6089 matching lines...) Expand 10 before | Expand all | Expand 10 after
10954 ASSERT(field.is_const()); 10836 ASSERT(field.is_const());
10955 ASSERT(field.value() != Object::sentinel().raw()); 10837 ASSERT(field.value() != Object::sentinel().raw());
10956 ASSERT(field.value() != Object::transition_sentinel().raw()); 10838 ASSERT(field.value() != Object::transition_sentinel().raw());
10957 return Instance::ZoneHandle(I, field.value()); 10839 return Instance::ZoneHandle(I, field.value());
10958 } else { 10840 } else {
10959 ASSERT(expr->EvalConstExpr() != NULL); 10841 ASSERT(expr->EvalConstExpr() != NULL);
10960 ReturnNode* ret = new(I) ReturnNode(expr->token_pos(), expr); 10842 ReturnNode* ret = new(I) ReturnNode(expr->token_pos(), expr);
10961 // Compile time constant expressions cannot reference anything from a 10843 // Compile time constant expressions cannot reference anything from a
10962 // local scope. 10844 // local scope.
10963 LocalScope* empty_scope = new(I) LocalScope(NULL, 0, 0); 10845 LocalScope* empty_scope = new(I) LocalScope(NULL, 0, 0);
10964 SequenceNode* seq = new(I) SequenceNode(expr->token_pos(), 10846 SequenceNode* seq = new(I) SequenceNode(expr->token_pos(), empty_scope);
10965 empty_scope);
10966 seq->Add(ret); 10847 seq->Add(ret);
10967 10848
10968 Object& result = Object::Handle(I, Compiler::ExecuteOnce(seq)); 10849 Object& result = Object::Handle(I, Compiler::ExecuteOnce(seq));
10969 if (result.IsError()) { 10850 if (result.IsError()) {
10970 ReportErrors(Error::Cast(result), 10851 ReportErrors(Error::Cast(result),
10971 script_, expr_pos, 10852 script_, expr_pos,
10972 "error evaluating constant expression"); 10853 "error evaluating constant expression");
10973 } 10854 }
10974 ASSERT(result.IsInstance()); 10855 ASSERT(result.IsInstance());
10975 Instance& value = Instance::ZoneHandle(I); 10856 Instance& value = Instance::ZoneHandle(I);
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after
11308 void Parser::SkipQualIdent() { 11189 void Parser::SkipQualIdent() {
11309 ASSERT(IsIdentifier()); 11190 ASSERT(IsIdentifier());
11310 ConsumeToken(); 11191 ConsumeToken();
11311 if (CurrentToken() == Token::kPERIOD) { 11192 if (CurrentToken() == Token::kPERIOD) {
11312 ConsumeToken(); // Consume the kPERIOD token. 11193 ConsumeToken(); // Consume the kPERIOD token.
11313 ExpectIdentifier("identifier expected after '.'"); 11194 ExpectIdentifier("identifier expected after '.'");
11314 } 11195 }
11315 } 11196 }
11316 11197
11317 } // namespace dart 11198 } // namespace dart
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698