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