From 21ca62f8bc0cb56e64d0944967bc70baeb6ff09d Mon Sep 17 00:00:00 2001 From: "hausner@google.com" Date: Tue, 21 Apr 2015 17:36:34 +0000 Subject: [PATCH] Properly resolve top-level setters Fixes issue 4386. When converting a getter to a setter (converting a right-hand side expression to a left-hand side, assignable entity), we need to know where to look for the setter function. Top-level setters need to be resolved in the library or prefix scope in which the getter was found. R=regis@google.com Review URL: https://codereview.chromium.org//1090373006 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@45316 260f80e4-7a28-3924-810f-c04153c831b5 --- runtime/vm/ast.cc | 102 ++++++++++++++++-- runtime/vm/ast.h | 47 +++++++- runtime/vm/ast_transformer.cc | 21 ++-- runtime/vm/flow_graph_builder.cc | 5 +- runtime/vm/object.cc | 20 +++- runtime/vm/parser.cc | 79 ++++++++------ runtime/vm/parser.h | 11 +- tests/language/getter_setter_in_lib.dart | 2 + tests/language/getter_setter_in_lib2.dart | 7 ++ tests/language/getter_setter_in_lib3.dart | 11 ++ tests/language/getter_setter_in_lib_test.dart | 10 ++ tests/language/language.status | 1 + tests/language/language_analyzer.status | 1 + tests/language/language_analyzer2.status | 1 + tests/language/language_dart2js.status | 1 + 15 files changed, 251 insertions(+), 68 deletions(-) create mode 100644 tests/language/getter_setter_in_lib2.dart create mode 100644 tests/language/getter_setter_in_lib3.dart diff --git a/runtime/vm/ast.cc b/runtime/vm/ast.cc index 6e780883b5f..b66ad444ec9 100644 --- a/runtime/vm/ast.cc +++ b/runtime/vm/ast.cc @@ -561,34 +561,116 @@ AstNode* LoadIndexedNode::MakeAssignmentNode(AstNode* rhs) { AstNode* StaticGetterNode::MakeAssignmentNode(AstNode* rhs) { if (is_super_getter()) { ASSERT(receiver() != NULL); - // If the static setter is not found in the superclass, noSuchMethod will be - // called at runtime. + const String& setter_name = + String::ZoneHandle(Field::SetterSymbol(field_name_)); + Function& setter = Function::ZoneHandle( + Resolver::ResolveDynamicAnyArgs(cls(), setter_name)); + if (setter.IsNull() || setter.is_abstract()) { + // No instance setter found in super class chain, + // noSuchMethod will be called at runtime. + return new StaticSetterNode(token_pos(), + receiver(), + cls(), + field_name_, + rhs); + } return new StaticSetterNode(token_pos(), receiver(), - cls(), - field_name(), + field_name_, + setter, rhs); } + if (owner().IsLibraryPrefix()) { + const LibraryPrefix& prefix = LibraryPrefix::Cast(owner_); + // The parser has already dealt with the pathological case where a + // library imports itself. See Parser::ResolveIdentInPrefixScope() + ASSERT(field_name_.CharAt(0) != Library::kPrivateIdentifierStart); + + // If the prefix is not yet loaded, the getter doesn't exist. Return a + // setter that will throw a NSME at runtime. + if (!prefix.is_loaded()) { + return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs); + } + + Object& obj = Object::Handle(prefix.LookupObject(field_name_)); + if (obj.IsField()) { + const Field& field = Field::ZoneHandle(Field::Cast(obj).raw()); + if (!field.is_final()) { + if (Isolate::Current()->TypeChecksEnabled()) { + rhs = new AssignableNode(field.token_pos(), + rhs, + AbstractType::ZoneHandle(field.type()), + field_name_); + } + return new StoreStaticFieldNode(token_pos(), field, rhs); + } + } + + // No field found in prefix. Look for a setter function. + const String& setter_name = String::Handle(Field::SetterName(field_name_)); + obj = prefix.LookupObject(setter_name); + if (obj.IsFunction()) { + const Function& setter = Function::ZoneHandle(Function::Cast(obj).raw()); + ASSERT(setter.is_static() && setter.IsSetterFunction()); + return new StaticSetterNode( + token_pos(), NULL, field_name_, setter, rhs); + } + + // No writeable field and no setter found in the prefix. Return a + // non-existing setter that will throw an NSM error. + return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs); + } + + if (owner().IsLibrary()) { + const Library& library = Library::Cast(owner()); + Object& obj = Object::Handle(library.ResolveName(field_name_)); + if (obj.IsField()) { + const Field& field = Field::ZoneHandle(Field::Cast(obj).raw()); + if (!field.is_final()) { + if (Isolate::Current()->TypeChecksEnabled()) { + rhs = new AssignableNode(field.token_pos(), + rhs, + AbstractType::ZoneHandle(field.type()), + field_name_); + } + return new StoreStaticFieldNode(token_pos(), field, rhs); + } + } + + // No field found in library. Look for a setter function. + const String& setter_name = String::Handle(Field::SetterName(field_name_)); + obj = library.ResolveName(setter_name); + if (obj.IsFunction()) { + const Function& setter = Function::ZoneHandle(Function::Cast(obj).raw()); + ASSERT(setter.is_static() && setter.IsSetterFunction()); + return new StaticSetterNode(token_pos(), NULL, field_name_, setter, rhs); + } + + // No writeable field and no setter found in the library. Return a + // non-existing setter that will throw an NSM error. + return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs); + } + const Function& setter = - Function::Handle(cls().LookupSetterFunction(field_name())); + Function::ZoneHandle(cls().LookupSetterFunction(field_name_)); if (!setter.IsNull() && setter.IsStaticFunction()) { - return new StaticSetterNode(token_pos(), NULL, cls(), field_name(), rhs); + return new StaticSetterNode(token_pos(), NULL, field_name_, setter, rhs); } // Could not find a static setter. Look for a field. // Access to a lazily initialized static field that has not yet been // initialized is compiled to a static implicit getter. // A setter may not exist for such a field. - const Field& field = Field::ZoneHandle(cls().LookupStaticField(field_name())); + const Field& field = Field::ZoneHandle(cls().LookupStaticField(field_name_)); if (!field.IsNull()) { if (field.is_final()) { // Attempting to assign to a final variable will cause a NoSuchMethodError // to be thrown. Change static getter to non-existent static setter in // order to trigger the throw at runtime. - return new StaticSetterNode(token_pos(), NULL, cls(), field_name(), rhs); + return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs); } #if defined(DEBUG) - const String& getter_name = String::Handle(Field::GetterName(field_name())); + const String& getter_name = String::Handle(Field::GetterName(field_name_)); const Function& getter = Function::Handle(cls().LookupStaticFunction(getter_name)); ASSERT(!getter.IsNull() && @@ -605,7 +687,7 @@ AstNode* StaticGetterNode::MakeAssignmentNode(AstNode* rhs) { } // Didn't find a static setter or a static field. Make a call to // the non-existent setter to trigger a NoSuchMethodError at runtime. - return new StaticSetterNode(token_pos(), NULL, cls(), field_name(), rhs); + return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs); } diff --git a/runtime/vm/ast.h b/runtime/vm/ast.h index 19833f08a4d..6da90a562bb 100644 --- a/runtime/vm/ast.h +++ b/runtime/vm/ast.h @@ -1286,6 +1286,7 @@ class LoadStaticFieldNode : public AstNode { const Field& field() const { return field_; } void set_is_deferred(bool value) { is_deferred_reference_ = value; } + bool is_deferred_reference() const { return is_deferred_reference_; } virtual void VisitChildren(AstNodeVisitor* visitor) const { } @@ -1550,6 +1551,7 @@ class StaticGetterNode : public AstNode { const String& field_name) : AstNode(token_pos), receiver_(receiver), + owner_(Object::ZoneHandle(cls.raw())), cls_(cls), field_name_(field_name), is_deferred_reference_(false) { @@ -1561,6 +1563,17 @@ class StaticGetterNode : public AstNode { // The receiver is required for a super getter (an instance method that // is resolved at compile time rather than at runtime). AstNode* receiver() const { return receiver_; } + + // The getter node needs to remmeber how the getter was referenced + // so that it can resolve a corresponding setter if necessary. + // The owner of this getter is either a class, the top-level library scope, + // or a prefix (if the getter has been referenced throug a library prefix). + void set_owner(const Object& value) { + ASSERT(value.IsLibraryPrefix() || value.IsLibrary() || value.IsClass()); + owner_ = value.raw(); + } + const Object& owner() const { return owner_; } + const Class& cls() const { return cls_; } const String& field_name() const { return field_name_; } bool is_super_getter() const { return receiver_ != NULL; } @@ -1577,6 +1590,7 @@ class StaticGetterNode : public AstNode { private: AstNode* receiver_; + Object& owner_; const Class& cls_; const String& field_name_; bool is_deferred_reference_; @@ -1587,16 +1601,36 @@ class StaticGetterNode : public AstNode { class StaticSetterNode : public AstNode { public: + // Static setter with resolved setter function. + StaticSetterNode(intptr_t token_pos, + AstNode* receiver, + const String& field_name, + const Function& function, + AstNode* value) + : AstNode(token_pos), + receiver_(receiver), + cls_(Class::ZoneHandle(function.Owner())), + field_name_(field_name), + function_(function), + value_(value) { + ASSERT(function_.IsZoneHandle()); + ASSERT(function.is_static() || receiver != NULL); + ASSERT(field_name_.IsZoneHandle()); + ASSERT(value_ != NULL); + } + + // For unresolved setters. StaticSetterNode(intptr_t token_pos, AstNode* receiver, const Class& cls, const String& field_name, AstNode* value) - : AstNode(token_pos), - receiver_(receiver), - cls_(cls), - field_name_(field_name), - value_(value) { + : AstNode(token_pos), + receiver_(receiver), + cls_(cls), + field_name_(field_name), + function_(Function::ZoneHandle()), + value_(value) { ASSERT(cls_.IsZoneHandle()); ASSERT(field_name_.IsZoneHandle()); ASSERT(value_ != NULL); @@ -1607,6 +1641,8 @@ class StaticSetterNode : public AstNode { AstNode* receiver() const { return receiver_; } const Class& cls() const { return cls_; } const String& field_name() const { return field_name_; } + // function() returns null for unresolved setters. + const Function& function() const { return function_; } AstNode* value() const { return value_; } virtual void VisitChildren(AstNodeVisitor* visitor) const { @@ -1619,6 +1655,7 @@ class StaticSetterNode : public AstNode { AstNode* receiver_; const Class& cls_; const String& field_name_; + const Function& function_; AstNode* value_; DISALLOW_IMPLICIT_CONSTRUCTORS(StaticSetterNode); diff --git a/runtime/vm/ast_transformer.cc b/runtime/vm/ast_transformer.cc index e10879acc24..b6d20c16403 100644 --- a/runtime/vm/ast_transformer.cc +++ b/runtime/vm/ast_transformer.cc @@ -499,6 +499,7 @@ void AwaitTransformer::VisitStaticGetterNode(StaticGetterNode* node) { new_receiver, node->cls(), node->field_name()); + new_getter->set_owner(node->owner()); LocalVariable* result = AddToPreambleNewTempVar(new_getter); result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result); } @@ -510,12 +511,20 @@ void AwaitTransformer::VisitStaticSetterNode(StaticSetterNode* node) { new_receiver = Transform(new_receiver); } AstNode* new_value = Transform(node->value()); - LocalVariable* result = AddToPreambleNewTempVar( - new(Z) StaticSetterNode(node->token_pos(), - new_receiver, - node->cls(), - node->field_name(), - new_value)); + StaticSetterNode* new_setter = + node->function().IsNull() + ? new(Z) StaticSetterNode(node->token_pos(), + new_receiver, + node->cls(), + node->field_name(), + new_value) + : new(Z) StaticSetterNode(node->token_pos(), + new_receiver, + node->field_name(), + node->function(), + new_value); + + LocalVariable* result = AddToPreambleNewTempVar(new_setter); result_ = new(Z) LoadLocalNode(Scanner::kNoSourcePos, result); } diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc index f08f53273d2..e49da93db99 100644 --- a/runtime/vm/flow_graph_builder.cc +++ b/runtime/vm/flow_graph_builder.cc @@ -3166,10 +3166,7 @@ void EffectGraphVisitor::BuildStaticSetter(StaticSetterNode* node, // resolved at compile time (in the caller instance getter's super class). // Unlike a static getter, a super getter has a receiver parameter. const bool is_super_setter = (node->receiver() != NULL); - Function& setter_function = - Function::ZoneHandle(Z, is_super_setter - ? Resolver::ResolveDynamicAnyArgs(node->cls(), setter_name) - : node->cls().LookupStaticFunction(setter_name)); + const Function& setter_function = node->function(); StaticCallInstr* call; if (setter_function.IsNull()) { if (is_super_setter) { diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 3f5d8f29659..d8c590fa55e 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -9343,6 +9343,7 @@ RawObject* Library::LookupImportedObject(const String& name) const { String& import_lib_url = String::Handle(); String& first_import_lib_url = String::Handle(); Object& found_obj = Object::Handle(); + String& found_obj_name = String::Handle(); for (intptr_t i = 0; i < num_imports(); i++) { import ^= ImportAt(i); obj = import.Lookup(name); @@ -9358,13 +9359,28 @@ RawObject* Library::LookupImportedObject(const String& name) const { // from the Dart library. first_import_lib_url = import_lib.url(); found_obj = obj.raw(); + found_obj_name = obj.DictionaryName(); } else if (import_lib_url.StartsWith(Symbols::DartScheme())) { // The newly found object is exported from a Dart system // library. It is hidden by the previously found object. // We continue to search. } else { // We found two different objects with the same name. - return Object::null(); + // Note that we need to compare the names again because + // looking up an unmangled name can return a getter or a + // setter. A getter name is the same as the unmangled name, + // but a setter name is different from an unmangled name or a + // getter name. + if (Field::IsGetterName(found_obj_name)) { + found_obj_name = Field::NameFromGetter(found_obj_name); + } + String& second_obj_name = String::Handle(obj.DictionaryName()); + if (Field::IsGetterName(second_obj_name)) { + second_obj_name = Field::NameFromGetter(second_obj_name); + } + if (found_obj_name.Equals(second_obj_name)) { + return Object::null(); + } } } } @@ -10337,7 +10353,7 @@ RawObject* Namespace::Lookup(const String& name) const { if (Field::IsGetterName(name)) { filter_name = &String::Handle(Field::NameFromGetter(name)); } else if (Field::IsSetterName(name)) { - filter_name = &String::Handle(Field::NameFromGetter(name)); + filter_name = &String::Handle(Field::NameFromSetter(name)); } else { if (obj.IsNull() || obj.IsLibraryPrefix()) { obj = lib.LookupEntry(String::Handle(Field::GetterName(name)), &ignore); diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc index 2749168e769..beaf11ff880 100644 --- a/runtime/vm/parser.cc +++ b/runtime/vm/parser.cc @@ -3912,8 +3912,8 @@ void Parser::ParseFieldDefinition(ClassDesc* members, MemberDesc* field) { if (field->has_static && has_initializer) { class_field.set_value(init_value); if (!has_simple_literal) { - String& getter_name = String::Handle(Z, - Field::GetterSymbol(*field->name)); + String& getter_name = + String::Handle(Z, Field::GetterSymbol(*field->name)); getter = Function::New(getter_name, RawFunction::kImplicitStaticFinalGetter, field->has_static, @@ -3934,8 +3934,8 @@ void Parser::ParseFieldDefinition(ClassDesc* members, MemberDesc* field) { // For instance fields, we create implicit getter and setter methods. if (!field->has_static) { - String& getter_name = String::Handle(Z, - Field::GetterSymbol(*field->name)); + String& getter_name = + String::Handle(Z, Field::GetterSymbol(*field->name)); getter = Function::New(getter_name, RawFunction::kImplicitGetter, field->has_static, field->has_final, @@ -3953,8 +3953,8 @@ void Parser::ParseFieldDefinition(ClassDesc* members, MemberDesc* field) { members->AddFunction(getter); if (!field->has_final) { // Build a setter accessor for non-const fields. - String& setter_name = String::Handle(Z, - Field::SetterSymbol(*field->name)); + String& setter_name = + String::Handle(Z, Field::SetterSymbol(*field->name)); setter = Function::New(setter_name, RawFunction::kImplicitSetter, field->has_static, field->has_final, @@ -10529,7 +10529,8 @@ AstNode* Parser::ParseCascades(AstNode* expr) { static AstNode* LiteralIfStaticConst(Zone* zone, AstNode* expr) { if (expr->IsLoadStaticFieldNode()) { const Field& field = expr->AsLoadStaticFieldNode()->field(); - if (field.is_const()) { + if (field.is_const() && + !expr->AsLoadStaticFieldNode()->is_deferred_reference()) { ASSERT(field.value() != Object::sentinel().raw()); ASSERT(field.value() != Object::transition_sentinel().raw()); return new(zone) LiteralNode(expr->token_pos(), @@ -10898,24 +10899,17 @@ AstNode* Parser::GenerateStaticFieldLookup(const Field& field, } -AstNode* Parser::ParseStaticFieldAccess(const Class& cls, - const String& field_name, - intptr_t ident_pos, - bool consume_cascades) { - TRACE_PARSER("ParseStaticFieldAccess"); +// Reference to 'field_name' with explicit class as primary. +AstNode* Parser::GenerateStaticFieldAccess(const Class& cls, + const String& field_name, + intptr_t ident_pos) { AstNode* access = NULL; const Field& field = Field::ZoneHandle(Z, cls.LookupStaticField(field_name)); Function& func = Function::ZoneHandle(Z); if (field.IsNull()) { // No field, check if we have an explicit getter function. - const String& getter_name = - String::ZoneHandle(Z, Field::GetterName(field_name)); - const int kNumArguments = 0; // no arguments. - func = Resolver::ResolveStatic(cls, - getter_name, - kNumArguments, - Object::empty_array()); - if (func.IsNull()) { + func = cls.LookupGetterFunction(field_name); + if (func.IsNull() || func.IsDynamicFunction()) { // We might be referring to an implicit closure, check to see if // there is a function of the same name. func = cls.LookupStaticFunction(field_name); @@ -11056,12 +11050,14 @@ AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) { } else { // Field access. Class& cls = Class::Handle(Z); + bool is_deferred = false; if (left->IsPrimaryNode()) { PrimaryNode* primary_node = left->AsPrimaryNode(); if (primary_node->primary().IsClass()) { // If the primary node referred to a class we are loading a // qualified static field. cls ^= primary_node->primary().raw(); + is_deferred = primary_node->is_deferred_reference(); } } if (cls.IsNull()) { @@ -11069,8 +11065,13 @@ AstNode* Parser::ParseSelectors(AstNode* primary, bool is_cascade) { selector = CallGetter(ident_pos, left, *ident); } else { // Static field access. - selector = - ParseStaticFieldAccess(cls, *ident, ident_pos, !is_cascade); + selector = GenerateStaticFieldAccess(cls, *ident, ident_pos); + ASSERT(selector != NULL); + if (selector->IsLoadStaticFieldNode()) { + selector->AsLoadStaticFieldNode()->set_is_deferred(is_deferred); + } else if (selector->IsStaticGetterNode()) { + selector->AsStaticGetterNode()->set_is_deferred(is_deferred); + } } } } else if (CurrentToken() == Token::kLBRACK) { @@ -11466,8 +11467,8 @@ RawInstance* Parser::TryCanonicalize(const Instance& instance, // If the field is already initialized, return no ast (NULL). // Otherwise, if the field is constant, initialize the field and return no ast. // If the field is not initialized and not const, return the ast for the getter. -AstNode* Parser::RunStaticFieldInitializer(const Field& field, - intptr_t field_ref_pos) { +StaticGetterNode* Parser::RunStaticFieldInitializer(const Field& field, + intptr_t field_ref_pos) { ASSERT(field.is_static()); const Class& field_owner = Class::ZoneHandle(Z, field.owner()); const String& field_name = String::ZoneHandle(Z, field.name()); @@ -11752,16 +11753,22 @@ AstNode* Parser::ResolveIdentInCurrentLibraryScope(intptr_t ident_pos, } else if (obj.IsField()) { const Field& field = Field::Cast(obj); ASSERT(field.is_static()); - return GenerateStaticFieldLookup(field, ident_pos); + AstNode* get_field = GenerateStaticFieldLookup(field, ident_pos); + if (get_field->IsStaticGetterNode()) { + get_field->AsStaticGetterNode()->set_owner(library_); + } + return get_field; } else if (obj.IsFunction()) { const Function& func = Function::Cast(obj); ASSERT(func.is_static()); if (func.IsGetterFunction() || func.IsSetterFunction()) { - return new(Z) StaticGetterNode(ident_pos, - /* receiver */ NULL, - Class::ZoneHandle(Z, func.Owner()), - ident); - + StaticGetterNode* getter = + new(Z) StaticGetterNode(ident_pos, + /* receiver */ NULL, + Class::ZoneHandle(Z, func.Owner()), + ident); + getter->set_owner(library_); + return getter; } else { return new(Z) PrimaryNode(ident_pos, Function::ZoneHandle(Z, func.raw())); } @@ -11796,7 +11803,7 @@ AstNode* Parser::ResolveIdentInPrefixScope(intptr_t ident_pos, // 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 includes itself with + // In the pathological case where a library imports itself with // a prefix, the name mangling would not help in hiding the private // name, so we need to explicitly reject private names here. return NULL; @@ -11833,6 +11840,7 @@ AstNode* Parser::ResolveIdentInPrefixScope(intptr_t ident_pos, get_field->AsLoadStaticFieldNode()->set_is_deferred(is_deferred); } else if (get_field->IsStaticGetterNode()) { get_field->AsStaticGetterNode()->set_is_deferred(is_deferred); + get_field->AsStaticGetterNode()->set_owner(prefix); } return get_field; } else if (obj.IsFunction()) { @@ -11840,11 +11848,12 @@ AstNode* Parser::ResolveIdentInPrefixScope(intptr_t ident_pos, ASSERT(func.is_static()); if (func.IsGetterFunction() || func.IsSetterFunction()) { StaticGetterNode* getter = new(Z) StaticGetterNode( - ident_pos, - /* receiver */ NULL, - Class::ZoneHandle(Z, func.Owner()), - ident); + ident_pos, + /* receiver */ NULL, + Class::ZoneHandle(Z, func.Owner()), + ident); getter->set_is_deferred(is_deferred); + getter->set_owner(prefix); return getter; } else { PrimaryNode* primary = new(Z) PrimaryNode( diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h index 3a30140d209..d4b1d61d7cf 100644 --- a/runtime/vm/parser.h +++ b/runtime/vm/parser.h @@ -381,8 +381,8 @@ class Parser : public ValueObject { void CheckRecursiveInvocation(); const Instance& EvaluateConstExpr(intptr_t expr_pos, AstNode* expr); - AstNode* RunStaticFieldInitializer(const Field& field, - intptr_t field_ref_pos); + StaticGetterNode* RunStaticFieldInitializer(const Field& field, + intptr_t field_ref_pos); RawObject* EvaluateConstConstructorCall(const Class& type_class, const TypeArguments& type_arguments, const Function& constructor, @@ -711,10 +711,9 @@ class Parser : public ValueObject { AstNode* ParseClosureCall(AstNode* closure); AstNode* GenerateStaticFieldLookup(const Field& field, intptr_t ident_pos); - AstNode* ParseStaticFieldAccess(const Class& cls, - const String& field_name, - intptr_t ident_pos, - bool consume_cascades); + AstNode* GenerateStaticFieldAccess(const Class& cls, + const String& field_name, + intptr_t ident_pos); LocalVariable* LookupLocalScope(const String& ident); void CheckInstanceFieldAccess(intptr_t field_pos, const String& field_name); diff --git a/tests/language/getter_setter_in_lib.dart b/tests/language/getter_setter_in_lib.dart index 0234f6ed0ad..9761ab27c2e 100644 --- a/tests/language/getter_setter_in_lib.dart +++ b/tests/language/getter_setter_in_lib.dart @@ -6,3 +6,5 @@ library GetterSetterInLib; get foo => 42; set foo(a) {} + +get bar => 77; diff --git a/tests/language/getter_setter_in_lib2.dart b/tests/language/getter_setter_in_lib2.dart new file mode 100644 index 00000000000..addfb650fcb --- /dev/null +++ b/tests/language/getter_setter_in_lib2.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library GetterSetterInLib2; + +set bar(a) { } diff --git a/tests/language/getter_setter_in_lib3.dart b/tests/language/getter_setter_in_lib3.dart new file mode 100644 index 00000000000..343edfc30c8 --- /dev/null +++ b/tests/language/getter_setter_in_lib3.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library GetterSetterInLib3; + +var _f = 33; + +set bar(a) { _f = a; } + +get bar => _f; diff --git a/tests/language/getter_setter_in_lib_test.dart b/tests/language/getter_setter_in_lib_test.dart index deba2ba37f3..2195a295db5 100644 --- a/tests/language/getter_setter_in_lib_test.dart +++ b/tests/language/getter_setter_in_lib_test.dart @@ -5,9 +5,19 @@ library GetterSetterInLibTest; import "package:expect/expect.dart"; import 'getter_setter_in_lib.dart'; +import 'getter_setter_in_lib2.dart'; +import 'getter_setter_in_lib3.dart' as L3; main() { Expect.equals(42, foo); foo = 43; Expect.equals(42, foo); + + Expect.equals(77, bar); + bar = 43; + Expect.equals(77, bar); + + Expect.equals(L3.bar, 33); + L3.bar = 44; + Expect.equals(L3.bar, 44); } diff --git a/tests/language/language.status b/tests/language/language.status index b519982e485..75755e6e5ac 100644 --- a/tests/language/language.status +++ b/tests/language/language.status @@ -40,6 +40,7 @@ deferred_inlined_test: Fail # Issue 17523 deferred_optimized_test: Fail # Issue 17523 regress_22443_test: Fail # Issue 17523 enum_mirror_test: Skip # Issue 11511. +getter_setter_in_lib_test: Fail # Issue 23288 override_inheritance_mixed_test/08: Fail # Issue 18124 override_inheritance_mixed_test/09: Fail # Issue 18124 diff --git a/tests/language/language_analyzer.status b/tests/language/language_analyzer.status index 3d3be81b6af..320edeaff8b 100644 --- a/tests/language/language_analyzer.status +++ b/tests/language/language_analyzer.status @@ -13,6 +13,7 @@ await_test: CompileTimeError # Issue 22052 async_await_test/02: CompileTimeError # Issue 22052 regress_17382_test: Skip # don't care about the static warning. regress_23038_test/01: Skip # Issue 23038 +getter_setter_in_lib_test: Fail # issue 23286 issue13179_test: CompileTimeError # Issue 13179 diff --git a/tests/language/language_analyzer2.status b/tests/language/language_analyzer2.status index c9ab3bf1d0a..a49eff2fea0 100644 --- a/tests/language/language_analyzer2.status +++ b/tests/language/language_analyzer2.status @@ -14,6 +14,7 @@ enum_syntax_test/05: Fail # 21649 enum_syntax_test/06: Fail # 21649 regress_17382_test: Skip # don't care about the static warning. +getter_setter_in_lib_test: Fail # issue 23286 # Test issue 12694 (was analyzer issue), (1) when "abstract" is import prefix using it as type is warning; (2) currently analyzer resolves prefix as field (don't ask) built_in_identifier_prefix_test: CompileTimeError # Issue 12694 diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status index a4b385d5c59..52f80df3706 100644 --- a/tests/language/language_dart2js.status +++ b/tests/language/language_dart2js.status @@ -6,6 +6,7 @@ sync_generator2_test/07: MissingCompileTimeError # Issue 22324 sync_generator2_test/08: MissingCompileTimeError # Issue 22324 sync_generator2_test/10: MissingCompileTimeError # Issue 22324 +getter_setter_in_lib_test: Fail # Issue 23288 async_star_test/02: RuntimeError # 22853 async_star_test/05: RuntimeError, Timeout