Index: runtime/vm/parser.cc |
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc |
index 00acf39c96d6ce8d5624d88b86aac4525e91ef81..4b11bb65fc7b33a1b99ed5118cfa8e1c1b2e8a39 100644 |
--- a/runtime/vm/parser.cc |
+++ b/runtime/vm/parser.cc |
@@ -831,6 +831,38 @@ RawObject* Parser::ParseFunctionParameters(const Function& func) { |
} |
+bool Parser::ParseFormalParameters(const Function& func, ParamList* params) { |
+ ASSERT(!func.IsNull()); |
+ // This is currently only used for constructors. To handle all kinds |
+ // of functions, special cases for getters and posibly other kinds |
regis
2015/08/03 21:51:35
possibly
hausner
2015/08/03 22:52:16
Done.
|
+ // need to be added. |
+ ASSERT(func.kind() == RawFunction::kConstructor); |
+ ASSERT(!func.IsRedirectingFactory()); |
+ // Implicit constructors have no source, no user-defined formal parameters. |
+ if (func.IsImplicitConstructor()) { |
+ return true; |
+ } |
+ LongJumpScope jump; |
+ if (setjmp(*jump.Set()) == 0) { |
+ const Script& script = Script::Handle(func.script()); |
srdjan
2015/08/03 23:27:30
Handle(isolate(), ...)
hausner
2015/08/03 23:52:21
This is in a static method, so isolate() is not av
|
+ const Class& owner = Class::Handle(func.Owner()); |
+ ASSERT(!owner.IsNull()); |
+ ParsedFunction* parsed_function = |
+ new ParsedFunction(Thread::Current(), Function::ZoneHandle(func.raw())); |
+ Parser parser(script, parsed_function, func.token_pos()); |
+ parser.SkipFunctionPreamble(); |
+ parser.ParseFormalParameterList(true, true, params); |
+ return true; |
+ } else { |
+ Thread::Current()->isolate()->object_store()->clear_sticky_error(); |
srdjan
2015/08/03 23:27:30
Parser has isolate_ field:
isolate()->object_store
hausner
2015/08/03 23:52:21
same here.
|
+ params->Clear(); |
+ return false; |
+ } |
+ UNREACHABLE(); |
+ return false; |
+} |
+ |
+ |
void Parser::ParseFunction(ParsedFunction* parsed_function) { |
Isolate* isolate = parsed_function->isolate(); |
Zone* zone = parsed_function->zone(); |
@@ -850,6 +882,11 @@ void Parser::ParseFunction(ParsedFunction* parsed_function) { |
parser.ParseImplicitClosure(func, &default_parameter_values); |
break; |
} |
+ if (func.IsConstructorClosureFunction()) { |
+ node_sequence = |
+ parser.ParseConstructorClosure(func, &default_parameter_values); |
+ break; |
+ } |
// Fall-through: Handle non-implicit closures. |
case RawFunction::kRegularFunction: |
case RawFunction::kGetterFunction: |
@@ -1282,6 +1319,58 @@ SequenceNode* Parser::ParseInstanceSetter(const Function& func) { |
} |
+SequenceNode* Parser::ParseConstructorClosure(const Function& func, |
+ Array* default_values) { |
+ TRACE_PARSER("ParseConstructorClosure"); |
+ const intptr_t token_pos = func.token_pos(); |
+ |
+ Function& constructor = Function::ZoneHandle(Z); |
+ TypeArguments& type_args = TypeArguments::ZoneHandle(Z); |
+ ParseConstructorClosurization(&constructor, &type_args); |
+ ASSERT(!constructor.IsNull()); |
+ |
+ ParamList params; |
+ // The first parameter of the closure function is the implicit closure |
+ // argument. |
+ params.AddFinalParameter(token_pos, |
+ &Symbols::ClosureParameter(), |
+ &Type::ZoneHandle(Z, Type::DynamicType())); |
+ bool params_ok = ParseFormalParameters(constructor, ¶ms); |
+ if (!params_ok) { |
+ UNREACHABLE(); |
regis
2015/08/03 21:51:35
Is this leftover debug code?
hausner
2015/08/03 22:52:16
It's a case that should never happen, because the
|
+ } |
+ SetupDefaultsForOptionalParams(¶ms, default_values); |
+ ASSERT(func.num_fixed_parameters() == params.num_fixed_parameters); |
+ ASSERT(func.NumOptionalParameters() == params.num_optional_parameters); |
+ |
+ OpenFunctionBlock(func); |
+ LocalScope* scope = current_block_->scope; |
+ AddFormalParamsToScope(¶ms, scope); |
+ |
+ ArgumentListNode* ctor_args = new ArgumentListNode(token_pos); |
+ // Skip implicit closure parameter at 0. |
+ for (intptr_t i = 1; i < func.NumParameters(); i++) { |
+ ctor_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i))); |
+ } |
+ |
+ if (func.HasOptionalNamedParameters()) { |
+ const Array& arg_names = |
+ Array::ZoneHandle(Array::New(func.NumOptionalParameters())); |
+ for (intptr_t i = 0; i < arg_names.Length(); i++) { |
+ intptr_t index = func.num_fixed_parameters() + i; |
+ arg_names.SetAt(i, String::Handle(func.ParameterNameAt(index))); |
+ } |
+ ctor_args->set_names(arg_names); |
+ } |
+ |
+ AstNode* new_object = |
+ CreateConstructorCallNode(token_pos, type_args, constructor, ctor_args); |
+ ReturnNode* return_node = new ReturnNode(token_pos, new_object); |
+ current_block_->statements->Add(return_node); |
+ return CloseBlock(); |
+} |
+ |
+ |
SequenceNode* Parser::ParseImplicitClosure(const Function& func, |
Array* default_values) { |
TRACE_PARSER("ParseImplicitClosure"); |
@@ -7482,10 +7571,10 @@ AstNode* Parser::ParseFunctionStatement(bool is_literal) { |
// Since the signature type is cached by the signature class, it may have |
// been finalized already. |
- Type& signature_type = Type::Handle(Z, |
- signature_class.SignatureType()); |
- TypeArguments& signature_type_arguments = TypeArguments::Handle(Z, |
- signature_type.arguments()); |
+ Type& signature_type = |
+ Type::Handle(Z, signature_class.SignatureType()); |
+ TypeArguments& signature_type_arguments = |
+ TypeArguments::Handle(Z, signature_type.arguments()); |
if (!signature_type.IsFinalized()) { |
signature_type ^= ClassFinalizer::FinalizeType( |
@@ -12759,6 +12848,44 @@ AstNode* Parser::ParseSymbolLiteral() { |
} |
+RawFunction* Parser::BuildConstructorClosureFunction(const Function& ctr, |
+ intptr_t token_pos) { |
+ ASSERT(ctr.kind() == RawFunction::kConstructor); |
+ String& closure_name = String::Handle(Z, ctr.name()); |
+ closure_name = Symbols::FromConcat(Symbols::ConstructorClosurePrefix(), |
+ closure_name); |
+ ParamList params; |
+ params.AddFinalParameter(token_pos, |
+ &Symbols::ClosureParameter(), |
+ &Type::ZoneHandle(Z, Type::DynamicType())); |
+ |
+ ParseFormalParameters(ctr, ¶ms); |
+ Function& closure = Function::Handle(Z); |
+ closure = Function::NewClosureFunction(closure_name, |
+ innermost_function(), |
+ token_pos); |
+ closure.set_is_generated_body(true); |
+ closure.set_result_type(AbstractType::Handle(Type::DynamicType())); |
+ AddFormalParamsToFunction(¶ms, closure); |
+ |
+ // Create and set the signature class of the closure. |
+ const String& sig = String::Handle(Z, closure.Signature()); |
+ Class& sig_cls = Class::Handle(Z, library_.LookupLocalClass(sig)); |
+ if (sig_cls.IsNull()) { |
+ sig_cls = Class::NewSignatureClass(sig, closure, script_, token_pos); |
+ library_.AddClass(sig_cls); |
+ } |
+ closure.set_signature_class(sig_cls); |
+ const Type& sig_type = Type::Handle(Z, sig_cls.SignatureType()); |
+ if (!sig_type.IsFinalized()) { |
regis
2015/08/03 21:51:35
Better make sure we are not top level parsing, or
hausner
2015/08/03 22:52:16
Done.
|
+ ClassFinalizer::FinalizeType(sig_cls, |
+ sig_type, |
+ ClassFinalizer::kCanonicalize); |
+ } |
+ return closure.raw(); |
+} |
+ |
+ |
static String& BuildConstructorName(const String& type_class_name, |
const String* named_constructor) { |
// By convention, the static function implementing a named constructor 'C' |
@@ -12774,6 +12901,61 @@ static String& BuildConstructorName(const String& type_class_name, |
} |
+// Parse a primary expression of the form new T# or new T#m. |
+// Current token position is after the keyword new. Extracts the |
+// anonymous or named constructor and type arguments. |
+// Note that type type T has already been parsed before |
+// (by ParseNewOperator()) and is guaranteed to be well-formed, |
+// and the constructor is known to exist. |
+void Parser::ParseConstructorClosurization(Function* constructor, |
+ TypeArguments* type_arguments) { |
+ *constructor = Function::null(); |
+ *type_arguments = TypeArguments::null(); |
+ const Token::Kind la3 = LookaheadToken(3); |
+ const bool consume_unresolved_prefix = |
+ (la3 == Token::kLT) || (la3 == Token::kPERIOD) || (la3 == Token::kHASH); |
+ LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(Z); |
+ AbstractType& type = AbstractType::Handle(Z, |
+ ParseType(ClassFinalizer::kCanonicalizeWellFormed, |
+ true, // allow deferred type |
+ consume_unresolved_prefix, |
+ &prefix)); |
+ // A constructor tear-off closure can only have been created for a |
+ // type that is loaded. |
+ ASSERT(prefix.IsNull() || prefix.is_loaded()); |
+ ASSERT(!type.IsMalformed() && !type.IsTypeParameter()); |
+ ExpectToken(Token::kHASH); |
+ String* named_constructor = NULL; |
+ if (IsIdentifier()) { |
+ named_constructor = CurrentLiteral(); |
+ ConsumeToken(); |
+ } |
+ // Resolve the type and optional identifier to a constructor or factory. |
+ Class& type_class = Class::Handle(Z, type.type_class()); |
+ String& type_class_name = String::Handle(Z, type_class.Name()); |
+ *type_arguments = type.arguments(); |
+ String& constructor_name = |
+ BuildConstructorName(type_class_name, named_constructor); |
+ *constructor = type_class.LookupConstructor(constructor_name); |
+ if (constructor->IsNull()) { |
+ *constructor = type_class.LookupFactory(constructor_name); |
+ ASSERT(!constructor->IsNull()); |
+ if (constructor->IsRedirectingFactory()) { |
+ ClassFinalizer::ResolveRedirectingFactory(type_class, *constructor); |
+ type = constructor->RedirectionType(); |
+ ASSERT(!type.IsMalformedOrMalbounded()); |
+ if (!type.IsInstantiated()) { |
+ Error& error = Error::Handle(Z); |
+ type ^= type.InstantiateFrom(*type_arguments, &error); |
+ ASSERT(error.IsNull()); |
+ } |
+ *type_arguments = type.arguments(); |
+ *constructor = constructor->RedirectionTarget(); |
+ } |
+ } |
+} |
+ |
+ |
AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { |
TRACE_PARSER("ParseNewOperator"); |
const intptr_t new_pos = TokenPos(); |
@@ -12795,9 +12977,7 @@ AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { |
allow_deferred_type, |
consume_unresolved_prefix, |
&prefix)); |
- if (CurrentToken() == Token::kHASH) { |
- ReportError("constructor closurization not yet supported"); |
- } |
+ |
if (FLAG_load_deferred_eagerly && |
!prefix.IsNull() && prefix.is_deferred_load() && !prefix.is_loaded()) { |
// Add runtime check. |
@@ -12835,20 +13015,37 @@ AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { |
String::Handle(Z, type_class.Name()).ToCString()); |
} |
- // The grammar allows for an optional ('.' identifier)? after the type, which |
- // is a named constructor. Note that we tell ParseType() above not to |
- // consume it as part of a misinterpreted qualified identifier. Only a |
- // valid library prefix is accepted as qualifier. |
+ // The type can be followed by an optional named constructor identifier. |
+ // Note that we tell ParseType() above not to consume it as part of |
+ // a misinterpreted qualified identifier. Only a valid library |
+ // prefix is accepted as qualifier. |
String* named_constructor = NULL; |
- if (CurrentToken() == Token::kPERIOD) { |
+ const bool is_tearoff_expression = (CurrentToken() == Token::kHASH); |
+ if (is_tearoff_expression) { |
+ if (is_const) { |
+ ReportError("tear-off closure not allowed with const allocation"); |
+ } |
+ ConsumeToken(); |
+ if (IsIdentifier()) { |
+ named_constructor = ExpectIdentifier("name of constructor expected"); |
+ } |
+ } else if (CurrentToken() == Token::kPERIOD) { |
ConsumeToken(); |
named_constructor = ExpectIdentifier("name of constructor expected"); |
} |
// Parse constructor parameters. |
- CheckToken(Token::kLPAREN); |
intptr_t call_pos = TokenPos(); |
- ArgumentListNode* arguments = ParseActualParameters(NULL, is_const); |
+ ArgumentListNode* arguments = NULL; |
+ if (!is_tearoff_expression) { |
+ CheckToken(Token::kLPAREN); |
+ call_pos = TokenPos(); |
+ arguments = ParseActualParameters(NULL, is_const); |
+ } else { |
+ // Allocate dummy node with no arguments so we don't have to deal |
+ // with the NULL corner case below. |
+ arguments = new(Z) ArgumentListNode(TokenPos()); |
+ } |
// Parsing is complete, so we can return a throw in case of a malformed or |
// malbounded type or report a compile-time error if the constructor is const. |
@@ -12966,10 +13163,10 @@ AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { |
arguments_length -= 1; |
} |
} |
+ ASSERT(!constructor.IsNull()); |
// It is ok to call a factory method of an abstract class, but it is |
// a dynamic error to instantiate an abstract class. |
- ASSERT(!constructor.IsNull()); |
if (type_class.is_abstract() && !constructor.IsFactory()) { |
// Evaluate arguments before throwing. |
LetNode* result = new(Z) LetNode(call_pos); |
@@ -12987,6 +13184,32 @@ AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { |
error_arguments)); |
return result; |
} |
+ |
+ type_arguments ^= type_arguments.Canonicalize(); |
+ |
+ if (is_tearoff_expression) { |
+ const Function& tearoff_func = Function::ZoneHandle(Z, |
+ BuildConstructorClosureFunction(constructor, new_pos)); |
+ |
+ // Local functions normally get parsed when the enclosing function is |
+ // compiled. Since constructor tearoff closures don't get parsed here, |
+ // we need to duplicate some of the side effects of parsing, namely |
+ // creating a function scope, and capturing the instantiator of the |
+ // enclosing function if necessary. |
+ OpenFunctionBlock(tearoff_func); |
+ // If there are type arguments in the tearoff expression that are |
+ // not yet instantiated, capture the instantiator. |
+ if (IsInstantiatorRequired() && |
+ !type_arguments.IsNull() && !type_arguments.IsInstantiated()) { |
+ CaptureInstantiator(); |
+ } |
+ SequenceNode* tearoff_body = CloseBlock(); |
+ ClosureNode* closure_obj = |
+ new(Z) ClosureNode(new_pos, tearoff_func, NULL, tearoff_body->scope()); |
+ return closure_obj; |
+ } |
+ |
+ ASSERT(!is_tearoff_expression); |
String& error_message = String::Handle(Z); |
if (!constructor.AreValidArguments(arguments_length, |
arguments->names(), |
@@ -13018,7 +13241,7 @@ AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { |
} |
return ThrowTypeError(type_pos, type); |
} |
- type_arguments ^= type_arguments.Canonicalize(); |
+ |
// Make the constructor call. |
AstNode* new_object = NULL; |
if (is_const) { |