Index: runtime/vm/parser.cc |
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc |
index a966b40a639cb4b29aff9cba5fcb7ee7b033ca80..7f2e645ece4671b4a6847e8036a22f12a7dc3aeb 100644 |
--- a/runtime/vm/parser.cc |
+++ b/runtime/vm/parser.cc |
@@ -838,7 +838,6 @@ void Parser::ParseFunction(ParsedFunction* parsed_function) { |
switch (func.kind()) { |
case RawFunction::kClosureFunction: |
if (func.IsImplicitClosureFunction()) { |
- parser.SkipFunctionPreamble(); |
node_sequence = |
parser.ParseImplicitClosure(func, &default_parameter_values); |
break; |
@@ -1279,26 +1278,35 @@ SequenceNode* Parser::ParseInstanceSetter(const Function& func) { |
SequenceNode* Parser::ParseImplicitClosure(const Function& func, |
Array* default_values) { |
TRACE_PARSER("ParseImplicitClosure"); |
- |
intptr_t token_pos = func.token_pos(); |
OpenFunctionBlock(func); |
ParamList params; |
- |
params.AddFinalParameter( |
token_pos, |
&Symbols::ClosureParameter(), |
&Type::ZoneHandle(Type::DynamicType())); |
- const bool allow_explicit_default_values = true; |
- ParseFormalParameterList(allow_explicit_default_values, false, ¶ms); |
- SetupDefaultsForOptionalParams(¶ms, default_values); |
- |
- // Getters can't be closurized. If supported, they need special |
- // handling of the parameters as in ParseFunc. |
const Function& parent = Function::ZoneHandle(func.parent_function()); |
- ASSERT(!parent.IsGetterFunction()); |
+ if (parent.IsImplicitSetterFunction()) { |
+ const intptr_t ident_pos = func.token_pos(); |
+ ASSERT(IsIdentifier()); |
+ const String& field_name = *CurrentLiteral(); |
+ const Class& field_class = Class::ZoneHandle(Z, parent.Owner()); |
+ const Field& field = |
+ Field::ZoneHandle(Z, field_class.LookupInstanceField(field_name)); |
+ const AbstractType& field_type = AbstractType::ZoneHandle(Z, field.type()); |
+ params.AddFinalParameter(ident_pos, |
+ &Symbols::Value(), |
+ &field_type); |
+ ASSERT(func.num_fixed_parameters() == 2); // closure, value. |
+ } else if (!parent.IsGetterFunction() && !parent.IsImplicitGetterFunction()) { |
+ const bool allow_explicit_default_values = true; |
+ SkipFunctionPreamble(); |
+ ParseFormalParameterList(allow_explicit_default_values, false, ¶ms); |
+ SetupDefaultsForOptionalParams(¶ms, default_values); |
+ } |
// Populate function scope with the formal parameters. |
LocalScope* scope = current_block_->scope; |
@@ -3423,8 +3431,9 @@ void Parser::SkipInitializers() { |
RawLibraryPrefix* Parser::ParsePrefix() { |
ASSERT(IsIdentifier()); |
// A library prefix can never stand by itself. It must be followed by |
- // a period. |
- if (LookaheadToken(1) != Token::kPERIOD) { |
+ // a period or a hash mark (for closurization). |
+ Token::Kind next_token = LookaheadToken(1); |
+ if ((next_token != Token::kPERIOD) && (next_token != Token::kHASH)) { |
return LibraryPrefix::null(); |
} |
const String& ident = *CurrentLiteral(); |
@@ -3451,10 +3460,8 @@ RawLibraryPrefix* Parser::ParsePrefix() { |
return LibraryPrefix::null(); |
} |
- // We have a name that is not shadowed, followed by a period. |
- // Consume the identifier and the period. |
- ConsumeToken(); |
- ASSERT(CurrentToken() == Token::kPERIOD); // We checked above. |
+ // We have a name that is not shadowed, followed by a period or #. |
+ // Consume the identifier, let the caller consume the . or #. |
ConsumeToken(); |
return prefix.raw(); |
} |
@@ -11294,13 +11301,151 @@ AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) { |
} |
+// Closurization e#m of getter, setter, method or operator. |
+AstNode* Parser::ParseClosurization(AstNode* primary) { |
+ ExpectToken(Token::kHASH); |
+ intptr_t property_pos = TokenPos(); |
+ bool is_setter_name = false; |
+ |
+ String& extractor_name = String::ZoneHandle(Z); |
+ if (IsIdentifier()) { |
+ extractor_name = CurrentLiteral()->raw(); |
+ ConsumeToken(); |
+ if (CurrentToken() == Token::kASSIGN) { |
+ ConsumeToken(); |
+ is_setter_name = true; |
+ } |
+ } else if (Token::CanBeOverloaded(CurrentToken())) { |
+ extractor_name = String::New(Token::Str(CurrentToken())); |
+ ConsumeToken(); |
+ } else { |
+ ReportError("identifier or operator expected"); |
+ } |
+ |
+ if (primary->IsPrimaryNode() && primary->AsPrimaryNode()->IsSuper()) { |
+ // TODO(hausner): implement super#m |
+ ReportError("closurization of super method not yet supported"); |
+ } |
+ |
+ // Handle closurization of top-level names from library prefixes, P#m |
+ if (primary->IsLiteralNode() && |
+ primary->AsLiteralNode()->literal().IsLibraryPrefix()) { |
+ const LibraryPrefix& prefix = |
+ LibraryPrefix::Cast(primary->AsLiteralNode()->literal()); |
+ Object& obj = Object::Handle(Z); |
+ const bool is_private_name = |
+ (extractor_name.CharAt(0) == Library::kPrivateIdentifierStart); |
+ if (!is_private_name) { |
+ // Private names are not exported by libraries. The name mangling |
+ // of private names with a library-specific suffix usually ensures |
+ // that _x in library A is not found when looked up from library B. |
+ // In the pathological case where a library imports itself with |
+ // a prefix, the name mangling does not help in hiding the private |
+ // name, so we explicitly prevent lookup of private names here. |
+ obj = prefix.LookupObject(extractor_name); |
+ } |
+ if (!prefix.is_loaded() && (parsed_function() != NULL)) { |
+ // Remember that this function depends on an import prefix of an |
+ // unloaded deferred library. |
+ parsed_function()->AddDeferredPrefix(prefix); |
+ } |
+ |
+ if (obj.IsFunction()) { |
+ const Function& func = Function::Cast(obj); |
+ if (!func.IsSetterFunction() || is_setter_name) { |
+ return CreateImplicitClosureNode(func, property_pos, NULL); |
+ } |
+ } else if (obj.IsField()) { |
+ const Field& field = Field::Cast(obj); |
+ if (is_setter_name && !field.is_final()) { |
+ Instance& setter_closure = Instance::ZoneHandle(field.SetterClosure()); |
Ivan Posva
2015/07/17 08:35:42
These closures are not cached anywhere, which mean
hausner
2015/07/17 17:53:51
They get collected if they are not stored anywhere
|
+ return new(Z) LiteralNode(property_pos, setter_closure); |
+ } |
+ if (!is_setter_name) { |
+ Instance& getter_closure = Instance::ZoneHandle(field.GetterClosure()); |
+ return new(Z) LiteralNode(property_pos, getter_closure); |
+ } |
+ } |
+ return ThrowNoSuchMethodError(property_pos, |
+ current_class(), |
+ extractor_name, |
+ NULL, // No arguments. |
+ InvocationMirror::kTopLevel, |
+ is_setter_name |
+ ? InvocationMirror::kSetter |
+ : InvocationMirror::kMethod, |
+ NULL); // No existing function. |
+ } |
+ |
+ // Handle closurization of static properties of classes, C#n. |
+ if (primary->IsPrimaryNode() && |
+ primary->AsPrimaryNode()->primary().IsClass()) { |
+ const Class& cls = Class::Cast(primary->AsPrimaryNode()->primary()); |
+ const Field& field = |
+ Field::Handle(Z, cls.LookupStaticField(extractor_name)); |
+ if (!field.IsNull()) { |
+ if (is_setter_name) { |
+ extractor_name = Field::SetterName(extractor_name); |
+ if (!field.is_final()) { |
+ const Instance& setter_closure = |
+ Instance::ZoneHandle(Z, field.SetterClosure()); |
+ return new(Z) LiteralNode(property_pos, setter_closure); |
Florian Schneider
2015/07/17 09:42:02
My suggestion would be to do sth. like
return Cre
hausner
2015/07/17 17:53:51
Yes, I was considering this. I am hoping that we c
|
+ } |
+ } |
+ if (!is_setter_name) { |
+ const Instance& getter_closure = |
+ Instance::ZoneHandle(Z, field.GetterClosure()); |
+ return new(Z) LiteralNode(property_pos, getter_closure); |
+ } |
+ } else { |
+ Function& func = Function::Handle(Z); |
+ if (is_setter_name) { |
+ extractor_name = Field::SetterName(extractor_name); |
+ func = cls.LookupStaticFunction(extractor_name); |
+ } else { |
+ func = cls.LookupStaticFunction(extractor_name); |
+ if (func.IsNull()) { |
+ const String& getter_name = |
+ String::Handle(Z, Field::GetterName(extractor_name)); |
+ func = cls.LookupStaticFunction(getter_name); |
+ } |
+ } |
+ if (!func.IsNull()) { |
+ return CreateImplicitClosureNode(func, property_pos, NULL); |
+ } |
+ } |
+ return ThrowNoSuchMethodError(property_pos, |
+ cls, |
+ extractor_name, |
+ NULL, // No arguments. |
+ InvocationMirror::kStatic, |
+ is_setter_name |
+ ? InvocationMirror::kSetter |
+ : InvocationMirror::kMethod, |
+ NULL); // No existing function. |
+ } |
+ |
+ // Closurization of instance getter, setter, method or operator. |
+ if (is_setter_name) { |
+ extractor_name = String::Concat(Symbols::SetterPrefix(), extractor_name); |
+ } |
+ extractor_name = String::Concat(Symbols::HashMark(), extractor_name); |
+ extractor_name = Symbols::New(extractor_name); |
+ return new(Z) InstanceGetterNode(property_pos, primary, extractor_name); |
+} |
+ |
+ |
AstNode* Parser::ParsePostfixExpr() { |
TRACE_PARSER("ParsePostfixExpr"); |
String* expr_ident = |
Token::IsIdentifier(CurrentToken()) ? CurrentLiteral() : NULL; |
const intptr_t expr_pos = TokenPos(); |
AstNode* expr = ParsePrimary(); |
- expr = ParseSelectors(expr, false); |
+ if (CurrentToken() == Token::kHASH) { |
+ expr = ParseClosurization(expr); |
+ } else { |
+ expr = ParseSelectors(expr, false); |
+ } |
if (IsIncrementOperator(CurrentToken())) { |
TRACE_PARSER("IncrementOperator"); |
if (!IsLegalAssignableSyntax(expr, TokenPos())) { |
@@ -11973,6 +12118,9 @@ RawAbstractType* Parser::ParseType( |
SkipQualIdent(); |
} else { |
prefix = ParsePrefix(); |
+ if (!prefix.IsNull()) { |
+ ExpectToken(Token::kPERIOD); |
+ } |
type_name = CurrentLiteral()->raw(); |
ConsumeToken(); |
@@ -12597,12 +12745,16 @@ AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { |
intptr_t type_pos = TokenPos(); |
// Can't allocate const objects of a deferred type. |
const bool allow_deferred_type = !is_const; |
- const bool consume_unresolved_prefix = (LookaheadToken(3) == Token::kLT) || |
- (LookaheadToken(3) == Token::kPERIOD); |
+ const Token::Kind la3 = LookaheadToken(3); |
+ const bool consume_unresolved_prefix = |
+ (la3 == Token::kLT) || (la3 == Token::kPERIOD) || (la3 == Token::kHASH); |
AbstractType& type = AbstractType::Handle(Z, |
ParseType(ClassFinalizer::kCanonicalizeWellFormed, |
allow_deferred_type, |
consume_unresolved_prefix)); |
+ if (CurrentToken() == Token::kHASH) { |
+ ReportError("constructor closurization not yet supported"); |
+ } |
// In case the type is malformed, throw a dynamic type error after finishing |
// parsing the instance creation expression. |
if (!type.IsMalformed() && (type.IsTypeParameter() || type.IsDynamicType())) { |
@@ -13021,6 +13173,14 @@ AstNode* Parser::ParsePrimary() { |
} else if (IsIdentifier()) { |
intptr_t qual_ident_pos = TokenPos(); |
const LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(Z, ParsePrefix()); |
+ if (!prefix.IsNull()) { |
+ if (CurrentToken() == Token::kHASH) { |
+ // Closurization of top-level entity in prefix scope. |
+ return new(Z) LiteralNode(qual_ident_pos, prefix); |
+ } else { |
+ ExpectToken(Token::kPERIOD); |
+ } |
+ } |
String& ident = *CurrentLiteral(); |
ConsumeToken(); |
if (prefix.IsNull()) { |