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

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

Issue 10050022: Fix lazy initialization of static fields (issue 2472). (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 8 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 "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
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(&params, current_block_->scope); 705 AddFormalParamsToScope(&params, 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
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
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
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
OLDNEW
« runtime/vm/ast.cc ('K') | « runtime/vm/ast.cc ('k') | tests/language/language.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698