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

Unified Diff: runtime/vm/parser.cc

Issue 8440014: Two-phase constructors (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: '' Created 9 years, 1 month 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
« no previous file with comments | « runtime/vm/parser.h ('k') | tests/language/language.status » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/parser.cc
===================================================================
--- runtime/vm/parser.cc (revision 1098)
+++ runtime/vm/parser.cc (working copy)
@@ -41,6 +41,7 @@
static const char* kStringClassName = "StringBase";
static const char* kInterpolateName = "_interpolate";
static const char* kThisName = "this";
+static const char* kPhaseParameterName = ":phase";
static const char* kGetIteratorName = "iterator";
#if defined(DEBUG)
@@ -1037,10 +1038,11 @@
const Function& super_function = Function::ZoneHandle(
GetSuperFunction(supercall_pos, function_name));
+ ArgumentListNode* arguments = new ArgumentListNode(supercall_pos);
// 'this' parameter is the first argument to super call.
AstNode* receiver = LoadReceiver(supercall_pos);
- ArgumentListNode* arguments =
- ParseActualParameters(receiver, kAllowConst);
+ arguments->Add(receiver);
+ ParseActualParameters(arguments, kAllowConst);
return new StaticCallNode(supercall_pos, super_function, arguments);
}
@@ -1212,7 +1214,7 @@
}
-void Parser::GenerateSuperInitializerCall(const Class& cls,
+void Parser::GenerateSuperConstructorCall(const Class& cls,
LocalVariable* receiver) {
const intptr_t supercall_pos = token_index_;
const Class& super_class = Class::Handle(cls.SuperClass());
@@ -1226,9 +1228,14 @@
String& ctor_suffix = String::Handle(String::NewSymbol("."));
ctor_name = String::Concat(ctor_name, ctor_suffix);
ArgumentListNode* arguments = new ArgumentListNode(supercall_pos);
- // implicit 'this' parameter is the only argument.
+ // Implicit 'this' parameter is the first argument.
AstNode* implicit_argument = new LoadLocalNode(supercall_pos, *receiver);
arguments->Add(implicit_argument);
+ // Implicit constructor phase parameter is second argument.
+ AstNode* phase_parameter =
+ new LiteralNode(supercall_pos,
+ Smi::ZoneHandle(Smi::New(Function::kCtorPhaseAll)));
+ arguments->Add(phase_parameter);
const Function& super_ctor = Function::ZoneHandle(
super_class.LookupConstructor(ctor_name));
if (super_ctor.IsNull() ||
@@ -1263,13 +1270,24 @@
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("parameter list expected");
}
+
+ ArgumentListNode* arguments = new ArgumentListNode(supercall_pos);
// 'this' parameter is the first argument to super class constructor.
AstNode* implicit_argument = new LoadLocalNode(supercall_pos, *receiver);
+ arguments->Add(implicit_argument);
+ // Second implicit parameter is the constructor phase. We optimistically
+ // assume that we can execute both the super initializer and the super
+ // constructor body. We may later change this to only execute the
+ // super initializer.
+ AstNode* phase_parameter =
+ new LiteralNode(supercall_pos,
+ Smi::ZoneHandle(Smi::New(Function::kCtorPhaseAll)));
+ arguments->Add(phase_parameter);
// 'this' parameter must not be accessible to the other super call arguments.
receiver->set_invisible(true);
- ArgumentListNode* arguments =
- ParseActualParameters(implicit_argument, kAllowConst);
+ ParseActualParameters(arguments, kAllowConst);
receiver->set_invisible(false);
+
// Resolve the constructor.
const Function& super_ctor = Function::ZoneHandle(
super_class.LookupConstructor(ctor_name));
@@ -1373,10 +1391,9 @@
}
-void Parser::ParseInitializers(const Class& cls) {
+void Parser::ParseInitializers(const Class& cls, LocalVariable* receiver) {
TRACE_PARSER("ParseInitializers");
- LocalVariable* receiver = current_block_->scope->VariableAt(0);
- AstNode* super_init_statement = NULL;
+ bool super_init_seen = false;
if (CurrentToken() == Token::kCOLON) {
if ((LookaheadToken(1) == Token::kTHIS) &&
((LookaheadToken(2) == Token::kLPAREN) ||
@@ -1390,33 +1407,26 @@
ParseConstructorRedirection(cls, receiver);
return;
}
-
do {
ConsumeToken(); // Colon or comma.
+ AstNode* init_statement;
if (CurrentToken() == Token::kSUPER) {
- if (super_init_statement != NULL) {
+ if (super_init_seen) {
ErrorMsg("Duplicate call to super constructor");
}
- super_init_statement = ParseSuperInitializer(cls, receiver);
+ init_statement = ParseSuperInitializer(cls, receiver);
+ super_init_seen = true;
} else {
- AstNode* init_statement = ParseInitializer(cls, receiver);
- current_block_->statements->Add(init_statement);
+ init_statement = ParseInitializer(cls, receiver);
}
+ current_block_->statements->Add(init_statement);
} while (CurrentToken() == Token::kCOMMA);
}
-
- if (super_init_statement != NULL) {
- // Move explicit supercall to the end of the initializer list to
- // avoid executing constructor code on partially initialized objects.
- // TODO(hausner): Fix issue 4995181, evaluation order of constructor
- // initializer lists.
- current_block_->statements->Add(super_init_statement);
- } else {
+ if (!super_init_seen) {
// Generate implicit super() if we haven't seen an explicit super call
// or constructor redirection.
- GenerateSuperInitializerCall(cls, receiver);
+ GenerateSuperConstructorCall(cls, receiver);
}
-
CheckConstFieldsInitialized(cls);
}
@@ -1438,10 +1448,17 @@
if (CurrentToken() != Token::kLPAREN) {
ErrorMsg("parameter list expected");
}
- // 'this' parameter is the first argument to super class constructor.
+
+ ArgumentListNode* arguments = new ArgumentListNode(call_pos);
+ // 'this' parameter is the first argument to constructor.
AstNode* implicit_argument = new LoadLocalNode(call_pos, *receiver);
- ArgumentListNode* arguments =
- ParseActualParameters(implicit_argument, kAllowConst);
+ arguments->Add(implicit_argument);
+ // Constructor phase parameter is second argument.
+ LocalVariable* phase_param = LookupPhaseParameter();
+ ASSERT(phase_param != NULL);
+ AstNode* phase_argument = new LoadLocalNode(call_pos, *phase_param);
+ arguments->Add(phase_argument);
+ ParseActualParameters(arguments, kAllowConst);
// Resolve the constructor.
const Function& redirect_ctor = Function::ZoneHandle(
@@ -1465,7 +1482,6 @@
// Implicit 'this' is the only parameter/local variable.
OpenFunctionBlock(func);
-
// Parse expressions of instance fields that have an explicit
// initializers.
GrowableArray<FieldInitExpression> initializers;
@@ -1478,6 +1494,12 @@
Type::ZoneHandle(Type::DynamicType()));
current_block_->scope->AddVariable(receiver);
+ LocalVariable* phase_parameter = new LocalVariable(
+ ctor_pos,
+ String::ZoneHandle(String::NewSymbol(kPhaseParameterName)),
+ Type::ZoneHandle(Type::DynamicType()));
+ current_block_->scope->AddVariable(phase_parameter);
+
// Now that the "this" parameter is in scope, we can generate the code
// to strore the initializer expressions in the respective instance fields.
for (int i = 0; i < initializers.length(); i++) {
@@ -1491,7 +1513,7 @@
current_block_->statements->Add(field_init);
}
- GenerateSuperInitializerCall(cls, receiver);
+ GenerateSuperConstructorCall(cls, receiver);
CheckConstFieldsInitialized(cls);
// Empty constructor body.
@@ -1531,6 +1553,11 @@
if (has_receiver) {
params.AddReceiver(token_index_);
}
+ if (func.IsConstructor()) {
+ // Add implicit parameter for constructor phase.
+ params.AddFinalParameter(token_index_, kPhaseParameterName,
+ &Type::ZoneHandle(Type::DynamicType()));
+ }
if (are_implicitly_final) {
params.SetImplicitlyFinal();
}
@@ -1551,70 +1578,95 @@
// NB: the instance field initializers have to be compiled before
// the parameters are added to the scope, so that a parameter
// name cannot shadow a name used in the field initializer expression.
-
- GrowableArray<FieldInitExpression> initializers;
+ SequenceNode* init_statements = NULL;
if (func.IsConstructor()) {
+ GrowableArray<FieldInitExpression> initializers;
ParseInitializedInstanceFields(cls, &initializers);
- }
- // Now populate function scope with the formal parameters.
- AddFormalParamsToScope(&params, current_block_->scope);
+ // Now populate function scope with the formal parameters.
+ AddFormalParamsToScope(&params, current_block_->scope);
+ LocalVariable* receiver = current_block_->scope->VariableAt(0);
- // Now that the "this" parameter is in scope, we can generate the code
- // to strore the initializer expressions in the respective instance fields.
- // We do this before the field parameters and the initializers from the
- // constuctor's initializer list get compiled.
- if (initializers.length() > 0) {
- LocalVariable* receiver = current_block_->scope->VariableAt(0);
- for (int i = 0; i < initializers.length(); i++) {
- const Field* field = initializers[i].inst_field;
- AstNode* instance = new LoadLocalNode(field->token_index(), *receiver);
- AstNode* field_init =
- new StoreInstanceFieldNode(field->token_index(),
- instance,
- *field,
- initializers[i].expr);
- current_block_->statements->Add(field_init);
+ // Now that the "this" parameter is in scope, we can generate the code
+ // to strore the initializer expressions in the respective instance fields.
+ // We do this before the field parameters and the initializers from the
+ // constructor's initializer list get compiled.
+ OpenBlock();
+ if (initializers.length() > 0) {
+ for (int i = 0; i < initializers.length(); i++) {
+ const Field* field = initializers[i].inst_field;
+ AstNode* instance = new LoadLocalNode(field->token_index(), *receiver);
+ AstNode* field_init =
+ new StoreInstanceFieldNode(field->token_index(),
+ instance,
+ *field,
+ initializers[i].expr);
+ current_block_->statements->Add(field_init);
+ }
}
- }
- // Turn formal field parameters into field initializers or report error
- // if the function is not a constructor
- if (params.has_field_initializer) {
- LocalVariable* receiver = current_block_->scope->VariableAt(0);
- for (int i = 0; i < params.parameters->length(); i++) {
- ParamDesc& param = (*params.parameters)[i];
- if (param.is_field_initializer) {
- if (!func.IsConstructor()) {
+ // Turn formal field parameters into field initializers or report error
+ // if the function is not a constructor
+ if (params.has_field_initializer) {
+ for (int i = 0; i < params.parameters->length(); i++) {
+ ParamDesc& param = (*params.parameters)[i];
+ if (param.is_field_initializer) {
+ if (!func.IsConstructor()) {
+ ErrorMsg(param.name_pos,
+ "field initializer only allowed in constructors");
+ }
+
+ const String& field_name = *param.name;
+ Field& field = Field::ZoneHandle(cls.LookupInstanceField(field_name));
+ if (field.IsNull()) {
+ ErrorMsg(param.name_pos,
+ "unresolved reference to instance field '%s'",
+ field_name.ToCString());
+ }
+ const String& mangled_name =
+ String::ZoneHandle(MangledInitParamName(field_name));
+ AstNode* instance = new LoadLocalNode(param.name_pos, *receiver);
+ LocalVariable* p =
+ current_block_->scope->LookupVariable(mangled_name, false);
+ ASSERT(p != NULL);
+ AstNode* value = new LoadLocalNode(param.name_pos, *p);
+ AstNode* initializer = new StoreInstanceFieldNode(
+ param.name_pos, instance, field, value);
+ current_block_->statements->Add(initializer);
+ }
+ }
+ }
+ ParseInitializers(cls, receiver);
+ init_statements = CloseBlock();
+ LocalVariable* phase_param = LookupPhaseParameter();
+ AstNode* phase_value = new LoadLocalNode(token_index_, *phase_param);
+ AstNode* phase_check =
+ new BinaryOpNode(token_index_, Token::kBIT_AND,
+ phase_value,
+ new LiteralNode(token_index_,
+ Smi::ZoneHandle(Smi::New(Function::kCtorPhaseInit))));
+ AstNode* comparison =
+ new ComparisonNode(token_index_, Token::kNE_STRICT,
+ phase_check,
+ new LiteralNode(token_index_, Smi::ZoneHandle(Smi::New(0))));
+ AstNode* guarded_init_statements =
+ new IfNode(token_index_, comparison, init_statements, NULL);
+ current_block_->statements->Add(guarded_init_statements);
+ } else {
+ // Parsing a function that is not a constructor.
+ if (params.has_field_initializer) {
+ for (int i = 0; i < params.parameters->length(); i++) {
+ ParamDesc& param = (*params.parameters)[i];
+ if (param.is_field_initializer) {
ErrorMsg(param.name_pos,
"field initializer only allowed in constructors");
}
-
- const String& field_name = *param.name;
- Field& field = Field::ZoneHandle(cls.LookupInstanceField(field_name));
- if (field.IsNull()) {
- ErrorMsg(param.name_pos,
- "unresolved reference to instance field '%s'",
- field_name.ToCString());
- }
- const String& mangled_name =
- String::ZoneHandle(MangledInitParamName(field_name));
- AstNode* instance = new LoadLocalNode(param.name_pos, *receiver);
- LocalVariable* p =
- current_block_->scope->LocalLookupVariable(mangled_name);
- ASSERT(p != NULL);
- AstNode* value = new LoadLocalNode(param.name_pos, *p);
- AstNode* initializer =
- new StoreInstanceFieldNode(param.name_pos, instance, field, value);
- current_block_->statements->Add(initializer);
}
}
+ // Populate function scope with the formal parameters.
+ AddFormalParamsToScope(&params, current_block_->scope);
}
- if (func.IsConstructor()) {
- ParseInitializers(cls);
- }
-
if (FLAG_enable_type_checks &&
(current_block_->scope->function_level() > 0)) {
// We are parsing, but not compiling, a local function.
@@ -1630,6 +1682,87 @@
}
}
+ if (func.IsConstructor()) {
+ LocalVariable* receiver = current_block_->scope->VariableAt(0);
+ StaticCallNode* super_call = NULL;
+ ASSERT(init_statements != NULL);
+ // Look for the super initializer call in the sequence of initializer
+ // statements. If it exists and is not the last initializer statement,
+ // we need to create an implicit super call to the super constructor's
+ // body.
+ // Thus, iterate over all but the last initializer to see whether
+ // it's a super constructor call.
+ for (int i = 0; i < init_statements->length() - 1; i++) {
+ if (init_statements->NodeAt(i)->IsStaticCallNode()) {
+ StaticCallNode* static_call =
+ init_statements->NodeAt(i)->AsStaticCallNode();
+ if (static_call->function().IsConstructor()) {
+ super_call = static_call;
+ break;
+ }
+ }
+ }
+ if (super_call != NULL) {
+ // Generate an implicit call to the super constructor's body.
+ // We need to patch the super _initializer_ call so that it
+ // saves the evaluated actual arguments in temporary variables.
+ // The temporary variables are necessary so that the argument
+ // expressions are not evaluated twice.
+ ArgumentListNode* ctor_args = super_call->arguments();
+ // The super initializer call has at least 2 arguments: the
+ // implicit receiver, and the hidden constructor phase.
+ ASSERT(ctor_args->length() >= 2);
+ for (int i = 2; i < ctor_args->length(); i++) {
+ AstNode* arg = ctor_args->NodeAt(i);
+ if (!arg->IsLoadLocalNode() && !arg->IsLiteralNode()) {
+ LocalVariable* temp =
+ CreateTempConstVariable(arg->token_index(), arg->id(), "sca");
+ AstNode* save_temp =
+ new StoreLocalNode(arg->token_index(), *temp, arg);
+ ctor_args->SetNodeAt(i, save_temp);
+ }
+ }
+ }
+ OpenBlock();
+ if (super_call != NULL) {
+ ArgumentListNode* initializer_args = super_call->arguments();
+ const Function& super_ctor = super_call->function();
+ // Patch the initializer call so it only executes the super
+ // initializer.
+ initializer_args->SetNodeAt(1,
+ new LiteralNode(token_index_,
+ Smi::ZoneHandle(Smi::New(Function::kCtorPhaseInit))));
+
+ ArgumentListNode* super_call_args = new ArgumentListNode(token_index_);
+ // First argument is the receiver.
+ super_call_args->Add(new LoadLocalNode(token_index_, *receiver));
+ // Second argument is the constructor phase argument.
+ AstNode* phase_parameter =
+ new LiteralNode(token_index_,
+ Smi::ZoneHandle(Smi::New(Function::kCtorPhaseBody)));
+ super_call_args->Add(phase_parameter);
+ for (int i = 2; i < initializer_args->length(); i++) {
+ AstNode* arg = initializer_args->NodeAt(i);
+ if (arg->IsLiteralNode()) {
+ LiteralNode* lit = arg->AsLiteralNode();
+ super_call_args->Add(new LiteralNode(token_index_, lit->literal()));
+ } else {
+ ASSERT(arg->IsLoadLocalNode() || arg->IsStoreLocalNode());
+ if (arg->IsLoadLocalNode()) {
+ const LocalVariable& temp = arg->AsLoadLocalNode()->local();
+ super_call_args->Add(new LoadLocalNode(token_index_, temp));
+ } else if (arg->IsStoreLocalNode()) {
+ const LocalVariable& temp = arg->AsStoreLocalNode()->local();
+ super_call_args->Add(new LoadLocalNode(token_index_, temp));
+ }
+ }
+ }
+ ASSERT(super_ctor.AreValidArguments(super_call_args->length(),
+ super_call_args->names()));
+ current_block_->statements->Add(
+ new StaticCallNode(token_index_, super_ctor, super_call_args));
+ }
+ }
if (CurrentToken() == Token::kLBRACE) {
ConsumeToken();
ParseStatementSequence();
@@ -1649,6 +1782,23 @@
} else {
UnexpectedToken();
}
+ if (func.IsConstructor()) {
+ SequenceNode* ctor_block = CloseBlock();
+ LocalVariable* phase_param = LookupPhaseParameter();
+ AstNode* phase_value = new LoadLocalNode(token_index_, *phase_param);
+ AstNode* phase_check =
+ new BinaryOpNode(token_index_, Token::kBIT_AND,
+ phase_value,
+ new LiteralNode(token_index_,
+ Smi::ZoneHandle(Smi::New(Function::kCtorPhaseBody))));
+ AstNode* comparison =
+ new ComparisonNode(token_index_, Token::kNE_STRICT,
+ phase_check,
+ new LiteralNode(token_index_, Smi::ZoneHandle(Smi::New(0))));
+ AstNode* guarded_block_statements =
+ new IfNode(token_index_, comparison, ctor_block, NULL);
+ current_block_->statements->Add(guarded_block_statements);
+ }
SequenceNode* statements = CloseBlock();
return statements;
@@ -1796,6 +1946,11 @@
if (has_this_param) {
method->params.AddReceiver(formal_param_pos);
}
+ // Constructors have an implicit parameter for the constructor phase.
+ if (method->IsConstructor()) {
+ method->params.AddFinalParameter(token_index_, kPhaseParameterName,
+ &Type::ZoneHandle(Type::DynamicType()));
+ }
if (are_implicitly_final) {
method->params.SetImplicitlyFinal();
}
@@ -2307,8 +2462,8 @@
void Parser::CheckConstructors(ClassDesc* class_desc) {
// Add an implicit constructor if no explicit constructor is present.
if (!class_desc->has_constructor()) {
- // The implicit constructor is unnamed, has no parameter, and contains
- // a supercall in the initializer list.
+ // The implicit constructor is unnamed, has no explicit parameter,
+ // and contains a supercall in the initializer list.
String& ctor_name = String::ZoneHandle(
String::Concat(class_desc->class_name(),
String::Handle(String::NewSymbol("."))));
@@ -2320,7 +2475,12 @@
/* is_const = */ false,
class_desc->token_pos()));
ParamList params;
+ // Add implicit 'this' parameter.
params.AddReceiver(token_index_);
+ // Add implicit parameter for constructor phase.
+ params.AddFinalParameter(token_index_, kPhaseParameterName,
+ &Type::ZoneHandle(Type::DynamicType()));
+
AddFormalParamsToFunction(&params, ctor);
// TODO(regis): What are the type arguments?
Type& result_type = Type::ZoneHandle(
@@ -3200,6 +3360,14 @@
}
+LocalVariable* Parser::LookupPhaseParameter() {
+ const String& phase_name =
+ String::Handle(String::NewSymbol(kPhaseParameterName));
+ const bool kTestOnly = false;
+ return current_block_->scope->LookupVariable(phase_name, kTestOnly);
+}
+
+
void Parser::CaptureReceiver() {
ASSERT(current_block_->scope->function_level() > 0);
const bool kTestOnly = false;
@@ -5200,14 +5368,17 @@
}
-ArgumentListNode* Parser::ParseActualParameters(AstNode* implicit_argument,
- bool require_const) {
+ArgumentListNode* Parser::ParseActualParameters(
+ ArgumentListNode* implicit_arguments,
+ bool require_const) {
TRACE_PARSER("ParseActualParameters");
ASSERT(CurrentToken() == Token::kLPAREN);
const bool saved_mode = SetAllowFunctionLiterals(true);
- ArgumentListNode* arguments = new ArgumentListNode(token_index_);
- if (implicit_argument != NULL) {
- arguments->Add(implicit_argument);
+ ArgumentListNode* arguments;
+ if (implicit_arguments == NULL) {
+ arguments = new ArgumentListNode(token_index_);
+ } else {
+ arguments = implicit_arguments;
}
GrowableArray<const String*> names;
bool named_argument_seen = false;
@@ -5807,7 +5978,8 @@
const TypeArguments& type_arguments,
const Function& constructor,
ArgumentListNode* arguments) {
- GrowableArray<const Object*> arg_values(arguments->length() + 1);
+ // +2 for implicit receiver and constructor phase arguments.
+ GrowableArray<const Object*> arg_values(arguments->length() + 2);
Instance& instance = Instance::Handle();
if (!constructor.IsFactory()) {
instance = Instance::New(type_class);
@@ -5819,6 +5991,7 @@
instance.SetTypeArguments(type_arguments);
}
arg_values.Add(&instance);
+ arg_values.Add(&Smi::ZoneHandle(Smi::New(Function::kCtorPhaseAll)));
} else {
// Prepend type_arguments to list of arguments to factory.
ASSERT(type_arguments.IsZoneHandle());
@@ -6483,7 +6656,8 @@
// A constructor has an implicit 'this' parameter (instance to construct)
// and a factory has an implicit 'this' parameter (type_arguments).
- intptr_t arguments_length = arguments->length() + 1;
+ // A constructor has a second implicit 'phase' parameter.
+ intptr_t arguments_length = arguments->length() + 2;
if (type_class.is_interface()) {
// We need to make sure that an appropriate constructor is
@@ -6548,6 +6722,8 @@
type_class.LookupConstructor(constructor_name));
if (constructor.IsNull()) {
constructor = type_class.LookupFactory(constructor_name);
+ // A factory does not have the implicit 'phase' parameter.
+ arguments_length -= 1;
}
if (constructor.IsNull()) {
ErrorMsg(new_pos, "class '%s' has no constructor or factory named '%s'",
« no previous file with comments | « runtime/vm/parser.h ('k') | tests/language/language.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698