Version 2.14.0-352.0.dev
Merge commit '12d1bc7236f9afa24df29da6c3dc64fc08d241c7' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
index 80022ea..76697c1 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
@@ -225,7 +225,11 @@
// Continue to resolve type.
}
- // TODO(srawlins): If PropertyAccessorElement, report error.
+ if (member is PropertyAccessorElement) {
+ function.accept(_resolver);
+ _resolveDisallowedExpression(node, member.returnType);
+ return;
+ }
_resolve(node: node, rawType: member.type, name: propertyName.name);
}
@@ -298,9 +302,11 @@
return;
}
- // TODO(srawlins): Need to report cases where [methodElement] is not
- // generic. The 'test_instanceGetter_explicitReceiver' test case needs to
- // be updated to handle this.
+ if (methodElement is PropertyAccessorElement) {
+ function.accept(_resolver);
+ _resolveDisallowedExpression(node, methodElement.returnType);
+ return;
+ }
function.accept(_resolver);
node.staticType = DynamicTypeImpl.instance;
@@ -388,8 +394,6 @@
PrefixedIdentifier prefix,
Element element,
) {
- // TODO(srawlins): Handle `loadLibrary`, as in `p.loadLibrary<int>;`.
-
if (element is MultiplyDefinedElement) {
MultiplyDefinedElement multiply = element;
element = multiply.conflictingElements[0];
@@ -472,17 +476,25 @@
// Continue to assign types.
}
+ if (method is PropertyAccessorElement) {
+ _resolveDisallowedExpression(node, method.returnType);
+ return;
+ }
+
function.staticElement = method;
function.staticType = method.type;
_resolve(node: node, rawType: method.type, name: function.name);
return;
} else {
- // TODO(srawlins): Report CompileTimeErrorCode.UNDEFINED_METHOD.
+ _resolver.errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.UNDEFINED_METHOD,
+ function,
+ [function.name, enclosingClass],
+ );
+ function.staticType = DynamicTypeImpl.instance;
+ node.staticType = DynamicTypeImpl.instance;
return;
}
-
- // TODO(srawlins): if `(target is PropertyAccessorElement)`, report an
- // error.
}
// Classes and type aliases are checked first so as to include a
@@ -504,18 +516,34 @@
_resolveTypeAlias(node: node, element: element, typeAlias: function);
return;
}
- } else if (element is ExecutableElement) {
+ } else if (element is MethodElement) {
function.staticElement = element;
function.staticType = element.type;
_resolve(node: node, rawType: element.type, name: element.name);
return;
+ } else if (element is FunctionElement) {
+ function.staticElement = element;
+ function.staticType = element.type;
+ _resolve(node: node, rawType: element.type, name: element.name);
+ return;
+ } else if (element is PropertyAccessorElement) {
+ function.staticElement = element;
+ function.staticType = element.returnType;
+ _resolveDisallowedExpression(node, element.returnType);
+ return;
+ } else if (element is ExecutableElement) {
+ function.staticElement = element;
+ function.staticType = element.type;
+ _resolveDisallowedExpression(node, element.type);
+ return;
} else if (element is VariableElement) {
function.staticElement = element;
function.staticType = element.type;
_resolveDisallowedExpression(node, element.type);
return;
} else {
- node.staticType = DynamicTypeImpl.instance;
+ _resolveDisallowedExpression(node, DynamicTypeImpl.instance);
+ return;
}
}
diff --git a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
index 21ffd13..3675f92 100644
--- a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
@@ -41,6 +41,26 @@
assertType(reference, 'dynamic');
}
+ test_extensionGetter_extensionOverride() async {
+ await assertErrorsInCode('''
+class A {}
+
+extension E on A {
+ int get foo => 0;
+}
+
+bar(A a) {
+ E(a).foo<int>;
+}
+''', [
+ error(
+ CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION, 67, 8),
+ ]);
+
+ var reference = findNode.functionReference('foo<int>;');
+ assertType(reference, 'dynamic');
+ }
+
test_extensionMethod() async {
await assertNoErrorsInCode('''
class A {}
@@ -175,21 +195,41 @@
reference, findElement.method('foo'), 'void Function(int)');
}
+ test_instanceGetter() async {
+ await assertErrorsInCode('''
+abstract class A {
+ late void Function<T>(T) foo;
+
+ bar() {
+ foo<int>;
+ }
+}
+
+''', [
+ error(
+ CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION, 66, 3),
+ ]);
+
+ var reference = findNode.functionReference('foo<int>;');
+ assertType(reference, 'void Function(int)');
+ }
+
test_instanceGetter_explicitReceiver() async {
- // This test is here to assert that the resolver does not throw, but in the
- // future, an error should be reported here as well.
- await assertNoErrorsInCode('''
+ await assertErrorsInCode('''
class A {
- int foo = 0;
+ late void Function<T>(T) foo;
}
bar(A a) {
a.foo<int>;
}
-''');
+''', [
+ error(
+ CompileTimeErrorCode.DISALLOWED_TYPE_INSTANTIATION_EXPRESSION, 58, 5),
+ ]);
var reference = findNode.functionReference('foo<int>;');
- assertType(reference, 'dynamic');
+ assertType(reference, 'void Function(int)');
}
test_instanceMethod() async {
@@ -430,6 +470,21 @@
reference, findElement.method('foo'), 'void Function(int)');
}
+ test_instanceMethod_unknown() async {
+ await assertErrorsInCode('''
+class A {
+ bar() {
+ foo<int>;
+ }
+}
+''', [
+ error(CompileTimeErrorCode.UNDEFINED_METHOD, 24, 3),
+ ]);
+
+ var reference = findNode.functionReference('foo<int>;');
+ assertType(reference, 'dynamic');
+ }
+
test_localFunction() async {
await assertNoErrorsInCode('''
void bar() {
@@ -722,7 +777,20 @@
);
}
- test_topLevelVariable_prefix_unknown() async {
+ test_topLevelFunction_prefix_unknownPrefix() async {
+ await assertErrorsInCode('''
+bar() {
+ prefix.foo<int>;
+}
+''', [
+ error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 10, 6),
+ ]);
+
+ var reference = findNode.functionReference('foo<int>;');
+ assertType(reference, 'dynamic');
+ }
+
+ test_topLevelVariable_prefix_unknownIdentifier() async {
newFile('$testPackageLibPath/a.dart', content: '');
await assertErrorsInCode('''
import 'a.dart' as prefix;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 7ebeb28..1903854 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -18902,6 +18902,49 @@
return true;
}
+static ClassPtr EnsureSymbolClass(Thread* thread) {
+ ObjectStore* const store = thread->isolate_group()->object_store();
+
+ if (store->symbol_class() != Class::null()) {
+ return store->symbol_class();
+ }
+ Zone* const zone = thread->zone();
+ const auto& library = Library::Handle(zone, Library::InternalLibrary());
+ const auto& symbol_class =
+ Class::Handle(zone, library.LookupClass(Symbols::Symbol()));
+ ASSERT(!symbol_class.IsNull());
+ store->set_symbol_class(symbol_class);
+ return symbol_class.ptr();
+}
+
+bool Symbol::IsSymbolCid(classid_t class_id) {
+ Thread* const thread = Thread::Current();
+ Zone* const zone = thread->zone();
+
+ Class& symbol_class = Class::Handle(zone, EnsureSymbolClass(thread));
+
+ return class_id == symbol_class.id();
+}
+
+// Must be kept in sync with Symbol.hashCode in symbol_patch.dart
+uint32_t Symbol::CanonicalizeHash(const Instance& instance) {
+ ASSERT(IsSymbolCid(instance.GetClassId()));
+
+ Thread* const thread = Thread::Current();
+ Zone* const zone = thread->zone();
+
+ Class& symbol_class = Class::Handle(zone, EnsureSymbolClass(thread));
+ const auto& symbol_name_field = Field::Handle(
+ zone, symbol_class.LookupInstanceFieldAllowPrivate(Symbols::_name()));
+ ASSERT(!symbol_name_field.IsNull());
+
+ // Keep in sync with sdk/lib/_internal/vm/lib/symbol_patch.dart.
+ const auto& name =
+ String::Cast(Object::Handle(zone, instance.GetField(symbol_name_field)));
+ const uint32_t arbitrary_prime = 664597;
+ return 0x1fffffff & (arbitrary_prime * name.CanonicalizeHash());
+}
+
uint32_t Instance::CanonicalizeHash() const {
if (GetClassId() == kNullCid) {
return 2011; // Matches null_patch.dart.
@@ -18914,41 +18957,46 @@
Zone* zone = thread->zone();
const Class& cls = Class::Handle(zone, clazz());
NoSafepointScope no_safepoint(thread);
- const intptr_t instance_size = SizeFromClass();
- ASSERT(instance_size != 0);
- hash = instance_size / kCompressedWordSize;
- uword this_addr = reinterpret_cast<uword>(this->untag());
- Object& obj = Object::Handle(zone);
- Instance& instance = Instance::Handle(zone);
- const auto unboxed_fields_bitmap =
- thread->isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt(
- GetClassId());
+ if (Symbol::IsSymbolCid(GetClassId())) {
+ hash = Symbol::CanonicalizeHash(*this);
+ } else {
+ const intptr_t instance_size = SizeFromClass();
+ ASSERT(instance_size != 0);
+ hash = instance_size / kCompressedWordSize;
+ uword this_addr = reinterpret_cast<uword>(this->untag());
+ Object& obj = Object::Handle(zone);
+ Instance& instance = Instance::Handle(zone);
- for (intptr_t offset = Instance::NextFieldOffset();
- offset < cls.host_next_field_offset(); offset += kCompressedWordSize) {
- if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
- if (kCompressedWordSize == 8) {
- hash = CombineHashes(hash,
- *reinterpret_cast<uint32_t*>(this_addr + offset));
- hash = CombineHashes(
- hash, *reinterpret_cast<uint32_t*>(this_addr + offset + 4));
+ const auto unboxed_fields_bitmap =
+ thread->isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt(
+ GetClassId());
+
+ for (intptr_t offset = Instance::NextFieldOffset();
+ offset < cls.host_next_field_offset(); offset += kCompressedWordSize) {
+ if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
+ if (kCompressedWordSize == 8) {
+ hash = CombineHashes(
+ hash, *reinterpret_cast<uint32_t*>(this_addr + offset));
+ hash = CombineHashes(
+ hash, *reinterpret_cast<uint32_t*>(this_addr + offset + 4));
+ } else {
+ hash = CombineHashes(
+ hash, *reinterpret_cast<uint32_t*>(this_addr + offset));
+ }
} else {
- hash = CombineHashes(hash,
- *reinterpret_cast<uint32_t*>(this_addr + offset));
- }
- } else {
- obj = reinterpret_cast<CompressedObjectPtr*>(this_addr + offset)
- ->Decompress(untag()->heap_base());
- if (obj.IsSentinel()) {
- hash = CombineHashes(hash, 11);
- } else {
- instance ^= obj.ptr();
- hash = CombineHashes(hash, instance.CanonicalizeHash());
+ obj = reinterpret_cast<CompressedObjectPtr*>(this_addr + offset)
+ ->Decompress(untag()->heap_base());
+ if (obj.IsSentinel()) {
+ hash = CombineHashes(hash, 11);
+ } else {
+ instance ^= obj.ptr();
+ hash = CombineHashes(hash, instance.CanonicalizeHash());
+ }
}
}
+ hash = FinalizeHash(hash, String::kHashBits);
}
- hash = FinalizeHash(hash, String::kHashBits);
thread->heap()->SetCanonicalHash(ptr(), hash);
return hash;
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 3b80f79..718d4ca 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -9120,6 +9120,14 @@
friend class Number;
};
+// TODO(http://dartbug.com/46716): Recognize Symbol in the VM.
+class Symbol : public AllStatic {
+ public:
+ static bool IsSymbolCid(classid_t class_id);
+
+ static uint32_t CanonicalizeHash(const Instance& instance);
+};
+
// String may not be '\0' terminated.
class String : public Instance {
public:
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 0bce1aa..dae798f 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4874,8 +4874,7 @@
const char* value_script,
uint32_t hashcode_canonicalize_vm = kCalculateCanonizalizeHash,
bool check_identity = true,
- bool check_hashcode = true,
- bool print_failure = true) {
+ bool check_hashcode = true) {
auto kScriptChars = Utils::CStringUniquePtr(
OS::SCreate(nullptr,
"%s"
@@ -4929,7 +4928,7 @@
success &= identity_hashcode_dart == hashcode_canonicalize_vm;
}
- if (!success && print_failure) {
+ if (!success) {
LogBlock lb;
THR_Print(
"Dart hashCode or Dart identityHashCode does not equal VM "
@@ -4991,6 +4990,15 @@
EXPECT(HashCodeEqualsCanonicalizeHash(kScript));
}
+TEST_CASE(HashCode_Symbol) {
+ const char* kScript =
+ "value() {\n"
+ " return #A;\n"
+ "}\n";
+ EXPECT(HashCodeEqualsCanonicalizeHash(kScript, kCalculateCanonizalizeHash,
+ /*check_identity=*/false));
+}
+
TEST_CASE(HashCode_True) {
const char* kScript =
"value() {\n"
diff --git a/sdk/lib/_internal/vm/lib/symbol_patch.dart b/sdk/lib/_internal/vm/lib/symbol_patch.dart
index 06eb588..eeb9e0b 100644
--- a/sdk/lib/_internal/vm/lib/symbol_patch.dart
+++ b/sdk/lib/_internal/vm/lib/symbol_patch.dart
@@ -6,6 +6,7 @@
@patch
class Symbol {
+ // TODO(http://dartbug.com/46716): Recognize Symbol in the VM.
@patch
const Symbol(String name) : this._name = name;
@@ -52,6 +53,7 @@
return result.toString();
}
+ // Must be kept in sync with Symbol::CanonicalizeHash in object.cc.
@patch
int get hashCode {
const arbitraryPrime = 664597;
diff --git a/sdk/lib/internal/symbol.dart b/sdk/lib/internal/symbol.dart
index 718ac68..2ccb071 100644
--- a/sdk/lib/internal/symbol.dart
+++ b/sdk/lib/internal/symbol.dart
@@ -12,7 +12,9 @@
* make it accessible to Dart platform code via the static method
* [getName].
*/
+@pragma('vm:entry-point')
class Symbol implements core.Symbol {
+ @pragma('vm:entry-point')
final String _name;
/**
diff --git a/tools/VERSION b/tools/VERSION
index d5ad021..e6e0d78 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 351
+PRERELEASE 352
PRERELEASE_PATCH 0
\ No newline at end of file