Version 2.15.0-163.0.dev
Merge commit '7b4714d355d47cff33aaad5cfe8b476306860c0c' 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 6e2d340..a611f9f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/function_reference_resolver.dart
@@ -69,10 +69,19 @@
// TODO(srawlins): Handle `function` being a [SuperExpression].
function.accept(_resolver);
- if (function.staticType is FunctionType) {
- _resolve(node: node, rawType: function.staticType);
+ var functionType = function.staticType;
+ if (functionType == null) {
+ _resolveDisallowedExpression(node, functionType);
+ } else if (functionType is FunctionType) {
+ _resolve(node: node, rawType: functionType);
} else {
- _resolveDisallowedExpression(node, function.staticType);
+ var callMethodType =
+ _resolver.typeSystem.getCallMethodType(functionType);
+ if (callMethodType != null) {
+ _resolve(node: node, rawType: callMethodType);
+ } else {
+ _resolveDisallowedExpression(node, functionType);
+ }
}
}
}
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 abb0b8d..fea1532 100644
--- a/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/function_reference_test.dart
@@ -434,6 +434,75 @@
findElement.method('m'), 'void Function(int)');
}
+ test_implicitCallTearoff() async {
+ await assertNoErrorsInCode('''
+class C {
+ T call<T>(T t) => t;
+}
+
+foo() {
+ C()<int>;
+}
+''');
+
+ // TODO(srawlins): An arbitrary expression with a static type which is
+ // callable does not necessarily have an element. However, if we implement
+ // some "implicit call tearoff" node, it would have an element referring to
+ // the `call` method.
+ var functionReference = findNode.functionReference('C()<int>;');
+ assertType(functionReference, 'int Function(int)');
+ }
+
+ test_implicitCallTearoff_tooFewTypeArguments() async {
+ await assertErrorsInCode('''
+class C {
+ void call<T, U>(T t, U u) {}
+}
+
+foo() {
+ C()<int>;
+}
+''', [
+ error(
+ CompileTimeErrorCode
+ .WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION,
+ 57,
+ 5),
+ ]);
+
+ // TODO(srawlins): An arbitrary expression with a static type which is
+ // callable does not necessarily have an element. However, if we implement
+ // some "implicit call tearoff" node, it would have an element referring to
+ // the `call` method.
+ var functionReference = findNode.functionReference('C()<int>;');
+ assertType(functionReference, 'void Function(dynamic, dynamic)');
+ }
+
+ test_implicitCallTearoff_tooManyTypeArguments() async {
+ await assertErrorsInCode('''
+class C {
+ int call(int t) => t;
+}
+
+foo() {
+ C()<int>;
+}
+''', [
+ error(
+ CompileTimeErrorCode
+ .WRONG_NUMBER_OF_TYPE_ARGUMENTS_ANONYMOUS_FUNCTION,
+ 50,
+ 5),
+ ]);
+
+ // TODO(srawlins): An arbitrary expression with a static type which is
+ // callable does not necessarily have an element. However, if we implement
+ // some "implicit call tearoff" node, it would have an element referring to
+ // the `call` method.
+ var functionReference = findNode.functionReference('C()<int>;');
+ assertType(functionReference, 'int Function(int)');
+ }
+
test_instanceGetter_explicitReceiver() async {
await assertNoErrorsInCode('''
class A {
diff --git a/pkg/nnbd_migration/lib/instrumentation.dart b/pkg/nnbd_migration/lib/instrumentation.dart
index c2700c1..1038d97 100644
--- a/pkg/nnbd_migration/lib/instrumentation.dart
+++ b/pkg/nnbd_migration/lib/instrumentation.dart
@@ -249,6 +249,7 @@
dynamicAssignment,
enumValue,
expressionChecks,
+ externalDynamic,
fieldFormalParameter,
fieldNotInitialized,
forEachVariable,
diff --git a/pkg/nnbd_migration/lib/src/edge_origin.dart b/pkg/nnbd_migration/lib/src/edge_origin.dart
index b12f9ec..58ffe89 100644
--- a/pkg/nnbd_migration/lib/src/edge_origin.dart
+++ b/pkg/nnbd_migration/lib/src/edge_origin.dart
@@ -194,6 +194,18 @@
EdgeOriginKind get kind => EdgeOriginKind.enumValue;
}
+/// Edge origin resulting from an explicit or implicit `dynamic` type annotation
+/// appearing in an external declaration.
+class ExternalDynamicOrigin extends EdgeOrigin {
+ ExternalDynamicOrigin(Source? source, AstNode node) : super(source, node);
+
+ @override
+ String get description => 'dynamic type in external declaration';
+
+ @override
+ EdgeOriginKind get kind => EdgeOriginKind.externalDynamic;
+}
+
/// Edge origin resulting from the relationship between a field formal parameter
/// and the corresponding field.
class FieldFormalParameterOrigin extends EdgeOrigin {
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart
index 6673352..653d36b 100644
--- a/pkg/nnbd_migration/lib/src/node_builder.dart
+++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -67,6 +67,10 @@
final TypeProvider _typeProvider;
+ /// Indicates whether the declaration currently being visited is marked
+ /// `external`.
+ bool _visitingExternalDeclaration = false;
+
NodeBuilder(this._variables, this.source, this.listener, this._graph,
this._typeProvider, this._getLineInfo,
{this.instrumentation});
@@ -196,7 +200,8 @@
node.parameters,
node.initializers,
node.body,
- node.redirectedConstructor);
+ node.redirectedConstructor,
+ isExternal: node.externalKeyword != null);
return null;
}
@@ -340,14 +345,16 @@
node.functionExpression.parameters,
null,
node.functionExpression.body,
- null);
+ null,
+ isExternal: node.externalKeyword != null);
return null;
}
@override
DecoratedType? visitFunctionExpression(FunctionExpression node) {
_handleExecutableDeclaration(node, node.declaredElement!, null, null,
- node.typeParameters, node.parameters, null, node.body, null);
+ node.typeParameters, node.parameters, null, node.body, null,
+ isExternal: false);
return null;
}
@@ -456,7 +463,8 @@
node.parameters,
null,
node.body,
- null);
+ null,
+ isExternal: node.externalKeyword != null);
if (declaredElement is PropertyAccessorElement) {
// Store a decorated type for the synthetic field so that in case we try
// to access it later we won't crash (this could happen due to errors in
@@ -537,6 +545,10 @@
var nullabilityNode = NullabilityNode.forTypeAnnotation(target);
var decoratedType = DecoratedType(type, nullabilityNode);
_variables!.recordDecoratedTypeAnnotation(source, node, decoratedType);
+ if (_visitingExternalDeclaration) {
+ _graph.makeNullableUnion(
+ nullabilityNode, ExternalDynamicOrigin(source, node));
+ }
return decoratedType;
}
var typeArguments = const <DecoratedType>[];
@@ -708,49 +720,62 @@
FormalParameterList? parameters,
NodeList<ConstructorInitializer>? initializers,
FunctionBody body,
- ConstructorName? redirectedConstructor) {
+ ConstructorName? redirectedConstructor,
+ {required bool isExternal}) {
metadata?.accept(this);
- var functionType = declaredElement.type;
- DecoratedType? decoratedReturnType;
- var target = NullabilityNodeTarget.element(declaredElement, _getLineInfo);
- if (returnType != null) {
- _pushNullabilityNodeTarget(target.returnType(), () {
- decoratedReturnType = returnType.accept(this);
- });
- } else if (declaredElement is ConstructorElement) {
- // Constructors have no explicit return type annotation, so use the
- // implicit return type.
- decoratedReturnType = _createDecoratedTypeForClass(
- declaredElement.enclosingElement, parameters!.parent);
- instrumentation?.implicitReturnType(source, node, decoratedReturnType);
- } else {
- // Inferred return type.
- decoratedReturnType = DecoratedType.forImplicitType(
- _typeProvider, functionType.returnType, _graph, target);
- instrumentation?.implicitReturnType(source, node, decoratedReturnType);
- }
- var previousPositionalParameters = _positionalParameters;
- var previousNamedParameters = _namedParameters;
- _positionalParameters = [];
- _namedParameters = {};
- DecoratedType decoratedFunctionType;
+ var previouslyVisitingExternalDeclaration = _visitingExternalDeclaration;
try {
- typeParameters?.accept(this);
- _pushNullabilityNodeTarget(target, () => parameters?.accept(this));
- redirectedConstructor?.accept(this);
- initializers?.accept(this);
- decoratedFunctionType = DecoratedType(functionType, _graph.never,
- returnType: decoratedReturnType,
- positionalParameters: _positionalParameters,
- namedParameters: _namedParameters);
- body.accept(this);
+ if (isExternal) {
+ _visitingExternalDeclaration = true;
+ }
+ var functionType = declaredElement.type;
+ DecoratedType? decoratedReturnType;
+ var target = NullabilityNodeTarget.element(declaredElement, _getLineInfo);
+ if (returnType != null) {
+ _pushNullabilityNodeTarget(target.returnType(), () {
+ decoratedReturnType = returnType.accept(this);
+ });
+ } else if (declaredElement is ConstructorElement) {
+ // Constructors have no explicit return type annotation, so use the
+ // implicit return type.
+ decoratedReturnType = _createDecoratedTypeForClass(
+ declaredElement.enclosingElement, parameters!.parent);
+ instrumentation?.implicitReturnType(source, node, decoratedReturnType);
+ } else {
+ // Inferred return type.
+ decoratedReturnType = DecoratedType.forImplicitType(
+ _typeProvider, functionType.returnType, _graph, target);
+ instrumentation?.implicitReturnType(source, node, decoratedReturnType);
+ if (isExternal && functionType.returnType.isDynamic) {
+ _graph.makeNullableUnion(
+ decoratedReturnType.node!, ExternalDynamicOrigin(source, node));
+ }
+ }
+ var previousPositionalParameters = _positionalParameters;
+ var previousNamedParameters = _namedParameters;
+ _positionalParameters = [];
+ _namedParameters = {};
+ DecoratedType decoratedFunctionType;
+ try {
+ typeParameters?.accept(this);
+ _pushNullabilityNodeTarget(target, () => parameters?.accept(this));
+ redirectedConstructor?.accept(this);
+ initializers?.accept(this);
+ decoratedFunctionType = DecoratedType(functionType, _graph.never,
+ returnType: decoratedReturnType,
+ positionalParameters: _positionalParameters,
+ namedParameters: _namedParameters);
+ body.accept(this);
+ } finally {
+ _positionalParameters = previousPositionalParameters;
+ _namedParameters = previousNamedParameters;
+ }
+ _variables!
+ .recordDecoratedElementType(declaredElement, decoratedFunctionType);
+ return decoratedFunctionType;
} finally {
- _positionalParameters = previousPositionalParameters;
- _namedParameters = previousNamedParameters;
+ _visitingExternalDeclaration = previouslyVisitingExternalDeclaration;
}
- _variables!
- .recordDecoratedElementType(declaredElement, decoratedFunctionType);
- return decoratedFunctionType;
}
DecoratedType? _handleFormalParameter(
@@ -768,6 +793,10 @@
} else {
decoratedType = DecoratedType.forImplicitType(
_typeProvider, declaredElement.type, _graph, target);
+ if (_visitingExternalDeclaration) {
+ _graph.makeNullableUnion(
+ decoratedType.node!, ExternalDynamicOrigin(source, node));
+ }
instrumentation?.implicitType(source, node, decoratedType);
}
} else {
@@ -776,6 +805,10 @@
decoratedReturnType = DecoratedType.forImplicitType(_typeProvider,
DynamicTypeImpl.instance, _graph, target.returnType());
instrumentation?.implicitReturnType(source, node, decoratedReturnType);
+ if (_visitingExternalDeclaration) {
+ _graph.makeNullableUnion(
+ decoratedReturnType.node!, ExternalDynamicOrigin(source, node));
+ }
} else {
decoratedReturnType = type.accept(this);
}
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 7e35d77..a13964d 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -3310,6 +3310,114 @@
await _checkSingleFileChanges(content, expected);
}
+ Future<void> test_external_constructor() async {
+ var content = '''
+class C {
+ external C(dynamic Function(dynamic) callback);
+ static Object g(Object Function(Object) callback) => C(callback);
+}
+''';
+ var expected = '''
+class C {
+ external C(dynamic Function(dynamic) callback);
+ static Object g(Object Function(Object?) callback) => C(callback);
+}
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
+ Future<void> test_external_function() async {
+ var content = '''
+external dynamic f();
+Object g() => f();
+''';
+ var expected = '''
+external dynamic f();
+Object? g() => f();
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
+ Future<void> test_external_function_implicit_return() async {
+ var content = '''
+external f();
+Object g() => f();
+''';
+ var expected = '''
+external f();
+Object? g() => f();
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
+ Future<void> test_external_function_implicit_variance() async {
+ var content = '''
+external void f(callback(x));
+void g(Object Function(Object) callback) => f(callback);
+''';
+ var expected = '''
+external void f(callback(x));
+void g(Object Function(Object?) callback) => f(callback);
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
+ Future<void> test_external_function_implicit_variance_complex() async {
+ var content = '''
+external void f(callback(x()));
+void g(Object Function(Object Function()) callback) => f(callback);
+''';
+ var expected = '''
+external void f(callback(x()));
+void g(Object Function(Object? Function()) callback) => f(callback);
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
+ Future<void> test_external_function_variance() async {
+ var content = '''
+external void f(dynamic Function(dynamic) callback);
+void g(Object Function(Object) callback) => f(callback);
+''';
+ var expected = '''
+external void f(dynamic Function(dynamic) callback);
+void g(Object Function(Object?) callback) => f(callback);
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
+ Future<void> test_external_method() async {
+ var content = '''
+class C {
+ external dynamic f();
+ Object g() => f();
+}
+''';
+ var expected = '''
+class C {
+ external dynamic f();
+ Object? g() => f();
+}
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
+ Future<void> test_external_method_implicit() async {
+ var content = '''
+class C {
+ external f();
+ Object g() => f();
+}
+''';
+ var expected = '''
+class C {
+ external f();
+ Object? g() => f();
+}
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
Future<void> test_field_final_uninitalized_used() async {
var content = '''
class C {
diff --git a/runtime/bin/dartdev_isolate.cc b/runtime/bin/dartdev_isolate.cc
index 62701b7..0f75c8e 100644
--- a/runtime/bin/dartdev_isolate.cc
+++ b/runtime/bin/dartdev_isolate.cc
@@ -26,7 +26,7 @@
Dart_CloseNativePort(send_port_id); \
} \
Dart_ExitScope(); \
- Dart_ExitIsolate(); \
+ Dart_ShutdownIsolate(); \
return; \
}
@@ -237,7 +237,7 @@
ProcessError("Unable to find 'main' in root library 'dartdev'",
kErrorExitCode);
Dart_ExitScope();
- Dart_ExitIsolate();
+ Dart_ShutdownIsolate();
return;
}
diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
index 524fcb3..a705710 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
@@ -1112,21 +1112,25 @@
return reinterpret_cast<intptr_t>(value);
}
-// We're using this to keep track of whether the finalizer has been called.
-static intptr_t shared_resource = 0;
+intptr_t* AllocateResource(intptr_t value) {
+ return new intptr_t(value);
+}
+
+void DeleteResource(intptr_t* resource) {
+ delete resource;
+}
+
+intptr_t GetResourceValue(intptr_t* resource) {
+ return *resource;
+}
void DummyResourceFinalizer(void* isolate_peer, void* peer) {
- shared_resource = 0;
+ *reinterpret_cast<intptr_t*>(peer) = 0;
}
-void SetSharedResource(Dart_Handle handle, intptr_t value) {
- Dart_NewFinalizableHandle(handle, nullptr, sizeof(Dart_FinalizableHandle),
+void SetResourceFinalizer(Dart_Handle handle, intptr_t* resource) {
+ Dart_NewFinalizableHandle(handle, resource, sizeof(Dart_FinalizableHandle),
DummyResourceFinalizer);
- shared_resource = value;
-}
-
-intptr_t GetSharedResource() {
- return shared_resource;
}
static void* FfiNativeResolver(const char* name, uintptr_t args_n) {
@@ -1151,11 +1155,17 @@
if (strcmp(name, "PassAsValueAndPointer") == 0 && args_n == 2) {
return reinterpret_cast<void*>(PassAsValueAndPointer);
}
- if (strcmp(name, "SetSharedResource") == 0 && args_n == 2) {
- return reinterpret_cast<void*>(SetSharedResource);
+ if (strcmp(name, "AllocateResource") == 0 && args_n == 1) {
+ return reinterpret_cast<void*>(AllocateResource);
}
- if (strcmp(name, "GetSharedResource") == 0 && args_n == 0) {
- return reinterpret_cast<void*>(GetSharedResource);
+ if (strcmp(name, "DeleteResource") == 0 && args_n == 1) {
+ return reinterpret_cast<void*>(DeleteResource);
+ }
+ if (strcmp(name, "GetResourceValue") == 0 && args_n == 1) {
+ return reinterpret_cast<void*>(GetResourceValue);
+ }
+ if (strcmp(name, "SetResourceFinalizer") == 0 && args_n == 2) {
+ return reinterpret_cast<void*>(SetResourceFinalizer);
}
// This should be unreachable in tests.
ENSURE(false);
diff --git a/tests/ffi/vmspecific_ffi_native_test.dart b/tests/ffi/vmspecific_ffi_native_test.dart
index f9e1e39..8838ebc 100644
--- a/tests/ffi/vmspecific_ffi_native_test.dart
+++ b/tests/ffi/vmspecific_ffi_native_test.dart
@@ -51,17 +51,9 @@
// For automatic transform of NativeFieldWrapperClass1 to Pointer.
-// Sets the native (dummy) resource and the object's finalizer.
-@FfiNative<Void Function(Handle, IntPtr)>('SetSharedResource')
-external void setSharedResource(NativeFieldWrapperClass1 obj, int value);
-// Return the native (dummy) resource.
-@FfiNative<IntPtr Function()>('GetSharedResource')
-external int getSharedResource();
-
class ClassWithNativeField extends NativeFieldWrapperClass1 {
ClassWithNativeField(int value) {
setNativeInstanceField(this, 0, value);
- setSharedResource(this, value);
}
}
@@ -79,6 +71,32 @@
@FfiNative<IntPtr Function(IntPtr, Pointer<Void>)>('PassAsValueAndPointer')
external int passAsValueAndPointer(int value, NativeFieldWrapperClass1 obj);
+// Allocate new native resource we can use to keep track of whether the
+// finalizer has run.
+@FfiNative<Pointer<Void> Function(IntPtr)>('AllocateResource')
+external Pointer<Void> allocateResource(int value);
+
+@FfiNative<Void Function(Pointer<Void>)>('DeleteResource')
+external void deleteResource(Pointer<Void> resource);
+
+// Set up the object's finalizer to reset the resource.
+@FfiNative<Void Function(Handle, Pointer<Void>)>('SetResourceFinalizer')
+external void setResourceFinalizer(
+ NativeFieldWrapperClass1 obj, Pointer<Void> resource);
+
+// Return the native resource's value.
+@FfiNative<IntPtr Function(Pointer<Void>)>('GetResourceValue')
+external int getResourceValue(Pointer<Void> resource);
+
+// Class which ties itself to a resource, resetting the value of the resource
+// when the instance gets collected.
+class ResourceResetter extends NativeFieldWrapperClass1 {
+ ResourceResetter(Pointer<Void> resource) {
+ setNativeInstanceField(this, 0, 0);
+ setResourceFinalizer(this, resource);
+ }
+}
+
// Helper to embed triggerGC(..) as an expression.
int triggerGCWrap() {
triggerGC(0);
@@ -117,23 +135,27 @@
// Test that objects extending NativeFieldWrapperClass1 can be passed to
// FfiNative functions that take Pointer.
// Such objects should automatically be converted and pass as Pointer.
- final cwnf = ClassWithNativeField(123456);
- Expect.equals(123456, passAsHandle(cwnf));
- Expect.equals(123456, passAsPointer(cwnf));
+ {
+ final cwnf = ClassWithNativeField(123456);
+ Expect.equals(123456, passAsHandle(cwnf));
+ Expect.equals(123456, passAsPointer(cwnf));
+ }
// Test that the transform to wrap NativeFieldWrapperClass1 objects in
// _getNativeField(..) doesn't violate the original argument's liveness.
+ final resource = allocateResource(314159);
Expect.equals(
314159,
passAsPointerAndValue(
// 1: Locally alloc. instance.
// If this gets wrapped in another call the instance does not live
// past the return of the wrapper call.
- ClassWithNativeField(314159),
+ ResourceResetter(resource),
// 2: Force GC, to collect the above if it isn't being kept alive.
// 3: Check that the underlying (dummy) resource hasn't been
// "collected" (i.e. reset to 0).
- triggerGCWrap() + getSharedResource()));
+ triggerGCWrap() + getResourceValue(resource)));
+ deleteResource(resource);
// Test that the order of argument evaluation is being preserved through the
// transform wrapping NativeFieldWrapperClass1 objects.
diff --git a/tools/VERSION b/tools/VERSION
index 25ca929..a6d064a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 162
+PRERELEASE 163
PRERELEASE_PATCH 0
\ No newline at end of file