OLD | NEW |
---|---|
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/ast_transformer.h" | 9 #include "vm/ast_transformer.h" |
10 #include "vm/bootstrap.h" | 10 #include "vm/bootstrap.h" |
11 #include "vm/class_finalizer.h" | 11 #include "vm/class_finalizer.h" |
12 #include "vm/compiler.h" | 12 #include "vm/compiler.h" |
13 #include "vm/compiler_stats.h" | 13 #include "vm/compiler_stats.h" |
14 #include "vm/dart_api_impl.h" | 14 #include "vm/dart_api_impl.h" |
15 #include "vm/dart_entry.h" | 15 #include "vm/dart_entry.h" |
16 #include "vm/flags.h" | 16 #include "vm/flags.h" |
17 #include "vm/growable_array.h" | 17 #include "vm/growable_array.h" |
18 #include "vm/handles.h" | 18 #include "vm/handles.h" |
19 #include "vm/hash_table.h" | |
19 #include "vm/heap.h" | 20 #include "vm/heap.h" |
20 #include "vm/isolate.h" | 21 #include "vm/isolate.h" |
21 #include "vm/longjump.h" | 22 #include "vm/longjump.h" |
22 #include "vm/native_arguments.h" | 23 #include "vm/native_arguments.h" |
23 #include "vm/native_entry.h" | 24 #include "vm/native_entry.h" |
24 #include "vm/object.h" | 25 #include "vm/object.h" |
25 #include "vm/object_store.h" | 26 #include "vm/object_store.h" |
26 #include "vm/os.h" | 27 #include "vm/os.h" |
27 #include "vm/regexp_assembler.h" | 28 #include "vm/regexp_assembler.h" |
28 #include "vm/report.h" | 29 #include "vm/report.h" |
(...skipping 11849 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
11878 bool Parser::IsInstantiatorRequired() const { | 11879 bool Parser::IsInstantiatorRequired() const { |
11879 ASSERT(!current_function().IsNull()); | 11880 ASSERT(!current_function().IsNull()); |
11880 if (current_function().is_static() && | 11881 if (current_function().is_static() && |
11881 !current_function().IsInFactoryScope()) { | 11882 !current_function().IsInFactoryScope()) { |
11882 return false; | 11883 return false; |
11883 } | 11884 } |
11884 return current_class().NumTypeParameters() > 0; | 11885 return current_class().NumTypeParameters() > 0; |
11885 } | 11886 } |
11886 | 11887 |
11887 | 11888 |
11889 class ConstMapKeyEqualsTraits { | |
11890 public: | |
11891 static bool IsMatch(const Object& a, const Object& b) { | |
11892 return String::Cast(a).Equals(String::Cast(b)); | |
11893 } | |
11894 static uword Hash(const Object& obj) { | |
11895 return String::Cast(obj).Hash(); | |
11896 } | |
11897 }; | |
11898 typedef UnorderedHashMap<ConstMapKeyEqualsTraits> ConstantsMap; | |
11899 | |
11900 | |
11901 static void BuildConstMapKey(const Script& script, | |
11902 intptr_t token_pos, | |
11903 String* key) { | |
11904 *key = script.url(); | |
11905 String& suffix = String::Handle(String::NewFormatted("_%" Pd "", token_pos)); | |
koda
2015/08/25 18:27:12
Add TODO: avoid allocations for lookup by introduc
koda
2015/08/25 18:27:12
Make private method and then: Handle(Z, ...)
hausner
2015/08/25 19:48:51
We discussed offline, but for those following alon
hausner
2015/08/25 19:48:51
Done.
srdjan
2015/08/25 22:16:19
String::NewFormatted is allocating a string in new
| |
11906 *key = Symbols::FromConcat(*key, suffix); | |
11907 } | |
11908 | |
11909 | |
11910 void Parser::CacheConstantValue(intptr_t token_pos, const Instance& value) { | |
11911 String& key = String::Handle(); | |
koda
2015/08/25 18:27:12
Handle(Z)
hausner
2015/08/25 19:48:51
Done.
| |
11912 BuildConstMapKey(script_, token_pos, &key); | |
11913 if (isolate()->object_store()->compile_time_constants() == Array::null()) { | |
11914 const intptr_t kInitialConstMapSize = 16; | |
11915 isolate()->object_store()->set_compile_time_constants( | |
11916 Array::Handle(Z, HashTables::New<ConstantsMap>(kInitialConstMapSize))); | |
srdjan
2015/08/25 22:16:19
It does not matter for optimizing compiler (since
| |
11917 } | |
11918 ConstantsMap constants(isolate()->object_store()->compile_time_constants()); | |
11919 constants.UpdateOrInsert(key, value); | |
11920 if (FLAG_compiler_stats) { | |
11921 isolate_->compiler_stats()->num_cached_consts = constants.NumOccupied(); | |
11922 } | |
11923 isolate()->object_store()->set_compile_time_constants(constants.Release()); | |
11924 } | |
11925 | |
11926 | |
11927 bool Parser::GetCachedConstant(intptr_t token_pos, Instance* value) { | |
11928 if (isolate()->object_store()->compile_time_constants() == Array::null()) { | |
11929 return false; | |
11930 } | |
11931 String& key = String::Handle(); | |
koda
2015/08/25 18:27:12
Handle(Z)
hausner
2015/08/25 19:48:52
Done.
| |
11932 BuildConstMapKey(script_, token_pos, &key); | |
11933 ConstantsMap constants(isolate()->object_store()->compile_time_constants()); | |
11934 bool is_present = false; | |
11935 *value ^= constants.GetOrNull(key, &is_present); | |
11936 ASSERT(constants.Release().raw() == | |
11937 isolate()->object_store()->compile_time_constants()); | |
11938 if (FLAG_compiler_stats && is_present) { | |
11939 isolate_->compiler_stats()->num_const_cache_hits++; | |
11940 } | |
11941 return is_present; | |
11942 } | |
11943 | |
11944 | |
11888 RawInstance* Parser::TryCanonicalize(const Instance& instance, | 11945 RawInstance* Parser::TryCanonicalize(const Instance& instance, |
11889 intptr_t token_pos) { | 11946 intptr_t token_pos) { |
11890 if (instance.IsNull()) { | 11947 if (instance.IsNull()) { |
11891 return instance.raw(); | 11948 return instance.raw(); |
11892 } | 11949 } |
11893 const char* error_str = NULL; | 11950 const char* error_str = NULL; |
11894 Instance& result = | 11951 Instance& result = |
11895 Instance::Handle(Z, instance.CheckAndCanonicalize(&error_str)); | 11952 Instance::Handle(Z, instance.CheckAndCanonicalize(&error_str)); |
11896 if (result.IsNull()) { | 11953 if (result.IsNull()) { |
11897 ReportError(token_pos, "Invalid const object %s", error_str); | 11954 ReportError(token_pos, "Invalid const object %s", error_str); |
(...skipping 602 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
12500 // Note: if the list literal is empty and the brackets have no whitespace | 12557 // Note: if the list literal is empty and the brackets have no whitespace |
12501 // between them, the scanner recognizes the opening and closing bracket | 12558 // between them, the scanner recognizes the opening and closing bracket |
12502 // as one token of type Token::kINDEX. | 12559 // as one token of type Token::kINDEX. |
12503 AstNode* Parser::ParseListLiteral(intptr_t type_pos, | 12560 AstNode* Parser::ParseListLiteral(intptr_t type_pos, |
12504 bool is_const, | 12561 bool is_const, |
12505 const TypeArguments& type_arguments) { | 12562 const TypeArguments& type_arguments) { |
12506 TRACE_PARSER("ParseListLiteral"); | 12563 TRACE_PARSER("ParseListLiteral"); |
12507 ASSERT(type_pos >= 0); | 12564 ASSERT(type_pos >= 0); |
12508 ASSERT(CurrentToken() == Token::kLBRACK || CurrentToken() == Token::kINDEX); | 12565 ASSERT(CurrentToken() == Token::kLBRACK || CurrentToken() == Token::kINDEX); |
12509 const intptr_t literal_pos = TokenPos(); | 12566 const intptr_t literal_pos = TokenPos(); |
12567 | |
12568 if (is_const) { | |
12569 Instance& existing_const = Instance::ZoneHandle(Z); | |
12570 if (GetCachedConstant(literal_pos, &existing_const)) { | |
12571 SkipListLiteral(); | |
12572 return new(Z) LiteralNode(literal_pos, existing_const); | |
12573 } | |
12574 } | |
12575 | |
12510 bool is_empty_literal = CurrentToken() == Token::kINDEX; | 12576 bool is_empty_literal = CurrentToken() == Token::kINDEX; |
12511 ConsumeToken(); | 12577 ConsumeToken(); |
12512 | 12578 |
12513 AbstractType& element_type = Type::ZoneHandle(Z, Type::DynamicType()); | 12579 AbstractType& element_type = Type::ZoneHandle(Z, Type::DynamicType()); |
12514 TypeArguments& list_type_arguments = | 12580 TypeArguments& list_type_arguments = |
12515 TypeArguments::ZoneHandle(Z, type_arguments.raw()); | 12581 TypeArguments::ZoneHandle(Z, type_arguments.raw()); |
12516 // If no type argument vector is provided, leave it as null, which is | 12582 // If no type argument vector is provided, leave it as null, which is |
12517 // equivalent to using dynamic as the type argument for the element type. | 12583 // equivalent to using dynamic as the type argument for the element type. |
12518 if (!list_type_arguments.IsNull()) { | 12584 if (!list_type_arguments.IsNull()) { |
12519 ASSERT(list_type_arguments.Length() > 0); | 12585 ASSERT(list_type_arguments.Length() > 0); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
12567 } else if (CurrentToken() != Token::kRBRACK) { | 12633 } else if (CurrentToken() != Token::kRBRACK) { |
12568 ReportError("comma or ']' expected"); | 12634 ReportError("comma or ']' expected"); |
12569 } | 12635 } |
12570 } | 12636 } |
12571 ExpectToken(Token::kRBRACK); | 12637 ExpectToken(Token::kRBRACK); |
12572 SetAllowFunctionLiterals(saved_mode); | 12638 SetAllowFunctionLiterals(saved_mode); |
12573 } | 12639 } |
12574 | 12640 |
12575 if (is_const) { | 12641 if (is_const) { |
12576 // Allocate and initialize the const list at compile time. | 12642 // Allocate and initialize the const list at compile time. |
12577 Array& const_list = | 12643 Array& const_list = Array::ZoneHandle(Z, |
12578 Array::ZoneHandle(Z, Array::New(element_list.length(), Heap::kOld)); | 12644 Array::New(element_list.length(), Heap::kOld)); |
12579 const_list.SetTypeArguments( | 12645 const_list.SetTypeArguments( |
12580 TypeArguments::Handle(Z, list_type_arguments.Canonicalize())); | 12646 TypeArguments::Handle(Z, list_type_arguments.Canonicalize())); |
12581 Error& malformed_error = Error::Handle(Z); | 12647 Error& malformed_error = Error::Handle(Z); |
12582 for (int i = 0; i < element_list.length(); i++) { | 12648 for (int i = 0; i < element_list.length(); i++) { |
12583 AstNode* elem = element_list[i]; | 12649 AstNode* elem = element_list[i]; |
12584 // Arguments have been evaluated to a literal value already. | 12650 // Arguments have been evaluated to a literal value already. |
12585 ASSERT(elem->IsLiteralNode()); | 12651 ASSERT(elem->IsLiteralNode()); |
12586 ASSERT(!is_top_level_); // We cannot check unresolved types. | 12652 ASSERT(!is_top_level_); // We cannot check unresolved types. |
12587 if (I->flags().type_checks() && | 12653 if (I->flags().type_checks() && |
12588 !element_type.IsDynamicType() && | 12654 !element_type.IsDynamicType() && |
(...skipping 11 matching lines...) Expand all Loading... | |
12600 "a constant of type '%s'", | 12666 "a constant of type '%s'", |
12601 i, | 12667 i, |
12602 String::Handle(Z, | 12668 String::Handle(Z, |
12603 element_type.UserVisibleName()).ToCString()); | 12669 element_type.UserVisibleName()).ToCString()); |
12604 } | 12670 } |
12605 } | 12671 } |
12606 const_list.SetAt(i, elem->AsLiteralNode()->literal()); | 12672 const_list.SetAt(i, elem->AsLiteralNode()->literal()); |
12607 } | 12673 } |
12608 const_list.MakeImmutable(); | 12674 const_list.MakeImmutable(); |
12609 const_list ^= TryCanonicalize(const_list, literal_pos); | 12675 const_list ^= TryCanonicalize(const_list, literal_pos); |
12676 CacheConstantValue(literal_pos, const_list); | |
12610 return new(Z) LiteralNode(literal_pos, const_list); | 12677 return new(Z) LiteralNode(literal_pos, const_list); |
12611 } else { | 12678 } else { |
12612 // Factory call at runtime. | 12679 // Factory call at runtime. |
12613 const Class& factory_class = | 12680 const Class& factory_class = |
12614 Class::Handle(Z, Library::LookupCoreClass(Symbols::List())); | 12681 Class::Handle(Z, Library::LookupCoreClass(Symbols::List())); |
12615 ASSERT(!factory_class.IsNull()); | 12682 ASSERT(!factory_class.IsNull()); |
12616 const Function& factory_method = Function::ZoneHandle(Z, | 12683 const Function& factory_method = Function::ZoneHandle(Z, |
12617 factory_class.LookupFactory( | 12684 factory_class.LookupFactory( |
12618 Library::PrivateCoreLibName(Symbols::ListLiteralFactory()))); | 12685 Library::PrivateCoreLibName(Symbols::ListLiteralFactory()))); |
12619 ASSERT(!factory_method.IsNull()); | 12686 ASSERT(!factory_method.IsNull()); |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
12693 } | 12760 } |
12694 | 12761 |
12695 | 12762 |
12696 AstNode* Parser::ParseMapLiteral(intptr_t type_pos, | 12763 AstNode* Parser::ParseMapLiteral(intptr_t type_pos, |
12697 bool is_const, | 12764 bool is_const, |
12698 const TypeArguments& type_arguments) { | 12765 const TypeArguments& type_arguments) { |
12699 TRACE_PARSER("ParseMapLiteral"); | 12766 TRACE_PARSER("ParseMapLiteral"); |
12700 ASSERT(type_pos >= 0); | 12767 ASSERT(type_pos >= 0); |
12701 ASSERT(CurrentToken() == Token::kLBRACE); | 12768 ASSERT(CurrentToken() == Token::kLBRACE); |
12702 const intptr_t literal_pos = TokenPos(); | 12769 const intptr_t literal_pos = TokenPos(); |
12703 ConsumeToken(); | |
12704 | 12770 |
12771 if (is_const) { | |
12772 Instance& existing_const = Instance::ZoneHandle(Z); | |
12773 if (GetCachedConstant(literal_pos, &existing_const)) { | |
12774 SkipMapLiteral(); | |
12775 return new(Z) LiteralNode(literal_pos, existing_const); | |
12776 } | |
12777 } | |
12778 | |
12779 ConsumeToken(); // Opening brace. | |
12705 AbstractType& key_type = Type::ZoneHandle(Z, Type::DynamicType()); | 12780 AbstractType& key_type = Type::ZoneHandle(Z, Type::DynamicType()); |
12706 AbstractType& value_type = Type::ZoneHandle(Z, Type::DynamicType()); | 12781 AbstractType& value_type = Type::ZoneHandle(Z, Type::DynamicType()); |
12707 TypeArguments& map_type_arguments = | 12782 TypeArguments& map_type_arguments = |
12708 TypeArguments::ZoneHandle(Z, type_arguments.raw()); | 12783 TypeArguments::ZoneHandle(Z, type_arguments.raw()); |
12709 // If no type argument vector is provided, leave it as null, which is | 12784 // If no type argument vector is provided, leave it as null, which is |
12710 // equivalent to using dynamic as the type argument for the both key and value | 12785 // equivalent to using dynamic as the type argument for the both key and value |
12711 // types. | 12786 // types. |
12712 if (!map_type_arguments.IsNull()) { | 12787 if (!map_type_arguments.IsNull()) { |
12713 ASSERT(map_type_arguments.Length() > 0); | 12788 ASSERT(map_type_arguments.Length() > 0); |
12714 // Map literals take two type arguments. | 12789 // Map literals take two type arguments. |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
12851 EvaluateConstConstructorCall(immutable_map_class, | 12926 EvaluateConstConstructorCall(immutable_map_class, |
12852 map_type_arguments, | 12927 map_type_arguments, |
12853 map_constr, | 12928 map_constr, |
12854 constr_args)); | 12929 constr_args)); |
12855 if (constructor_result.IsUnhandledException()) { | 12930 if (constructor_result.IsUnhandledException()) { |
12856 ReportErrors(Error::Cast(constructor_result), | 12931 ReportErrors(Error::Cast(constructor_result), |
12857 script_, literal_pos, | 12932 script_, literal_pos, |
12858 "error executing const Map constructor"); | 12933 "error executing const Map constructor"); |
12859 } else { | 12934 } else { |
12860 const Instance& const_instance = Instance::Cast(constructor_result); | 12935 const Instance& const_instance = Instance::Cast(constructor_result); |
12936 CacheConstantValue(literal_pos, const_instance); | |
Florian Schneider
2015/08/26 08:58:09
Are cached constants ever GCed? What if the code t
hausner
2015/08/26 17:46:14
No, they are not GCed. But we hold on to these con
| |
12861 return new(Z) LiteralNode( | 12937 return new(Z) LiteralNode( |
12862 literal_pos, Instance::ZoneHandle(Z, const_instance.raw())); | 12938 literal_pos, Instance::ZoneHandle(Z, const_instance.raw())); |
12863 } | 12939 } |
12864 } else { | 12940 } else { |
12865 // Factory call at runtime. | 12941 // Factory call at runtime. |
12866 const Class& factory_class = | 12942 const Class& factory_class = |
12867 Class::Handle(Z, Library::LookupCoreClass(Symbols::Map())); | 12943 Class::Handle(Z, Library::LookupCoreClass(Symbols::Map())); |
12868 ASSERT(!factory_class.IsNull()); | 12944 ASSERT(!factory_class.IsNull()); |
12869 const Function& factory_method = Function::ZoneHandle(Z, | 12945 const Function& factory_method = Function::ZoneHandle(Z, |
12870 factory_class.LookupFactory( | 12946 factory_class.LookupFactory( |
(...skipping 523 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
13394 // Make the constructor call. | 13470 // Make the constructor call. |
13395 AstNode* new_object = NULL; | 13471 AstNode* new_object = NULL; |
13396 if (is_const) { | 13472 if (is_const) { |
13397 if (!constructor.is_const()) { | 13473 if (!constructor.is_const()) { |
13398 const String& external_constructor_name = | 13474 const String& external_constructor_name = |
13399 (named_constructor ? constructor_name : type_class_name); | 13475 (named_constructor ? constructor_name : type_class_name); |
13400 ReportError("non-const constructor '%s' cannot be used in " | 13476 ReportError("non-const constructor '%s' cannot be used in " |
13401 "const object creation", | 13477 "const object creation", |
13402 external_constructor_name.ToCString()); | 13478 external_constructor_name.ToCString()); |
13403 } | 13479 } |
13404 const Object& constructor_result = Object::Handle(Z, | 13480 |
13405 EvaluateConstConstructorCall(type_class, | 13481 Instance& const_instance = Instance::ZoneHandle(Z); |
13406 type_arguments, | 13482 if (GetCachedConstant(new_pos, &const_instance)) { |
13407 constructor, | 13483 // Cache hit, nothing else to do. |
13408 arguments)); | |
13409 if (constructor_result.IsUnhandledException()) { | |
13410 // It's a compile-time error if invocation of a const constructor | |
13411 // call fails. | |
13412 ReportErrors(Error::Cast(constructor_result), | |
13413 script_, new_pos, | |
13414 "error while evaluating const constructor"); | |
13415 } else { | 13484 } else { |
13416 // Const constructors can return null in the case where a const native | 13485 Object& constructor_result = Object::Handle(Z, |
13417 // factory returns a null value. Thus we cannot use a Instance::Cast here. | 13486 EvaluateConstConstructorCall(type_class, |
13418 Instance& const_instance = Instance::Handle(Z); | 13487 type_arguments, |
13488 constructor, | |
13489 arguments)); | |
13490 if (constructor_result.IsUnhandledException()) { | |
13491 // It's a compile-time error if invocation of a const constructor | |
13492 // call fails. | |
13493 ReportErrors(Error::Cast(constructor_result), | |
13494 script_, new_pos, | |
13495 "error while evaluating const constructor"); | |
13496 } | |
13419 const_instance ^= constructor_result.raw(); | 13497 const_instance ^= constructor_result.raw(); |
13420 new_object = new(Z) LiteralNode( | 13498 CacheConstantValue(new_pos, const_instance); |
13421 new_pos, Instance::ZoneHandle(Z, const_instance.raw())); | 13499 } |
13422 if (!type_bound.IsNull()) { | 13500 new_object = new(Z) LiteralNode(new_pos, const_instance); |
13423 ASSERT(!type_bound.IsMalformed()); | 13501 if (!type_bound.IsNull()) { |
13424 Error& malformed_error = Error::Handle(Z); | 13502 ASSERT(!type_bound.IsMalformed()); |
13425 ASSERT(!is_top_level_); // We cannot check unresolved types. | 13503 Error& malformed_error = Error::Handle(Z); |
13426 if (!const_instance.IsInstanceOf(type_bound, | 13504 ASSERT(!is_top_level_); // We cannot check unresolved types. |
13427 TypeArguments::Handle(Z), | 13505 if (!const_instance.IsInstanceOf(type_bound, |
13428 &malformed_error)) { | 13506 TypeArguments::Handle(Z), |
13429 type_bound = ClassFinalizer::NewFinalizedMalformedType( | 13507 &malformed_error)) { |
13430 malformed_error, | 13508 type_bound = ClassFinalizer::NewFinalizedMalformedType( |
13431 script_, | 13509 malformed_error, |
13432 new_pos, | 13510 script_, |
13433 "const factory result is not an instance of '%s'", | 13511 new_pos, |
13434 String::Handle(Z, type_bound.UserVisibleName()).ToCString()); | 13512 "const factory result is not an instance of '%s'", |
13435 new_object = ThrowTypeError(new_pos, type_bound); | 13513 String::Handle(Z, type_bound.UserVisibleName()).ToCString()); |
13436 } | 13514 new_object = ThrowTypeError(new_pos, type_bound); |
13437 type_bound = AbstractType::null(); | |
13438 } | 13515 } |
13516 type_bound = AbstractType::null(); | |
13439 } | 13517 } |
13440 } else { | 13518 } else { |
13441 CheckConstructorCallTypeArguments(new_pos, constructor, type_arguments); | 13519 CheckConstructorCallTypeArguments(new_pos, constructor, type_arguments); |
13442 if (!type_arguments.IsNull() && | 13520 if (!type_arguments.IsNull() && |
13443 !type_arguments.IsInstantiated() && | 13521 !type_arguments.IsInstantiated() && |
13444 (current_block_->scope->function_level() > 0)) { | 13522 (current_block_->scope->function_level() > 0)) { |
13445 // Make sure that the instantiator is captured. | 13523 // Make sure that the instantiator is captured. |
13446 CaptureInstantiator(); | 13524 CaptureInstantiator(); |
13447 } | 13525 } |
13448 // If the type argument vector is not instantiated, we verify in checked | 13526 // If the type argument vector is not instantiated, we verify in checked |
(...skipping 738 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
14187 void Parser::SkipQualIdent() { | 14265 void Parser::SkipQualIdent() { |
14188 ASSERT(IsIdentifier()); | 14266 ASSERT(IsIdentifier()); |
14189 ConsumeToken(); | 14267 ConsumeToken(); |
14190 if (CurrentToken() == Token::kPERIOD) { | 14268 if (CurrentToken() == Token::kPERIOD) { |
14191 ConsumeToken(); // Consume the kPERIOD token. | 14269 ConsumeToken(); // Consume the kPERIOD token. |
14192 ExpectIdentifier("identifier expected after '.'"); | 14270 ExpectIdentifier("identifier expected after '.'"); |
14193 } | 14271 } |
14194 } | 14272 } |
14195 | 14273 |
14196 } // namespace dart | 14274 } // namespace dart |
OLD | NEW |