Chromium Code Reviews| 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 "vm/bigint_operations.h" | 7 #include "vm/bigint_operations.h" |
| 8 #include "vm/class_finalizer.h" | 8 #include "vm/class_finalizer.h" |
| 9 #include "vm/compiler.h" | 9 #include "vm/compiler.h" |
| 10 #include "vm/compiler_stats.h" | 10 #include "vm/compiler_stats.h" |
| (...skipping 670 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 681 parsed_function->set_instantiator( | 681 parsed_function->set_instantiator( |
| 682 new LoadLocalNode(node_sequence->token_index(), *receiver)); | 682 new LoadLocalNode(node_sequence->token_index(), *receiver)); |
| 683 } | 683 } |
| 684 } | 684 } |
| 685 | 685 |
| 686 parsed_function->set_default_parameter_values(default_parameter_values); | 686 parsed_function->set_default_parameter_values(default_parameter_values); |
| 687 isolate->set_ast_node_id(prev_ast_node_id); | 687 isolate->set_ast_node_id(prev_ast_node_id); |
| 688 } | 688 } |
| 689 | 689 |
| 690 | 690 |
| 691 // TODO(regis): Implement support for non-const final static fields (currently | |
| 692 // supported "final" fields are actually const fields). | |
| 693 // TODO(regis): Since a const variable is implicitly final, | |
| 694 // rename ParseStaticConstGetter to ParseStaticFinalGetter and | |
| 695 // rename kConstImplicitGetter to kImplicitFinalGetter. | |
| 691 SequenceNode* Parser::ParseStaticConstGetter(const Function& func) { | 696 SequenceNode* Parser::ParseStaticConstGetter(const Function& func) { |
| 692 TRACE_PARSER("ParseStaticConstGetter"); | 697 TRACE_PARSER("ParseStaticConstGetter"); |
| 693 ParamList params; | 698 ParamList params; |
| 694 ASSERT(func.num_fixed_parameters() == 0); // static. | 699 ASSERT(func.num_fixed_parameters() == 0); // static. |
| 695 ASSERT(func.num_optional_parameters() == 0); | 700 ASSERT(func.num_optional_parameters() == 0); |
| 696 ASSERT(AbstractType::Handle(func.result_type()).IsResolved()); | 701 ASSERT(AbstractType::Handle(func.result_type()).IsResolved()); |
| 697 | 702 |
| 698 // Build local scope for function and populate with the formal parameters. | 703 // Build local scope for function and populate with the formal parameters. |
| 699 OpenFunctionBlock(func); | 704 OpenFunctionBlock(func); |
| 700 AddFormalParamsToScope(¶ms, current_block_->scope); | 705 AddFormalParamsToScope(¶ms, current_block_->scope); |
| 701 | 706 |
| 707 const String& field_name = *ExpectIdentifier("field name expected"); | |
| 708 const Class& field_class = Class::Handle(func.owner()); | |
| 709 const Field& field = | |
| 710 Field::ZoneHandle(field_class.LookupStaticField(field_name)); | |
| 711 | |
| 702 // Static const fields must have an initializer. | 712 // Static const fields must have an initializer. |
| 703 ExpectIdentifier("field name expected"); | |
| 704 ExpectToken(Token::kASSIGN); | 713 ExpectToken(Token::kASSIGN); |
| 705 | 714 |
| 706 // We don't want to use ParseConstExpr() here because we don't want | 715 // We don't want to use ParseConstExpr() here because we don't want |
| 707 // the constant folding code to create, compile and execute a code | 716 // the constant folding code to create, compile and execute a code |
| 708 // fragment to evaluate the expression. Instead, we just make sure | 717 // fragment to evaluate the expression. Instead, we just make sure |
| 709 // the static const field initializer is a constant expression and | 718 // the static const field initializer is a constant expression and |
| 710 // leave the evaluation to the getter function. | 719 // leave the evaluation to the getter function. |
| 711 const intptr_t expr_pos = token_index_; | 720 const intptr_t expr_pos = token_index_; |
| 712 AstNode* expr = ParseExpr(kAllowConst); | 721 AstNode* expr = ParseExpr(kAllowConst); |
| 713 if (expr->EvalConstExpr() == NULL) { | 722 // TODO(regis): Implement support for const fields. |
| 714 ErrorMsg(expr_pos, "initializer must be a compile time constant"); | 723 if (field.is_final()) { // TODO(regis): Should be field.is_const(). |
|
srdjan
2012/04/11 19:52:51
You could already add is_const() tester to field,
regis
2012/04/11 21:11:24
Done.
| |
| 724 // This getter will only be called once at compile time. | |
| 725 if (expr->EvalConstExpr() == NULL) { | |
| 726 ErrorMsg(expr_pos, "initializer must be a compile time constant"); | |
| 727 } | |
| 728 ReturnNode* return_node = new ReturnNode(token_index_, expr); | |
| 729 current_block_->statements->Add(return_node); | |
| 730 } else { | |
| 731 // This getter may be called each time the static field is accessed. | |
| 732 // The following generated code lazily initializes the field: | |
| 733 // if (field.value == transition_sentinel) { | |
|
srdjan
2012/04/11 19:52:51
=== (strict equal used)
regis
2012/04/11 21:11:24
Done.
| |
| 734 // field.value = null; | |
|
srdjan
2012/04/11 19:52:51
Why set it to null? If we catch the exception, nex
regis
2012/04/11 21:11:24
Yes, I spoke earlier with Gilad about it. We shoul
| |
| 735 // throw("circular dependency in field initialization"); | |
| 736 // } | |
| 737 // if (field.value == sentinel) { | |
|
srdjan
2012/04/11 19:52:51
===
regis
2012/04/11 21:11:24
Done.
| |
| 738 // field.value = transition_sentinel; | |
| 739 // field.value = expr; | |
| 740 // } | |
| 741 // return field.value; // Type check is executed here in checked mode. | |
| 742 | |
| 743 // TODO(regis): Remove this check once we support proper const fields. | |
| 744 if (expr->EvalConstExpr() == NULL) { | |
| 745 ErrorMsg(expr_pos, "initializer must be a compile time constant"); | |
| 746 } | |
| 747 | |
| 748 // Generate code checking for circular dependency in field initialization. | |
| 749 AstNode* compare_circular = new ComparisonNode( | |
| 750 token_index_, | |
| 751 Token::kEQ_STRICT, | |
| 752 new LoadStaticFieldNode(token_index_, field), | |
| 753 new LiteralNode(token_index_, | |
| 754 Instance::ZoneHandle(Object::transition_sentinel()))); | |
| 755 SequenceNode* report_circular = new SequenceNode(token_index_, NULL); | |
| 756 report_circular->Add( | |
| 757 new StoreStaticFieldNode( | |
| 758 token_index_, | |
| 759 field, | |
| 760 new LiteralNode(token_index_, Instance::ZoneHandle()))); | |
| 761 // TODO(regis): Exception to throw is not specified by spec. | |
| 762 const String& circular_error = String::ZoneHandle( | |
| 763 String::NewSymbol("circular dependency in field initialization")); | |
| 764 report_circular->Add( | |
| 765 new ThrowNode(token_index_, | |
| 766 new LiteralNode(token_index_, circular_error), | |
| 767 NULL)); | |
| 768 AstNode* circular_check = | |
| 769 new IfNode(token_index_, compare_circular, report_circular, NULL); | |
| 770 current_block_->statements->Add(circular_check); | |
| 771 | |
| 772 // Generate code checking for uninitialized field. | |
| 773 AstNode* compare_uninitialized = new ComparisonNode( | |
| 774 token_index_, | |
| 775 Token::kEQ_STRICT, | |
| 776 new LoadStaticFieldNode(token_index_, field), | |
| 777 new LiteralNode(token_index_, | |
| 778 Instance::ZoneHandle(Object::sentinel()))); | |
| 779 SequenceNode* initialize_field = new SequenceNode(token_index_, NULL); | |
| 780 initialize_field->Add( | |
| 781 new StoreStaticFieldNode( | |
| 782 token_index_, | |
| 783 field, | |
| 784 new LiteralNode( | |
| 785 token_index_, | |
| 786 Instance::ZoneHandle(Object::transition_sentinel())))); | |
| 787 initialize_field->Add(new StoreStaticFieldNode(token_index_, field, expr)); | |
| 788 AstNode* uninitialized_check = | |
| 789 new IfNode(token_index_, compare_uninitialized, initialize_field, NULL); | |
| 790 current_block_->statements->Add(uninitialized_check); | |
| 791 | |
| 792 // Generate code returning the field value. | |
| 793 ReturnNode* return_node = | |
| 794 new ReturnNode(token_index_, | |
| 795 new LoadStaticFieldNode(token_index_, field)); | |
| 796 current_block_->statements->Add(return_node); | |
| 715 } | 797 } |
| 716 ReturnNode* return_node = new ReturnNode(token_index_, expr); | |
| 717 current_block_->statements->Add(return_node); | |
| 718 return CloseBlock(); | 798 return CloseBlock(); |
| 719 } | 799 } |
| 720 | 800 |
| 721 | 801 |
| 722 // Create AstNodes for an implicit instance getter method: | 802 // Create AstNodes for an implicit instance getter method: |
| 723 // LoadLocalNode 0 ('this'); | 803 // LoadLocalNode 0 ('this'); |
| 724 // LoadInstanceFieldNode (field_name); | 804 // LoadInstanceFieldNode (field_name); |
| 725 // ReturnNode (field's value); | 805 // ReturnNode (field's value); |
| 726 SequenceNode* Parser::ParseInstanceGetter(const Function& func) { | 806 SequenceNode* Parser::ParseInstanceGetter(const Function& func) { |
| 727 TRACE_PARSER("ParseInstanceGetter"); | 807 TRACE_PARSER("ParseInstanceGetter"); |
| (...skipping 5440 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 6168 access = load_access->MakeAssignmentNode(value); | 6248 access = load_access->MakeAssignmentNode(value); |
| 6169 } else { | 6249 } else { |
| 6170 access = CallGetter(call_pos, receiver, field_name); | 6250 access = CallGetter(call_pos, receiver, field_name); |
| 6171 } | 6251 } |
| 6172 return access; | 6252 return access; |
| 6173 } | 6253 } |
| 6174 | 6254 |
| 6175 | 6255 |
| 6176 AstNode* Parser::GenerateStaticFieldLookup(const Field& field, | 6256 AstNode* Parser::GenerateStaticFieldLookup(const Field& field, |
| 6177 intptr_t ident_pos) { | 6257 intptr_t ident_pos) { |
| 6178 // Run static field initializer first if necessary. | 6258 AstNode* initializer = RunStaticFieldInitializer(field); |
|
srdjan
2012/04/11 19:52:51
initializer -> call_getter ?
regis
2012/04/11 21:11:24
No, the returned ast is the initializer. If the fi
| |
| 6179 // May return an exception throwing ast node. | 6259 if (initializer != NULL) { |
| 6180 AstNode* throw_exception = RunStaticFieldInitializer(field); | 6260 return initializer; |
| 6181 if (throw_exception != NULL) { | |
| 6182 return throw_exception; | |
| 6183 } | 6261 } |
| 6184 // Access the field. | 6262 if (field.is_final()) { // TODO(regis): Should be field.is_const(). |
|
srdjan
2012/04/11 19:52:51
Add is_const() to Field
regis
2012/04/11 21:11:24
Done.
| |
| 6185 if (field.is_final()) { | 6263 ASSERT(field.value() != Object::sentinel()); |
| 6264 ASSERT(field.value() != Object::transition_sentinel()); | |
| 6186 return new LiteralNode(ident_pos, Instance::ZoneHandle(field.value())); | 6265 return new LiteralNode(ident_pos, Instance::ZoneHandle(field.value())); |
| 6187 } else { | |
| 6188 return new LoadStaticFieldNode(ident_pos, | |
| 6189 Field::ZoneHandle(field.raw())); | |
| 6190 } | 6266 } |
| 6267 // No initializer. Access the field directly. | |
| 6268 return new LoadStaticFieldNode(ident_pos, Field::ZoneHandle(field.raw())); | |
| 6191 } | 6269 } |
| 6192 | 6270 |
| 6193 | 6271 |
| 6194 AstNode* Parser::ParseStaticFieldAccess(const Class& cls, | 6272 AstNode* Parser::ParseStaticFieldAccess(const Class& cls, |
| 6195 const String& field_name, | 6273 const String& field_name, |
| 6196 intptr_t ident_pos) { | 6274 intptr_t ident_pos) { |
| 6197 TRACE_PARSER("ParseStaticFieldAccess"); | 6275 TRACE_PARSER("ParseStaticFieldAccess"); |
| 6198 AstNode* access = NULL; | 6276 AstNode* access = NULL; |
| 6199 const intptr_t call_pos = token_index_; | 6277 const intptr_t call_pos = token_index_; |
| 6200 const Field& field = Field::ZoneHandle(cls.LookupStaticField(field_name)); | 6278 const Field& field = Field::ZoneHandle(cls.LookupStaticField(field_name)); |
| (...skipping 440 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 6641 while (outer_function.IsLocalFunction()) { | 6719 while (outer_function.IsLocalFunction()) { |
| 6642 outer_function = outer_function.parent_function(); | 6720 outer_function = outer_function.parent_function(); |
| 6643 } | 6721 } |
| 6644 if (outer_function.IsFactory() || !outer_function.is_static()) { | 6722 if (outer_function.IsFactory() || !outer_function.is_static()) { |
| 6645 return current_class().NumTypeParameters() > 0; | 6723 return current_class().NumTypeParameters() > 0; |
| 6646 } | 6724 } |
| 6647 return false; | 6725 return false; |
| 6648 } | 6726 } |
| 6649 | 6727 |
| 6650 | 6728 |
| 6651 // Returns null on success. | |
| 6652 // Returns a throw node if evaluation of the static initializer results in an | |
| 6653 // unhandled exception. | |
| 6654 AstNode* Parser::RunStaticFieldInitializer(const Field& field) { | 6729 AstNode* Parser::RunStaticFieldInitializer(const Field& field) { |
|
srdjan
2012/04/11 19:52:51
Add comment what you are returning.
regis
2012/04/11 21:11:24
Done.
| |
| 6730 // TODO(regis): Implement support for non-const final static fields. | |
| 6655 ASSERT(field.is_static()); | 6731 ASSERT(field.is_static()); |
| 6656 const Instance& value = Instance::Handle(field.value()); | 6732 const Instance& value = Instance::Handle(field.value()); |
| 6657 if (value.raw() == Object::transition_sentinel()) { | 6733 if (value.raw() == Object::transition_sentinel()) { |
| 6658 ErrorMsg("circular dependency while initializing static field '%s'", | 6734 if (field.is_final()) { // TODO(regis): Should be field.is_const(). |
| 6659 String::Handle(field.name()).ToCString()); | 6735 ErrorMsg("circular dependency while initializing static field '%s'", |
| 6660 | 6736 String::Handle(field.name()).ToCString()); |
| 6737 } else { | |
|
srdjan
2012/04/11 19:52:51
Add comment like: call static getter so that it ca
regis
2012/04/11 21:11:24
Done.
| |
| 6738 return new StaticGetterNode(token_index_, | |
| 6739 Class::ZoneHandle(field.owner()), | |
| 6740 String::ZoneHandle(field.name())); | |
| 6741 } | |
| 6661 } else if (value.raw() == Object::sentinel()) { | 6742 } else if (value.raw() == Object::sentinel()) { |
| 6662 // This field has not been referenced yet and thus the value has | 6743 // This field has not been referenced yet and thus the value has |
| 6663 // not been evaluated. Call the static getter method to evaluate | 6744 // not been evaluated. If the field is const, call the static getter method |
| 6664 // the expression and canonicalize the value. | 6745 // to evaluate the expression and canonicalize the value. |
| 6665 | 6746 if (field.is_final()) { // TODO(regis): Should be field.is_const(). |
| 6666 field.set_value(Instance::Handle(Object::transition_sentinel())); | 6747 field.set_value(Instance::Handle(Object::transition_sentinel())); |
| 6667 const String& field_name = String::Handle(field.name()); | 6748 const String& field_name = String::Handle(field.name()); |
| 6668 const String& getter_name = | 6749 const String& getter_name = |
| 6669 String::Handle(Field::GetterName(field_name)); | 6750 String::Handle(Field::GetterName(field_name)); |
| 6670 const Class& cls = Class::Handle(field.owner()); | 6751 const Class& cls = Class::Handle(field.owner()); |
| 6671 GrowableArray<const Object*> arguments; // no arguments. | 6752 GrowableArray<const Object*> arguments; // no arguments. |
| 6672 const int kNumArguments = 0; // no arguments. | 6753 const int kNumArguments = 0; // no arguments. |
| 6673 const Array& kNoArgumentNames = Array::Handle(); | 6754 const Array& kNoArgumentNames = Array::Handle(); |
| 6674 const Function& func = | 6755 const Function& func = |
| 6675 Function::Handle(Resolver::ResolveStatic(cls, | 6756 Function::Handle(Resolver::ResolveStatic(cls, |
| 6676 getter_name, | 6757 getter_name, |
| 6677 kNumArguments, | 6758 kNumArguments, |
| 6678 kNoArgumentNames, | 6759 kNoArgumentNames, |
| 6679 Resolver::kIsQualified)); | 6760 Resolver::kIsQualified)); |
| 6680 ASSERT(!func.IsNull()); | 6761 ASSERT(!func.IsNull()); |
| 6681 ASSERT(func.kind() == RawFunction::kConstImplicitGetter); | 6762 ASSERT(func.kind() == RawFunction::kConstImplicitGetter); |
| 6682 Object& const_value = Object::Handle( | 6763 Object& const_value = Object::Handle( |
| 6683 DartEntry::InvokeStatic(func, arguments, kNoArgumentNames)); | 6764 DartEntry::InvokeStatic(func, arguments, kNoArgumentNames)); |
| 6684 if (const_value.IsError()) { | 6765 if (const_value.IsError()) { |
| 6685 Error& error = Error::Handle(); | 6766 Error& error = Error::Handle(); |
| 6686 error ^= const_value.raw(); | 6767 error ^= const_value.raw(); |
| 6687 if (const_value.IsUnhandledException()) { | 6768 if (const_value.IsUnhandledException()) { |
| 6688 field.set_value(Instance::Handle()); | 6769 field.set_value(Instance::Handle()); |
| 6689 // It is a compile-time error if evaluation of a compile-time constant | 6770 // It is a compile-time error if evaluation of a compile-time constant |
| 6690 // would raise an exception. | 6771 // would raise an exception. |
| 6691 if (field.is_final()) { | |
| 6692 AppendErrorMsg(error, token_index_, | 6772 AppendErrorMsg(error, token_index_, |
| 6693 "error initializing final field '%s'", | 6773 "error initializing final field '%s'", |
| 6694 String::Handle(field.name()).ToCString()); | 6774 String::Handle(field.name()).ToCString()); |
| 6695 } else { | 6775 } else { |
| 6696 return GenerateRethrow(token_index_, const_value); | 6776 Isolate::Current()->long_jump_base()->Jump(1, error); |
| 6697 } | 6777 } |
| 6698 } else { | |
| 6699 Isolate::Current()->long_jump_base()->Jump(1, error); | |
| 6700 } | 6778 } |
| 6779 ASSERT(const_value.IsNull() || const_value.IsInstance()); | |
| 6780 Instance& instance = Instance::Handle(); | |
| 6781 instance ^= const_value.raw(); | |
| 6782 if (!instance.IsNull()) { | |
| 6783 instance ^= instance.Canonicalize(); | |
| 6784 } | |
| 6785 field.set_value(instance); | |
| 6786 } else { | |
|
srdjan
2012/04/11 19:52:51
If I understand correctly, we will now always call
regis
2012/04/11 21:11:24
As we discussed, it would be comparing apples and
| |
| 6787 return new StaticGetterNode(token_index_, | |
| 6788 Class::ZoneHandle(field.owner()), | |
| 6789 String::ZoneHandle(field.name())); | |
| 6701 } | 6790 } |
| 6702 ASSERT(const_value.IsNull() || const_value.IsInstance()); | |
| 6703 Instance& instance = Instance::Handle(); | |
| 6704 instance ^= const_value.raw(); | |
| 6705 if (!instance.IsNull()) { | |
| 6706 instance ^= instance.Canonicalize(); | |
| 6707 } | |
| 6708 field.set_value(instance); | |
| 6709 } | 6791 } |
| 6710 return NULL; | 6792 return NULL; |
| 6711 } | 6793 } |
| 6712 | 6794 |
| 6713 | 6795 |
| 6714 RawObject* Parser::EvaluateConstConstructorCall( | 6796 RawObject* Parser::EvaluateConstConstructorCall( |
| 6715 const Class& type_class, | 6797 const Class& type_class, |
| 6716 const AbstractTypeArguments& type_arguments, | 6798 const AbstractTypeArguments& type_arguments, |
| 6717 const Function& constructor, | 6799 const Function& constructor, |
| 6718 ArgumentListNode* arguments) { | 6800 ArgumentListNode* arguments) { |
| (...skipping 1538 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 8257 void Parser::SkipQualIdent() { | 8339 void Parser::SkipQualIdent() { |
| 8258 ASSERT(IsIdentifier()); | 8340 ASSERT(IsIdentifier()); |
| 8259 ConsumeToken(); | 8341 ConsumeToken(); |
| 8260 if (CurrentToken() == Token::kPERIOD) { | 8342 if (CurrentToken() == Token::kPERIOD) { |
| 8261 ConsumeToken(); // Consume the kPERIOD token. | 8343 ConsumeToken(); // Consume the kPERIOD token. |
| 8262 ExpectIdentifier("identifier expected after '.'"); | 8344 ExpectIdentifier("identifier expected after '.'"); |
| 8263 } | 8345 } |
| 8264 } | 8346 } |
| 8265 | 8347 |
| 8266 } // namespace dart | 8348 } // namespace dart |
| OLD | NEW |