| Index: runtime/vm/parser.cc
|
| diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
|
| index a8671215f191128cef668fb109dcac792140591c..ce6dec46202873d52a54b104ee486b62f2b08928 100644
|
| --- a/runtime/vm/parser.cc
|
| +++ b/runtime/vm/parser.cc
|
| @@ -846,7 +846,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;
|
| @@ -1287,26 +1286,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;
|
| @@ -3431,8 +3439,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();
|
| @@ -3459,10 +3468,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();
|
| }
|
| @@ -11318,13 +11325,156 @@ 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());
|
| + 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());
|
| + ASSERT(setter_closure.IsClosure());
|
| + // Note: the created closure is cached after it's created
|
| + // once. If eager compilation is desired, the compiler can
|
| + // be invoked here. The same applies for getters below.
|
| + return new(Z) LiteralNode(property_pos, setter_closure);
|
| + }
|
| + } else {
|
| + const Instance& getter_closure =
|
| + Instance::ZoneHandle(Z, field.GetterClosure());
|
| + ASSERT(getter_closure.IsClosure());
|
| + 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 = LoadFieldIfUnresolved(expr);
|
| + expr = ParseClosurization(expr);
|
| + } else {
|
| + expr = ParseSelectors(expr, false);
|
| + }
|
| if (IsIncrementOperator(CurrentToken())) {
|
| TRACE_PARSER("IncrementOperator");
|
| if (!IsLegalAssignableSyntax(expr, TokenPos())) {
|
| @@ -12006,6 +12156,9 @@ RawAbstractType* Parser::ParseType(
|
| SkipQualIdent();
|
| } else {
|
| *prefix = ParsePrefix();
|
| + if (!prefix->IsNull()) {
|
| + ExpectToken(Token::kPERIOD);
|
| + }
|
| type_name = CurrentLiteral()->raw();
|
| ConsumeToken();
|
|
|
| @@ -12631,14 +12784,19 @@ 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);
|
| +
|
| LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(Z);
|
| AbstractType& type = AbstractType::Handle(Z,
|
| ParseType(ClassFinalizer::kCanonicalizeWellFormed,
|
| 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.
|
| @@ -13073,6 +13231,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()) {
|
| @@ -13434,6 +13600,11 @@ void Parser::SkipNewOperator() {
|
| ConsumeToken(); // Skip new or const keyword.
|
| if (IsIdentifier()) {
|
| SkipType(false);
|
| + SkipIf(Token::kHASH);
|
| + if (CurrentToken() == Token::kPERIOD) {
|
| + ConsumeToken();
|
| + ExpectIdentifier("identifier expected");
|
| + }
|
| if (CurrentToken() == Token::kLPAREN) {
|
| SkipActualParameters();
|
| return;
|
| @@ -13552,6 +13723,16 @@ void Parser::SkipSelectors() {
|
|
|
| void Parser::SkipPostfixExpr() {
|
| SkipPrimary();
|
| + if (CurrentToken() == Token::kHASH) {
|
| + if (IsIdentifier()) {
|
| + ConsumeToken();
|
| + SkipIf(Token::kASSIGN);
|
| + } else if (Token::CanBeOverloaded(CurrentToken())) {
|
| + ConsumeToken();
|
| + } else {
|
| + ReportError("identifier or operator expected");
|
| + }
|
| + }
|
| SkipSelectors();
|
| if (IsIncrementOperator(CurrentToken())) {
|
| ConsumeToken();
|
|
|