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(¶ms, current_block_->scope); |
+ // Now populate function scope with the formal parameters. |
+ AddFormalParamsToScope(¶ms, 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(¶ms, 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(¶ms, 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'", |