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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: runtime/vm/parser.cc
===================================================================
--- runtime/vm/parser.cc (revision 6422)
+++ runtime/vm/parser.cc (working copy)
@@ -688,6 +688,11 @@
}
+// TODO(regis): Implement support for non-const final static fields (currently
+// supported "final" fields are actually const fields).
+// TODO(regis): Since a const variable is implicitly final,
+// rename ParseStaticConstGetter to ParseStaticFinalGetter and
+// rename kConstImplicitGetter to kImplicitFinalGetter.
SequenceNode* Parser::ParseStaticConstGetter(const Function& func) {
TRACE_PARSER("ParseStaticConstGetter");
ParamList params;
@@ -699,8 +704,12 @@
OpenFunctionBlock(func);
AddFormalParamsToScope(&params, current_block_->scope);
+ const String& field_name = *ExpectIdentifier("field name expected");
+ const Class& field_class = Class::Handle(func.owner());
+ const Field& field =
+ Field::ZoneHandle(field_class.LookupStaticField(field_name));
+
// Static const fields must have an initializer.
- ExpectIdentifier("field name expected");
ExpectToken(Token::kASSIGN);
// We don't want to use ParseConstExpr() here because we don't want
@@ -710,11 +719,82 @@
// leave the evaluation to the getter function.
const intptr_t expr_pos = token_index_;
AstNode* expr = ParseExpr(kAllowConst);
- if (expr->EvalConstExpr() == NULL) {
- ErrorMsg(expr_pos, "initializer must be a compile time constant");
+ // TODO(regis): Implement support for const fields.
+ 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.
+ // This getter will only be called once at compile time.
+ if (expr->EvalConstExpr() == NULL) {
+ ErrorMsg(expr_pos, "initializer must be a compile time constant");
+ }
+ ReturnNode* return_node = new ReturnNode(token_index_, expr);
+ current_block_->statements->Add(return_node);
+ } else {
+ // This getter may be called each time the static field is accessed.
+ // The following generated code lazily initializes the field:
+ // if (field.value == transition_sentinel) {
srdjan 2012/04/11 19:52:51 === (strict equal used)
regis 2012/04/11 21:11:24 Done.
+ // 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
+ // throw("circular dependency in field initialization");
+ // }
+ // if (field.value == sentinel) {
srdjan 2012/04/11 19:52:51 ===
regis 2012/04/11 21:11:24 Done.
+ // field.value = transition_sentinel;
+ // field.value = expr;
+ // }
+ // return field.value; // Type check is executed here in checked mode.
+
+ // TODO(regis): Remove this check once we support proper const fields.
+ if (expr->EvalConstExpr() == NULL) {
+ ErrorMsg(expr_pos, "initializer must be a compile time constant");
+ }
+
+ // Generate code checking for circular dependency in field initialization.
+ AstNode* compare_circular = new ComparisonNode(
+ token_index_,
+ Token::kEQ_STRICT,
+ new LoadStaticFieldNode(token_index_, field),
+ new LiteralNode(token_index_,
+ Instance::ZoneHandle(Object::transition_sentinel())));
+ SequenceNode* report_circular = new SequenceNode(token_index_, NULL);
+ report_circular->Add(
+ new StoreStaticFieldNode(
+ token_index_,
+ field,
+ new LiteralNode(token_index_, Instance::ZoneHandle())));
+ // TODO(regis): Exception to throw is not specified by spec.
+ const String& circular_error = String::ZoneHandle(
+ String::NewSymbol("circular dependency in field initialization"));
+ report_circular->Add(
+ new ThrowNode(token_index_,
+ new LiteralNode(token_index_, circular_error),
+ NULL));
+ AstNode* circular_check =
+ new IfNode(token_index_, compare_circular, report_circular, NULL);
+ current_block_->statements->Add(circular_check);
+
+ // Generate code checking for uninitialized field.
+ AstNode* compare_uninitialized = new ComparisonNode(
+ token_index_,
+ Token::kEQ_STRICT,
+ new LoadStaticFieldNode(token_index_, field),
+ new LiteralNode(token_index_,
+ Instance::ZoneHandle(Object::sentinel())));
+ SequenceNode* initialize_field = new SequenceNode(token_index_, NULL);
+ initialize_field->Add(
+ new StoreStaticFieldNode(
+ token_index_,
+ field,
+ new LiteralNode(
+ token_index_,
+ Instance::ZoneHandle(Object::transition_sentinel()))));
+ initialize_field->Add(new StoreStaticFieldNode(token_index_, field, expr));
+ AstNode* uninitialized_check =
+ new IfNode(token_index_, compare_uninitialized, initialize_field, NULL);
+ current_block_->statements->Add(uninitialized_check);
+
+ // Generate code returning the field value.
+ ReturnNode* return_node =
+ new ReturnNode(token_index_,
+ new LoadStaticFieldNode(token_index_, field));
+ current_block_->statements->Add(return_node);
}
- ReturnNode* return_node = new ReturnNode(token_index_, expr);
- current_block_->statements->Add(return_node);
return CloseBlock();
}
@@ -6175,19 +6255,17 @@
AstNode* Parser::GenerateStaticFieldLookup(const Field& field,
intptr_t ident_pos) {
- // Run static field initializer first if necessary.
- // May return an exception throwing ast node.
- AstNode* throw_exception = RunStaticFieldInitializer(field);
- if (throw_exception != NULL) {
- return throw_exception;
+ 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
+ if (initializer != NULL) {
+ return initializer;
}
- // Access the field.
- if (field.is_final()) {
+ 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.
+ ASSERT(field.value() != Object::sentinel());
+ ASSERT(field.value() != Object::transition_sentinel());
return new LiteralNode(ident_pos, Instance::ZoneHandle(field.value()));
- } else {
- return new LoadStaticFieldNode(ident_pos,
- Field::ZoneHandle(field.raw()));
}
+ // No initializer. Access the field directly.
+ return new LoadStaticFieldNode(ident_pos, Field::ZoneHandle(field.raw()));
}
@@ -6648,64 +6726,68 @@
}
-// Returns null on success.
-// Returns a throw node if evaluation of the static initializer results in an
-// unhandled exception.
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.
+ // TODO(regis): Implement support for non-const final static fields.
ASSERT(field.is_static());
const Instance& value = Instance::Handle(field.value());
if (value.raw() == Object::transition_sentinel()) {
- ErrorMsg("circular dependency while initializing static field '%s'",
- String::Handle(field.name()).ToCString());
-
+ if (field.is_final()) { // TODO(regis): Should be field.is_const().
+ ErrorMsg("circular dependency while initializing static field '%s'",
+ String::Handle(field.name()).ToCString());
+ } 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.
+ return new StaticGetterNode(token_index_,
+ Class::ZoneHandle(field.owner()),
+ String::ZoneHandle(field.name()));
+ }
} else if (value.raw() == Object::sentinel()) {
// This field has not been referenced yet and thus the value has
- // not been evaluated. Call the static getter method to evaluate
- // the expression and canonicalize the value.
-
- field.set_value(Instance::Handle(Object::transition_sentinel()));
- const String& field_name = String::Handle(field.name());
- const String& getter_name =
- String::Handle(Field::GetterName(field_name));
- const Class& cls = Class::Handle(field.owner());
- GrowableArray<const Object*> arguments; // no arguments.
- const int kNumArguments = 0; // no arguments.
- const Array& kNoArgumentNames = Array::Handle();
- const Function& func =
- Function::Handle(Resolver::ResolveStatic(cls,
- getter_name,
- kNumArguments,
- kNoArgumentNames,
- Resolver::kIsQualified));
- ASSERT(!func.IsNull());
- ASSERT(func.kind() == RawFunction::kConstImplicitGetter);
- Object& const_value = Object::Handle(
- DartEntry::InvokeStatic(func, arguments, kNoArgumentNames));
- if (const_value.IsError()) {
- Error& error = Error::Handle();
- error ^= const_value.raw();
- if (const_value.IsUnhandledException()) {
- field.set_value(Instance::Handle());
- // It is a compile-time error if evaluation of a compile-time constant
- // would raise an exception.
- if (field.is_final()) {
+ // not been evaluated. If the field is const, call the static getter method
+ // to evaluate the expression and canonicalize the value.
+ if (field.is_final()) { // TODO(regis): Should be field.is_const().
+ field.set_value(Instance::Handle(Object::transition_sentinel()));
+ const String& field_name = String::Handle(field.name());
+ const String& getter_name =
+ String::Handle(Field::GetterName(field_name));
+ const Class& cls = Class::Handle(field.owner());
+ GrowableArray<const Object*> arguments; // no arguments.
+ const int kNumArguments = 0; // no arguments.
+ const Array& kNoArgumentNames = Array::Handle();
+ const Function& func =
+ Function::Handle(Resolver::ResolveStatic(cls,
+ getter_name,
+ kNumArguments,
+ kNoArgumentNames,
+ Resolver::kIsQualified));
+ ASSERT(!func.IsNull());
+ ASSERT(func.kind() == RawFunction::kConstImplicitGetter);
+ Object& const_value = Object::Handle(
+ DartEntry::InvokeStatic(func, arguments, kNoArgumentNames));
+ if (const_value.IsError()) {
+ Error& error = Error::Handle();
+ error ^= const_value.raw();
+ if (const_value.IsUnhandledException()) {
+ field.set_value(Instance::Handle());
+ // It is a compile-time error if evaluation of a compile-time constant
+ // would raise an exception.
AppendErrorMsg(error, token_index_,
"error initializing final field '%s'",
String::Handle(field.name()).ToCString());
} else {
- return GenerateRethrow(token_index_, const_value);
+ Isolate::Current()->long_jump_base()->Jump(1, error);
}
- } else {
- Isolate::Current()->long_jump_base()->Jump(1, error);
}
+ ASSERT(const_value.IsNull() || const_value.IsInstance());
+ Instance& instance = Instance::Handle();
+ instance ^= const_value.raw();
+ if (!instance.IsNull()) {
+ instance ^= instance.Canonicalize();
+ }
+ field.set_value(instance);
+ } 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
+ return new StaticGetterNode(token_index_,
+ Class::ZoneHandle(field.owner()),
+ String::ZoneHandle(field.name()));
}
- ASSERT(const_value.IsNull() || const_value.IsInstance());
- Instance& instance = Instance::Handle();
- instance ^= const_value.raw();
- if (!instance.IsNull()) {
- instance ^= instance.Canonicalize();
- }
- field.set_value(instance);
}
return NULL;
}
« 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