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

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
« no previous file with comments | « runtime/vm/object.h ('k') | tests/language/language.status » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 671 matching lines...) Expand 10 before | Expand all | Expand 10 after
682 parsed_function->set_instantiator( 682 parsed_function->set_instantiator(
683 new LoadLocalNode(node_sequence->token_index(), *receiver)); 683 new LoadLocalNode(node_sequence->token_index(), *receiver));
684 } 684 }
685 } 685 }
686 686
687 parsed_function->set_default_parameter_values(default_parameter_values); 687 parsed_function->set_default_parameter_values(default_parameter_values);
688 isolate->set_ast_node_id(prev_ast_node_id); 688 isolate->set_ast_node_id(prev_ast_node_id);
689 } 689 }
690 690
691 691
692 // TODO(regis): Implement support for non-const final static fields (currently
693 // supported "final" fields are actually const fields).
694 // TODO(regis): Since a const variable is implicitly final,
695 // rename ParseStaticConstGetter to ParseStaticFinalGetter and
696 // rename kConstImplicitGetter to kImplicitFinalGetter.
692 SequenceNode* Parser::ParseStaticConstGetter(const Function& func) { 697 SequenceNode* Parser::ParseStaticConstGetter(const Function& func) {
693 TRACE_PARSER("ParseStaticConstGetter"); 698 TRACE_PARSER("ParseStaticConstGetter");
694 ParamList params; 699 ParamList params;
695 ASSERT(func.num_fixed_parameters() == 0); // static. 700 ASSERT(func.num_fixed_parameters() == 0); // static.
696 ASSERT(func.num_optional_parameters() == 0); 701 ASSERT(func.num_optional_parameters() == 0);
697 ASSERT(AbstractType::Handle(func.result_type()).IsResolved()); 702 ASSERT(AbstractType::Handle(func.result_type()).IsResolved());
698 703
699 // Build local scope for function and populate with the formal parameters. 704 // Build local scope for function and populate with the formal parameters.
700 OpenFunctionBlock(func); 705 OpenFunctionBlock(func);
701 AddFormalParamsToScope(&params, current_block_->scope); 706 AddFormalParamsToScope(&params, current_block_->scope);
702 707
708 const String& field_name = *ExpectIdentifier("field name expected");
709 const Class& field_class = Class::Handle(func.owner());
710 const Field& field =
711 Field::ZoneHandle(field_class.LookupStaticField(field_name));
712
703 // Static const fields must have an initializer. 713 // Static const fields must have an initializer.
704 ExpectIdentifier("field name expected");
705 ExpectToken(Token::kASSIGN); 714 ExpectToken(Token::kASSIGN);
706 715
707 // We don't want to use ParseConstExpr() here because we don't want 716 // We don't want to use ParseConstExpr() here because we don't want
708 // the constant folding code to create, compile and execute a code 717 // the constant folding code to create, compile and execute a code
709 // fragment to evaluate the expression. Instead, we just make sure 718 // fragment to evaluate the expression. Instead, we just make sure
710 // the static const field initializer is a constant expression and 719 // the static const field initializer is a constant expression and
711 // leave the evaluation to the getter function. 720 // leave the evaluation to the getter function.
712 const intptr_t expr_pos = token_index_; 721 const intptr_t expr_pos = token_index_;
713 AstNode* expr = ParseExpr(kAllowConst); 722 AstNode* expr = ParseExpr(kAllowConst);
714 if (expr->EvalConstExpr() == NULL) { 723 if (field.is_const()) {
715 ErrorMsg(expr_pos, "initializer must be a compile time constant"); 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) {
734 // field.value = null;
735 // throw("circular dependency in field initialization");
736 // }
737 // if (field.value === sentinel) {
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 // Set field to null prior to throwing exception, so that subsequent
756 // accesses to the field do not throw again, since initializers should only
757 // be executed once.
758 SequenceNode* report_circular = new SequenceNode(token_index_, NULL);
759 report_circular->Add(
760 new StoreStaticFieldNode(
761 token_index_,
762 field,
763 new LiteralNode(token_index_, Instance::ZoneHandle())));
764 // TODO(regis): Exception to throw is not specified by spec.
765 const String& circular_error = String::ZoneHandle(
766 String::NewSymbol("circular dependency in field initialization"));
767 report_circular->Add(
768 new ThrowNode(token_index_,
769 new LiteralNode(token_index_, circular_error),
770 NULL));
771 AstNode* circular_check =
772 new IfNode(token_index_, compare_circular, report_circular, NULL);
773 current_block_->statements->Add(circular_check);
774
775 // Generate code checking for uninitialized field.
776 AstNode* compare_uninitialized = new ComparisonNode(
777 token_index_,
778 Token::kEQ_STRICT,
779 new LoadStaticFieldNode(token_index_, field),
780 new LiteralNode(token_index_,
781 Instance::ZoneHandle(Object::sentinel())));
782 SequenceNode* initialize_field = new SequenceNode(token_index_, NULL);
783 initialize_field->Add(
784 new StoreStaticFieldNode(
785 token_index_,
786 field,
787 new LiteralNode(
788 token_index_,
789 Instance::ZoneHandle(Object::transition_sentinel()))));
790 initialize_field->Add(new StoreStaticFieldNode(token_index_, field, expr));
791 AstNode* uninitialized_check =
792 new IfNode(token_index_, compare_uninitialized, initialize_field, NULL);
793 current_block_->statements->Add(uninitialized_check);
794
795 // Generate code returning the field value.
796 ReturnNode* return_node =
797 new ReturnNode(token_index_,
798 new LoadStaticFieldNode(token_index_, field));
799 current_block_->statements->Add(return_node);
716 } 800 }
717 ReturnNode* return_node = new ReturnNode(token_index_, expr);
718 current_block_->statements->Add(return_node);
719 return CloseBlock(); 801 return CloseBlock();
720 } 802 }
721 803
722 804
723 // Create AstNodes for an implicit instance getter method: 805 // Create AstNodes for an implicit instance getter method:
724 // LoadLocalNode 0 ('this'); 806 // LoadLocalNode 0 ('this');
725 // LoadInstanceFieldNode (field_name); 807 // LoadInstanceFieldNode (field_name);
726 // ReturnNode (field's value); 808 // ReturnNode (field's value);
727 SequenceNode* Parser::ParseInstanceGetter(const Function& func) { 809 SequenceNode* Parser::ParseInstanceGetter(const Function& func) {
728 TRACE_PARSER("ParseInstanceGetter"); 810 TRACE_PARSER("ParseInstanceGetter");
(...skipping 5444 matching lines...) Expand 10 before | Expand all | Expand 10 after
6173 access = load_access->MakeAssignmentNode(value); 6255 access = load_access->MakeAssignmentNode(value);
6174 } else { 6256 } else {
6175 access = CallGetter(call_pos, receiver, field_name); 6257 access = CallGetter(call_pos, receiver, field_name);
6176 } 6258 }
6177 return access; 6259 return access;
6178 } 6260 }
6179 6261
6180 6262
6181 AstNode* Parser::GenerateStaticFieldLookup(const Field& field, 6263 AstNode* Parser::GenerateStaticFieldLookup(const Field& field,
6182 intptr_t ident_pos) { 6264 intptr_t ident_pos) {
6183 // Run static field initializer first if necessary. 6265 // If the static field has an initializer, initialize the field at compile
6184 // May return an exception throwing ast node. 6266 // time, which is only possible if the field is const.
6185 AstNode* throw_exception = RunStaticFieldInitializer(field); 6267 AstNode* initializing_getter = RunStaticFieldInitializer(field);
6186 if (throw_exception != NULL) { 6268 if (initializing_getter != NULL) {
6187 return throw_exception; 6269 // The field is not yet initialized and could not be initialized at compile
6270 // time. The getter will initialize the field.
6271 return initializing_getter;
6188 } 6272 }
6189 // Access the field. 6273 // The field is initialized.
6190 if (field.is_final()) { 6274 if (field.is_const()) {
6275 ASSERT(field.value() != Object::sentinel());
6276 ASSERT(field.value() != Object::transition_sentinel());
6191 return new LiteralNode(ident_pos, Instance::ZoneHandle(field.value())); 6277 return new LiteralNode(ident_pos, Instance::ZoneHandle(field.value()));
6192 } else {
6193 return new LoadStaticFieldNode(ident_pos,
6194 Field::ZoneHandle(field.raw()));
6195 } 6278 }
6279 // Access the field directly.
6280 return new LoadStaticFieldNode(ident_pos, Field::ZoneHandle(field.raw()));
6196 } 6281 }
6197 6282
6198 6283
6199 AstNode* Parser::ParseStaticFieldAccess(const Class& cls, 6284 AstNode* Parser::ParseStaticFieldAccess(const Class& cls,
6200 const String& field_name, 6285 const String& field_name,
6201 intptr_t ident_pos) { 6286 intptr_t ident_pos) {
6202 TRACE_PARSER("ParseStaticFieldAccess"); 6287 TRACE_PARSER("ParseStaticFieldAccess");
6203 AstNode* access = NULL; 6288 AstNode* access = NULL;
6204 const intptr_t call_pos = token_index_; 6289 const intptr_t call_pos = token_index_;
6205 const Field& field = Field::ZoneHandle(cls.LookupStaticField(field_name)); 6290 const Field& field = Field::ZoneHandle(cls.LookupStaticField(field_name));
(...skipping 440 matching lines...) Expand 10 before | Expand all | Expand 10 after
6646 while (outer_function.IsLocalFunction()) { 6731 while (outer_function.IsLocalFunction()) {
6647 outer_function = outer_function.parent_function(); 6732 outer_function = outer_function.parent_function();
6648 } 6733 }
6649 if (outer_function.IsFactory() || !outer_function.is_static()) { 6734 if (outer_function.IsFactory() || !outer_function.is_static()) {
6650 return current_class().NumTypeParameters() > 0; 6735 return current_class().NumTypeParameters() > 0;
6651 } 6736 }
6652 return false; 6737 return false;
6653 } 6738 }
6654 6739
6655 6740
6656 // Returns null on success. 6741 // If the field is already initialized, return no ast (NULL).
6657 // Returns a throw node if evaluation of the static initializer results in an 6742 // Otherwise, if the field is constant, initialize the field and return no ast.
6658 // unhandled exception. 6743 // If the field is not initialized and not const, return the ast for the getter.
6659 AstNode* Parser::RunStaticFieldInitializer(const Field& field) { 6744 AstNode* Parser::RunStaticFieldInitializer(const Field& field) {
6660 ASSERT(field.is_static()); 6745 ASSERT(field.is_static());
6661 const Instance& value = Instance::Handle(field.value()); 6746 const Instance& value = Instance::Handle(field.value());
6662 if (value.raw() == Object::transition_sentinel()) { 6747 if (value.raw() == Object::transition_sentinel()) {
6663 ErrorMsg("circular dependency while initializing static field '%s'", 6748 if (field.is_const()) {
6664 String::Handle(field.name()).ToCString()); 6749 ErrorMsg("circular dependency while initializing static field '%s'",
6665 6750 String::Handle(field.name()).ToCString());
6751 } else {
6752 // The implicit static getter will throw the exception if necessary.
6753 return new StaticGetterNode(token_index_,
6754 Class::ZoneHandle(field.owner()),
6755 String::ZoneHandle(field.name()));
6756 }
6666 } else if (value.raw() == Object::sentinel()) { 6757 } else if (value.raw() == Object::sentinel()) {
6667 // This field has not been referenced yet and thus the value has 6758 // This field has not been referenced yet and thus the value has
6668 // not been evaluated. Call the static getter method to evaluate 6759 // not been evaluated. If the field is const, call the static getter method
6669 // the expression and canonicalize the value. 6760 // to evaluate the expression and canonicalize the value.
6670 6761 if (field.is_const()) {
6671 field.set_value(Instance::Handle(Object::transition_sentinel())); 6762 field.set_value(Instance::Handle(Object::transition_sentinel()));
6672 const String& field_name = String::Handle(field.name()); 6763 const String& field_name = String::Handle(field.name());
6673 const String& getter_name = 6764 const String& getter_name =
6674 String::Handle(Field::GetterName(field_name)); 6765 String::Handle(Field::GetterName(field_name));
6675 const Class& cls = Class::Handle(field.owner()); 6766 const Class& cls = Class::Handle(field.owner());
6676 GrowableArray<const Object*> arguments; // no arguments. 6767 GrowableArray<const Object*> arguments; // no arguments.
6677 const int kNumArguments = 0; // no arguments. 6768 const int kNumArguments = 0; // no arguments.
6678 const Array& kNoArgumentNames = Array::Handle(); 6769 const Array& kNoArgumentNames = Array::Handle();
6679 const Function& func = 6770 const Function& func =
6680 Function::Handle(Resolver::ResolveStatic(cls, 6771 Function::Handle(Resolver::ResolveStatic(cls,
6681 getter_name, 6772 getter_name,
6682 kNumArguments, 6773 kNumArguments,
6683 kNoArgumentNames, 6774 kNoArgumentNames,
6684 Resolver::kIsQualified)); 6775 Resolver::kIsQualified));
6685 ASSERT(!func.IsNull()); 6776 ASSERT(!func.IsNull());
6686 ASSERT(func.kind() == RawFunction::kConstImplicitGetter); 6777 ASSERT(func.kind() == RawFunction::kConstImplicitGetter);
6687 Object& const_value = Object::Handle( 6778 Object& const_value = Object::Handle(
6688 DartEntry::InvokeStatic(func, arguments, kNoArgumentNames)); 6779 DartEntry::InvokeStatic(func, arguments, kNoArgumentNames));
6689 if (const_value.IsError()) { 6780 if (const_value.IsError()) {
6690 Error& error = Error::Handle(); 6781 Error& error = Error::Handle();
6691 error ^= const_value.raw(); 6782 error ^= const_value.raw();
6692 if (const_value.IsUnhandledException()) { 6783 if (const_value.IsUnhandledException()) {
6693 field.set_value(Instance::Handle()); 6784 field.set_value(Instance::Handle());
6694 // It is a compile-time error if evaluation of a compile-time constant 6785 // It is a compile-time error if evaluation of a compile-time constant
6695 // would raise an exception. 6786 // would raise an exception.
6696 if (field.is_final()) {
6697 AppendErrorMsg(error, token_index_, 6787 AppendErrorMsg(error, token_index_,
6698 "error initializing final field '%s'", 6788 "error initializing final field '%s'",
6699 String::Handle(field.name()).ToCString()); 6789 String::Handle(field.name()).ToCString());
6700 } else { 6790 } else {
6701 return GenerateRethrow(token_index_, const_value); 6791 Isolate::Current()->long_jump_base()->Jump(1, error);
6702 } 6792 }
6703 } else {
6704 Isolate::Current()->long_jump_base()->Jump(1, error);
6705 } 6793 }
6794 ASSERT(const_value.IsNull() || const_value.IsInstance());
6795 Instance& instance = Instance::Handle();
6796 instance ^= const_value.raw();
6797 if (!instance.IsNull()) {
6798 instance ^= instance.Canonicalize();
6799 }
6800 field.set_value(instance);
6801 } else {
6802 return new StaticGetterNode(token_index_,
6803 Class::ZoneHandle(field.owner()),
6804 String::ZoneHandle(field.name()));
6706 } 6805 }
6707 ASSERT(const_value.IsNull() || const_value.IsInstance());
6708 Instance& instance = Instance::Handle();
6709 instance ^= const_value.raw();
6710 if (!instance.IsNull()) {
6711 instance ^= instance.Canonicalize();
6712 }
6713 field.set_value(instance);
6714 } 6806 }
6715 return NULL; 6807 return NULL;
6716 } 6808 }
6717 6809
6718 6810
6719 RawObject* Parser::EvaluateConstConstructorCall( 6811 RawObject* Parser::EvaluateConstConstructorCall(
6720 const Class& type_class, 6812 const Class& type_class,
6721 const AbstractTypeArguments& type_arguments, 6813 const AbstractTypeArguments& type_arguments,
6722 const Function& constructor, 6814 const Function& constructor,
6723 ArgumentListNode* arguments) { 6815 ArgumentListNode* arguments) {
(...skipping 1538 matching lines...) Expand 10 before | Expand all | Expand 10 after
8262 void Parser::SkipQualIdent() { 8354 void Parser::SkipQualIdent() {
8263 ASSERT(IsIdentifier()); 8355 ASSERT(IsIdentifier());
8264 ConsumeToken(); 8356 ConsumeToken();
8265 if (CurrentToken() == Token::kPERIOD) { 8357 if (CurrentToken() == Token::kPERIOD) {
8266 ConsumeToken(); // Consume the kPERIOD token. 8358 ConsumeToken(); // Consume the kPERIOD token.
8267 ExpectIdentifier("identifier expected after '.'"); 8359 ExpectIdentifier("identifier expected after '.'");
8268 } 8360 }
8269 } 8361 }
8270 8362
8271 } // namespace dart 8363 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/object.h ('k') | tests/language/language.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698