Version 2.14.0-370.0.dev

Merge commit 'b13dc22338bee08f38029de3afb5c21a2569bd58' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index 2ccb299..01dd119 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -6237,14 +6237,14 @@
         message: r"""This is the enclosing class.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Null> codeJsInteropExternalExtensionMemberNotOnJSClass =
-    messageJsInteropExternalExtensionMemberNotOnJSClass;
+const Code<Null> codeJsInteropExternalExtensionMemberOnTypeInvalid =
+    messageJsInteropExternalExtensionMemberOnTypeInvalid;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const MessageCode messageJsInteropExternalExtensionMemberNotOnJSClass =
-    const MessageCode("JsInteropExternalExtensionMemberNotOnJSClass",
+const MessageCode messageJsInteropExternalExtensionMemberOnTypeInvalid =
+    const MessageCode("JsInteropExternalExtensionMemberOnTypeInvalid",
         message:
-            r"""JS interop class required for 'external' extension members.""",
+            r"""JS interop or Native class required for 'external' extension members.""",
         tip:
             r"""Try adding a JS interop annotation to the on type class of the extension.""");
 
diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart
index 7567930..975fca7 100644
--- a/pkg/_js_interop_checks/lib/js_interop_checks.dart
+++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart
@@ -12,7 +12,7 @@
         messageJsInteropAnonymousFactoryPositionalParameters,
         messageJsInteropEnclosingClassJSAnnotation,
         messageJsInteropEnclosingClassJSAnnotationContext,
-        messageJsInteropExternalExtensionMemberNotOnJSClass,
+        messageJsInteropExternalExtensionMemberOnTypeInvalid,
         messageJsInteropExternalMemberNotJSAnnotated,
         messageJsInteropIndexNotSupported,
         messageJsInteropNamedParameters,
@@ -282,13 +282,14 @@
   /// [member] is `external` and not an allowed `external` usage.
   void _checkDisallowedExternal(Member member) {
     if (member.isExternal) {
-      // TODO(rileyporter): Allow extension members on some Native classes.
       if (member.isExtensionMember) {
-        _diagnosticsReporter.report(
-            messageJsInteropExternalExtensionMemberNotOnJSClass,
-            member.fileOffset,
-            member.name.text.length,
-            member.fileUri);
+        if (!_isNativeExtensionMember(member)) {
+          _diagnosticsReporter.report(
+              messageJsInteropExternalExtensionMemberOnTypeInvalid,
+              member.fileOffset,
+              member.name.text.length,
+              member.fileUri);
+        }
       } else if (!hasJSInteropAnnotation(member) &&
           !_isAllowedExternalUsage(member)) {
         // Member could be JS annotated and not considered a JS interop member
@@ -338,6 +339,18 @@
   /// Returns whether given extension [member] is in an extension that is on a
   /// JS interop class.
   bool _isJSExtensionMember(Member member) {
+    return _checkExtensionMember(member, hasJSInteropAnnotation);
+  }
+
+  /// Returns whether given extension [member] is in an extension on a Native
+  /// class.
+  bool _isNativeExtensionMember(Member member) {
+    return _checkExtensionMember(member, _nativeClasses.containsValue);
+  }
+
+  /// Returns whether given extension [member] is on a class that passses the
+  /// given [validateExtensionClass].
+  bool _checkExtensionMember(Member member, Function validateExtensionClass) {
     assert(member.isExtensionMember);
     if (_libraryExtensionsIndex == null) {
       _libraryExtensionsIndex = {};
@@ -347,6 +360,6 @@
     }
 
     var onType = _libraryExtensionsIndex![member.reference]!.onType;
-    return onType is InterfaceType && hasJSInteropAnnotation(onType.classNode);
+    return onType is InterfaceType && validateExtensionClass(onType.classNode);
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
index 7f60edd..bd175b9 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
@@ -30,6 +30,7 @@
         AnalyzerOptions.chromeOsManifestChecks: EmptyProducer(),
       }),
       AnalyzerOptions.plugins: EmptyProducer(),
+      AnalyzerOptions.propagateLinterExceptions: EmptyProducer(),
       AnalyzerOptions.strong_mode: MapProducer({
         AnalyzerOptions.declarationCasts: EmptyProducer(),
         AnalyzerOptions.implicitCasts: EmptyProducer(),
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index aec3305..af4dab4 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -12,6 +12,7 @@
 import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/dart/analysis/testing_data.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
 import 'package:analyzer/src/dart/constant/compute.dart';
 import 'package:analyzer/src/dart/constant/constant_verifier.dart';
 import 'package:analyzer/src/dart/constant/evaluation.dart';
@@ -351,7 +352,10 @@
     }
 
     // Run lints that handle specific node types.
-    unit.accept(LinterVisitor(nodeRegistry));
+    unit.accept(LinterVisitor(
+        nodeRegistry,
+        LinterExceptionHandler(_analysisOptions.propagateLinterExceptions)
+            .logException));
   }
 
   void _computeVerifyErrors(FileState file, CompilationUnit unit) {
diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart
index a068ad7..4387e37 100644
--- a/pkg/analyzer/lib/src/dart/ast/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart
@@ -1306,6 +1306,12 @@
 ///
 /// Clients may not extend, implement or mix-in this class.
 class LinterExceptionHandler {
+  /// Indicates whether linter exceptions should be propagated to the caller (by
+  /// re-throwing them)
+  final bool propagateLinterExceptions;
+
+  LinterExceptionHandler(this.propagateLinterExceptions);
+
   /// A method that can be passed to the `LinterVisitor` constructor to handle
   /// exceptions that occur during linting.
   void logException(
@@ -1326,6 +1332,9 @@
     // TODO(39284): should this exception be silent?
     AnalysisEngine.instance.instrumentationService.logException(
         SilentException(buffer.toString(), exception, stackTrace));
+    if (propagateLinterExceptions) {
+      throw exception;
+    }
   }
 }
 
diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
index 0b1635c..327af6e 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
@@ -348,7 +348,10 @@
     }
 
     // Run lints that handle specific node types.
-    unit.accept(LinterVisitor(nodeRegistry));
+    unit.accept(LinterVisitor(
+        nodeRegistry,
+        LinterExceptionHandler(_analysisOptions.propagateLinterExceptions)
+            .logException));
   }
 
   void _computeVerifyErrors(FileState file, CompilationUnit unit) {
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index 7f04b64..c678842 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -281,6 +281,10 @@
   /// This option is experimental and subject to change.
   bool implicitDynamic = true;
 
+  /// Indicates whether linter exceptions should be propagated to the caller (by
+  /// re-throwing them)
+  bool propagateLinterExceptions = false;
+
   /// A flag indicating whether inference failures are allowed, off by default.
   ///
   /// This option is experimental and subject to change.
@@ -319,6 +323,7 @@
     if (options is AnalysisOptionsImpl) {
       implicitCasts = options.implicitCasts;
       implicitDynamic = options.implicitDynamic;
+      propagateLinterExceptions = options.propagateLinterExceptions;
       strictInference = options.strictInference;
       strictRawTypes = options.strictRawTypes;
     }
@@ -382,6 +387,7 @@
       // Append boolean flags.
       buffer.addBool(implicitCasts);
       buffer.addBool(implicitDynamic);
+      buffer.addBool(propagateLinterExceptions);
       buffer.addBool(strictInference);
       buffer.addBool(strictRawTypes);
       buffer.addBool(useFastaParser);
diff --git a/pkg/analyzer/lib/src/lint/linter_visitor.dart b/pkg/analyzer/lib/src/lint/linter_visitor.dart
index 25afec4..0794a4c 100644
--- a/pkg/analyzer/lib/src/lint/linter_visitor.dart
+++ b/pkg/analyzer/lib/src/lint/linter_visitor.dart
@@ -19,7 +19,7 @@
 
   LinterVisitor(this.registry, [LintRuleExceptionHandler? exceptionHandler])
       : exceptionHandler =
-            exceptionHandler ?? LinterExceptionHandler().logException;
+            exceptionHandler ?? LinterExceptionHandler(true).logException;
 
   @override
   void visitAdjacentStrings(AdjacentStrings node) {
diff --git a/pkg/analyzer/lib/src/task/options.dart b/pkg/analyzer/lib/src/task/options.dart
index 28b4452..2efaaa1 100644
--- a/pkg/analyzer/lib/src/task/options.dart
+++ b/pkg/analyzer/lib/src/task/options.dart
@@ -151,6 +151,8 @@
   /// Ways to say `include`.
   static const List<String> includeSynonyms = ['include', 'true'];
 
+  static const String propagateLinterExceptions = 'propagate-linter-exceptions';
+
   /// Ways to say `true` or `false`.
   static const List<String> trueOrFalse = ['true', 'false'];
 
@@ -163,6 +165,7 @@
     language,
     optionalChecks,
     plugins,
+    propagateLinterExceptions,
     strong_mode,
   ];
 
@@ -805,6 +808,9 @@
       if (feature == AnalyzerOptions.implicitDynamic) {
         options.implicitDynamic = boolValue;
       }
+      if (feature == AnalyzerOptions.propagateLinterExceptions) {
+        options.propagateLinterExceptions = boolValue;
+      }
     }
   }
 
diff --git a/pkg/analyzer/test/src/dart/analysis/context_builder_test.dart b/pkg/analyzer/test/src/dart/analysis/context_builder_test.dart
index f0a3fd2..44a7634 100644
--- a/pkg/analyzer/test/src/dart/analysis/context_builder_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/context_builder_test.dart
@@ -236,6 +236,8 @@
     );
     expect(actual.implicitCasts, expected.implicitCasts);
     expect(actual.implicitDynamic, expected.implicitDynamic);
+    expect(
+        actual.propagateLinterExceptions, expected.propagateLinterExceptions);
     expect(actual.strictInference, expected.strictInference);
     expect(actual.strictRawTypes, expected.strictRawTypes);
   }
diff --git a/pkg/dev_compiler/lib/src/kernel/asset_file_system.dart b/pkg/dev_compiler/lib/src/kernel/asset_file_system.dart
index 930fce7..54fd3b5 100644
--- a/pkg/dev_compiler/lib/src/kernel/asset_file_system.dart
+++ b/pkg/dev_compiler/lib/src/kernel/asset_file_system.dart
@@ -16,11 +16,18 @@
 /// A wrapper around asset server that redirects file read requests
 /// to http get requests to the asset server.
 class AssetFileSystem implements FileSystem {
-  FileSystem original;
+  final FileSystem original;
   final String server;
   final String port;
+  final RetryTimeoutClient client;
 
-  AssetFileSystem(this.original, this.server, this.port);
+  AssetFileSystem(this.original, this.server, this.port)
+      : client = RetryTimeoutClient(
+            HttpClient()
+              ..maxConnectionsPerHost = 200
+              ..connectionTimeout = const Duration(seconds: 30)
+              ..idleTimeout = const Duration(seconds: 30),
+            retries: 4);
 
   /// Convert the uri to a server uri.
   Uri _resourceUri(Uri uri) => Uri.parse('http://$server:$port/${uri.path}');
@@ -34,6 +41,10 @@
     // Pass the uri to the asset server in the debugger.
     return AssetFileSystemEntity(this, _resourceUri(uri));
   }
+
+  void close() {
+    client?.close(force: true);
+  }
 }
 
 class AssetFileSystemEntity implements FileSystemEntity {
@@ -85,20 +96,16 @@
     });
   }
 
-  /// Execute the [body] with the new http client.
+  /// Execute the [body] with the http client created in [fileSystem].
   ///
   /// Throws a [FileSystemException] on failure,
   /// and cleans up the client on return or error.
   Future<T> _runWithClient<T>(
       Future<T> Function(RetryTimeoutClient httpClient) body) async {
-    RetryTimeoutClient httpClient;
     try {
-      httpClient = RetryTimeoutClient(HttpClient(), retries: 5);
-      return await body(httpClient);
+      return await body(fileSystem.client);
     } on Exception catch (e, s) {
       throw FileSystemException(uri, '$e:$s');
-    } finally {
-      httpClient?.close(force: true);
     }
   }
 
diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
index 1cdbd66..86a0cc2 100644
--- a/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler_worker.dart
@@ -113,24 +113,30 @@
   /// receive port corresponding to [sendPort].
   static Future<void> createAndStart(List<String> args,
       {SendPort sendPort}) async {
+    ExpressionCompilerWorker worker;
     if (sendPort != null) {
       var receivePort = ReceivePort();
       sendPort.send(receivePort.sendPort);
       try {
-        var worker = await createFromArgs(args,
+        worker = await createFromArgs(args,
             requestStream: receivePort.cast<Map<String, dynamic>>(),
             sendResponse: sendPort.send);
-        await worker.start();
+        await worker.run();
       } catch (e, s) {
         sendPort
             .send({'exception': '$e', 'stackTrace': '$s', 'succeeded': false});
         rethrow;
       } finally {
         receivePort.close();
+        worker?.close();
       }
     } else {
-      var worker = await createFromArgs(args);
-      await worker.start();
+      try {
+        worker = await createFromArgs(args);
+        await worker.run();
+      } finally {
+        worker?.close();
+      }
     }
   }
 
@@ -243,7 +249,7 @@
   ///
   /// Completes when the [requestStream] closes and we finish handling the
   /// requests.
-  Future<void> start() async {
+  Future<void> run() async {
     await for (var request in requestStream) {
       try {
         var command = request['command'] as String;
@@ -275,6 +281,11 @@
     _processedOptions.ticker.logMs('Stopped expression compiler worker.');
   }
 
+  void close() {
+    var fileSystem = _processedOptions?.fileSystem;
+    if (fileSystem != null && fileSystem is AssetFileSystem) fileSystem.close();
+  }
+
   /// Handles a `CompileExpression` request.
   Future<Map<String, dynamic>> _compileExpression(
       CompileExpressionRequest request) async {
diff --git a/pkg/dev_compiler/test/expression_compiler/asset_file_system_test.dart b/pkg/dev_compiler/test/expression_compiler/asset_file_system_test.dart
index 94a6543..ca481dc 100644
--- a/pkg/dev_compiler/test/expression_compiler/asset_file_system_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/asset_file_system_test.dart
@@ -7,7 +7,6 @@
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io' show HttpServer;
-import 'dart:math';
 
 import 'package:browser_launcher/browser_launcher.dart';
 import 'package:dev_compiler/src/kernel/asset_file_system.dart';
@@ -67,6 +66,7 @@
   return Response.internalServerError();
 }
 
+int _attempts = 0;
 FutureOr<Response> unreliableHandler(Request request) {
   final uri = request.requestedUri;
   final headers = {
@@ -74,7 +74,7 @@
     ...request.headers,
   };
 
-  if (Random().nextInt(3) == 0) return Response.internalServerError();
+  if ((_attempts++) % 5 == 0) return Response.internalServerError();
 
   if (request.method == 'HEAD') {
     // 'exists'
@@ -112,6 +112,7 @@
 
     tearDownAll(() async {
       await expectLater(server.close(), completes);
+      fileSystem.close();
     });
 
     test('can tell if file exists', () async {
@@ -148,12 +149,10 @@
     test('can read a lot of files concurrently', () async {
       var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
       var futures = [
-        for (var i = 0; i < 512; i++) entity.readAsBytes(),
+        for (var i = 0; i < 512; i++)
+          _expectContents(entity, _smallFileContents),
       ];
-      var results = await Future.wait(futures);
-      for (var result in results) {
-        expect(utf8.decode(result), _smallFileContents);
-      }
+      await Future.wait(futures);
     }, timeout: const Timeout.factor(2));
   });
 
@@ -171,6 +170,7 @@
 
     tearDownAll(() async {
       await expectLater(server.close(), completes);
+      fileSystem.close();
     });
 
     test('can tell if file exists', () async {
@@ -225,15 +225,12 @@
     });
 
     test('can read a lot of files concurrently', () async {
+      var fileContents = _largeFileContents();
       var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
       var futures = [
-        for (var i = 0; i < 512; i++) entity.readAsString(),
+        for (var i = 0; i < 512; i++) _expectContents(entity, fileContents),
       ];
-      var results = await Future.wait(futures);
-      var fileContents = _largeFileContents();
-      for (var result in results) {
-        expect(result, fileContents);
-      }
+      await Future.wait(futures);
     }, timeout: const Timeout.factor(2));
   });
 
@@ -251,6 +248,7 @@
 
     tearDownAll(() async {
       await expectLater(server.close(), completes);
+      fileSystem.close();
     });
 
     test('can tell if file exists', () async {
@@ -287,12 +285,10 @@
     test('can read a lot of files concurrently', () async {
       var entity = fileSystem.entityForUri(Uri.parse(_existingFile));
       var futures = [
-        for (var i = 0; i < 512; i++) entity.readAsString(),
+        for (var i = 0; i < 512; i++)
+          _expectContents(entity, _smallFileContents),
       ];
-      var results = await Future.wait(futures);
-      for (var result in results) {
-        expect(result, _smallFileContents);
-      }
+      await Future.wait(futures);
     }, timeout: const Timeout.factor(2));
   });
 
@@ -310,6 +306,7 @@
 
     tearDownAll(() async {
       await expectLater(server.close(), completes);
+      fileSystem.close();
     });
 
     test('cannot tell if file exists', () async {
@@ -341,3 +338,11 @@
     });
   });
 }
+
+// Read the response (and free the socket) as soon as we get it.
+// That allows some connection to buffer and wait for free sockets
+// when the limit if connections is reached.
+Future<void> _expectContents(FileSystemEntity entity, String contents) async {
+  var result = await entity.readAsString();
+  expect(result, contents);
+}
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
index 9a06c77..c54916b 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
@@ -836,13 +836,14 @@
       soundNullSafety: soundNullSafety,
       verbose: verbose,
     );
-    workerDone = worker.start();
+    workerDone = worker.run();
   }
 
   Future<void> tearDown() async {
     unawaited(requestController.close());
     await workerDone;
     unawaited(responseController.close());
+    worker?.close();
   }
 }
 
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index ba2bc55..8eb1f8d 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -514,8 +514,8 @@
 JsInteropDartClassExtendsJSClass/example: Fail # Web compiler specific
 JsInteropEnclosingClassJSAnnotation/analyzerCode: Fail # Web compiler specific
 JsInteropEnclosingClassJSAnnotation/example: Fail # Web compiler specific
-JsInteropExternalExtensionMemberNotOnJSClass/analyzerCode: Fail # Web compiler specific
-JsInteropExternalExtensionMemberNotOnJSClass/example: Fail # Web compiler specific
+JsInteropExternalExtensionMemberOnTypeInvalid/analyzerCode: Fail # Web compiler specific
+JsInteropExternalExtensionMemberOnTypeInvalid/example: Fail # Web compiler specific
 JsInteropExternalMemberNotJSAnnotated/analyzerCode: Fail # Web compiler specific
 JsInteropExternalMemberNotJSAnnotated/example: Fail # Web compiler specific
 JsInteropIndexNotSupported/analyzerCode: Fail # Web compiler specific
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 0873cba..e94eac1 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4914,8 +4914,8 @@
   template: "This is the enclosing class."
   severity: CONTEXT
 
-JsInteropExternalExtensionMemberNotOnJSClass:
-  template: "JS interop class required for 'external' extension members."
+JsInteropExternalExtensionMemberOnTypeInvalid:
+  template: "JS interop or Native class required for 'external' extension members."
   tip: "Try adding a JS interop annotation to the on type class of the extension."
 
 JsInteropExternalMemberNotJSAnnotated:
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index 7c958ce..2518fb5 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -10,8 +10,6 @@
 
 constructor_tearoffs/call_instantiation: TypeCheckError
 constructor_tearoffs/const_tear_off: RuntimeError
-constructor_tearoffs/inferred_constructor_tear_off: RuntimeError
-constructor_tearoffs/inferred_non_proper_rename: RuntimeError
 constructor_tearoffs/redirecting_constructors: RuntimeError
 constructor_tearoffs/redirecting_factory_tear_off: RuntimeError
 extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 9f5f24a..7ea4ce0 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -8,8 +8,6 @@
 
 constructor_tearoffs/call_instantiation: TypeCheckError
 constructor_tearoffs/const_tear_off: RuntimeError
-constructor_tearoffs/inferred_constructor_tear_off: RuntimeError
-constructor_tearoffs/inferred_non_proper_rename: RuntimeError
 constructor_tearoffs/redirecting_constructors: RuntimeError
 constructor_tearoffs/redirecting_factory_tear_off: RuntimeError
 extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index 803d18f..8256003 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -13,8 +13,6 @@
 
 constructor_tearoffs/call_instantiation: TypeCheckError
 constructor_tearoffs/const_tear_off: RuntimeError
-constructor_tearoffs/inferred_constructor_tear_off: RuntimeError
-constructor_tearoffs/inferred_non_proper_rename: RuntimeError
 constructor_tearoffs/redirecting_constructors: RuntimeError
 constructor_tearoffs/redirecting_factory_tear_off: RuntimeError
 extension_types/extension_on_nullable: ExpectationFileMismatchSerialized # Expected.
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index d2157f7..9905fcc 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -2479,6 +2479,15 @@
 
   @override
   Type visitStaticTearOffConstant(StaticTearOffConstant constant) {
+    final Procedure member = constant.target;
+    summaryCollector._entryPointsListener
+        .addRawCall(new DirectSelector(member));
+    summaryCollector._entryPointsListener.recordTearOff(member);
+    return _getStaticType(constant);
+  }
+
+  @override
+  Type visitConstructorTearOffConstant(ConstructorTearOffConstant constant) {
     final Member member = constant.target;
     summaryCollector._entryPointsListener
         .addRawCall(new DirectSelector(member));
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 82bebec..e964f61 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -1939,6 +1939,11 @@
   }
 
   @override
+  visitConstructorTearOffConstant(ConstructorTearOffConstant constant) {
+    shaker.addUsedMember(constant.target);
+  }
+
+  @override
   visitInstantiationConstant(InstantiationConstant constant) {
     analyzeConstant(constant.tearOffConstant);
   }
diff --git a/runtime/vm/compiler/frontend/constant_reader.cc b/runtime/vm/compiler/frontend/constant_reader.cc
index f2c7656..6040fb1 100644
--- a/runtime/vm/compiler/frontend/constant_reader.cc
+++ b/runtime/vm/compiler/frontend/constant_reader.cc
@@ -353,6 +353,18 @@
       instance = function.ImplicitStaticClosure();
       break;
     }
+    case kConstructorTearOffConstant: {
+      const NameIndex index = reader.ReadCanonicalNameReference();
+      Function& function = Function::Handle(Z);
+      if (H.IsConstructor(index)) {
+        function = H.LookupConstructorByKernelConstructor(index);
+      } else {
+        function = H.LookupStaticMethodByKernelProcedure(index);
+      }
+      function = function.ImplicitClosureFunction();
+      instance = function.ImplicitStaticClosure();
+      break;
+    }
     case kTypeLiteralConstant: {
       // Build type from the raw bytes (needs temporary translator).
       // Const canonical type erasure is not applied to constant type literals.
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 9dd149c..257ee0e 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -5299,7 +5299,9 @@
         if (!closure_owner_.IsNull()) {
           function = Function::NewClosureFunctionWithKind(
               UntaggedFunction::kClosureFunction, *name,
-              parsed_function()->function(), position, closure_owner_);
+              parsed_function()->function(),
+              parsed_function()->function().is_static(), position,
+              closure_owner_);
         } else {
           function = Function::NewClosureFunction(
               *name, parsed_function()->function(), position);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index d35850e..06b2e7e 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -3425,13 +3425,45 @@
 
   intptr_t type_args_len = 0;
   if (function.IsGeneric()) {
-    type_args_len = function.NumTypeParameters();
-    ASSERT(parsed_function_->function_type_arguments() != NULL);
-    closure += LoadLocal(parsed_function_->function_type_arguments());
+    if (target.IsConstructor()) {
+      const auto& result_type = AbstractType::Handle(Z, function.result_type());
+      ASSERT(result_type.IsFinalized());
+      // Instantiate a flattened type arguments vector which
+      // includes type arguments corresponding to superclasses.
+      // TranslateInstantiatedTypeArguments is smart enough to
+      // avoid instantiation and resuse passed function type arguments
+      // if there are no extra type arguments in the flattened vector.
+      const auto& instantiated_type_arguments =
+          TypeArguments::ZoneHandle(Z, result_type.arguments());
+      closure +=
+          TranslateInstantiatedTypeArguments(instantiated_type_arguments);
+    } else {
+      type_args_len = function.NumTypeParameters();
+      ASSERT(parsed_function_->function_type_arguments() != NULL);
+      closure += LoadLocal(parsed_function_->function_type_arguments());
+    }
+  } else if (target.IsFactory()) {
+    // Factories always take an extra implicit argument for
+    // type arguments even if their classes don't have type parameters.
+    closure += NullConstant();
   }
 
   // Push receiver.
-  if (!target.is_static()) {
+  if (target.IsGenerativeConstructor()) {
+    const Class& cls = Class::Handle(Z, target.Owner());
+    if (cls.NumTypeArguments() > 0) {
+      if (!function.IsGeneric()) {
+        Type& cls_type = Type::Handle(Z, cls.DeclarationType());
+        closure += Constant(TypeArguments::ZoneHandle(Z, cls_type.arguments()));
+      }
+      closure += AllocateObject(function.token_pos(), cls, 1);
+    } else {
+      ASSERT(!function.IsGeneric());
+      closure += AllocateObject(function.token_pos(), cls, 0);
+    }
+    LocalVariable* receiver = MakeTemporary();
+    closure += LoadLocal(receiver);
+  } else if (!target.is_static()) {
     // The context has a fixed shape: a single variable which is the
     // closed-over receiver.
     closure += LoadLocal(parsed_function_->ParameterVariable(0));
@@ -3445,7 +3477,7 @@
   // Forward parameters to the target.
   intptr_t argument_count = function.NumParameters() -
                             function.NumImplicitParameters() +
-                            (target.is_static() ? 0 : 1);
+                            target.NumImplicitParameters();
   ASSERT(argument_count == target.NumParameters());
 
   Array& argument_names =
@@ -3455,6 +3487,12 @@
                         argument_names, ICData::kNoRebind,
                         /* result_type = */ NULL, type_args_len);
 
+  if (target.IsGenerativeConstructor()) {
+    // Drop result of constructor invocation, leave receiver
+    // instance on the stack.
+    closure += Drop();
+  }
+
   // Return the result.
   closure += Return(function.end_token_pos());
 
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 34249a3..0205fe4 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -1452,9 +1452,6 @@
 
 void ScopeBuilder::VisitTypeParameterType() {
   Function& function = Function::Handle(Z, parsed_function_->function().ptr());
-  while (function.IsClosureFunction()) {
-    function = function.parent_function();
-  }
 
   helper_.ReadNullability();  // read nullability.
 
@@ -1465,19 +1462,25 @@
 
   intptr_t index = helper_.ReadUInt();  // read index for parameter.
 
-  if (function.IsFactory()) {
-    // The type argument vector is passed as the very first argument to the
-    // factory constructor function.
-    HandleSpecialLoad(&result_->type_arguments_variable,
-                      Symbols::TypeArgumentsParameter());
-  } else {
-    // If the type parameter is a parameter to this or an enclosing function, we
-    // can read it directly from the function type arguments vector later.
-    // Otherwise, the type arguments vector we need is stored on the instance
-    // object, so we need to capture 'this'.
-    Class& parent_class = Class::Handle(Z, function.Owner());
-    if (index < parent_class.NumTypeParameters()) {
-      HandleLoadReceiver();
+  if (!function.IsImplicitStaticClosureFunction()) {
+    while (function.IsClosureFunction()) {
+      function = function.parent_function();
+    }
+
+    if (function.IsFactory()) {
+      // The type argument vector is passed as the very first argument to the
+      // factory constructor function.
+      HandleSpecialLoad(&result_->type_arguments_variable,
+                        Symbols::TypeArgumentsParameter());
+    } else {
+      // If the type parameter is a parameter to this or an enclosing function,
+      // we can read it directly from the function type arguments vector later.
+      // Otherwise, the type arguments vector we need is stored on the instance
+      // object, so we need to capture 'this'.
+      Class& parent_class = Class::Handle(Z, function.Owner());
+      if (index < parent_class.NumTypeParameters()) {
+        HandleLoadReceiver();
+      }
     }
   }
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 3e1f788..0df5c45 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -7519,7 +7519,7 @@
 }
 
 FunctionPtr Function::implicit_closure_function() const {
-  if (IsClosureFunction() || IsFactory() || IsDispatcherOrImplicitAccessor() ||
+  if (IsClosureFunction() || IsDispatcherOrImplicitAccessor() ||
       IsFieldInitializer() || IsFfiTrampoline()) {
     return Function::null();
   }
@@ -7689,6 +7689,7 @@
 //   native function:         Array[0] = String native name
 //                            Array[1] = Function implicit closure function
 //   regular function:        Function for implicit closure function
+//   constructor, factory:    Function for implicit closure function
 //   ffi trampoline function: FfiTrampolineData  (Dart->C)
 //   dyn inv forwarder:       Array[0] = Function target
 //                            Array[1] = TypeArguments default type args
@@ -9160,6 +9161,7 @@
 FunctionPtr Function::NewClosureFunctionWithKind(UntaggedFunction::Kind kind,
                                                  const String& name,
                                                  const Function& parent,
+                                                 bool is_static,
                                                  TokenPosition token_pos,
                                                  const Object& owner) {
   ASSERT((kind == UntaggedFunction::kClosureFunction) ||
@@ -9171,7 +9173,7 @@
                                                  : 0));
   const Function& result = Function::Handle(
       Function::New(signature, name, kind,
-                    /* is_static = */ parent.is_static(),
+                    /* is_static = */ is_static,
                     /* is_const = */ false,
                     /* is_abstract = */ false,
                     /* is_external = */ false,
@@ -9186,7 +9188,8 @@
   // Use the owner defining the parent function and not the class containing it.
   const Object& parent_owner = Object::Handle(parent.RawOwner());
   return NewClosureFunctionWithKind(UntaggedFunction::kClosureFunction, name,
-                                    parent, token_pos, parent_owner);
+                                    parent, parent.is_static(), token_pos,
+                                    parent_owner);
 }
 
 FunctionPtr Function::NewImplicitClosureFunction(const String& name,
@@ -9194,8 +9197,9 @@
                                                  TokenPosition token_pos) {
   // Use the owner defining the parent function and not the class containing it.
   const Object& parent_owner = Object::Handle(parent.RawOwner());
-  return NewClosureFunctionWithKind(UntaggedFunction::kImplicitClosureFunction,
-                                    name, parent, token_pos, parent_owner);
+  return NewClosureFunctionWithKind(
+      UntaggedFunction::kImplicitClosureFunction, name, parent,
+      parent.is_static() || parent.IsConstructor(), token_pos, parent_owner);
 }
 
 bool Function::SafeToClosurize() const {
@@ -9241,7 +9245,7 @@
       zone, NewImplicitClosureFunction(closure_name, *this, token_pos()));
 
   // Set closure function's context scope.
-  if (is_static()) {
+  if (is_static() || IsConstructor()) {
     closure_function.set_context_scope(Object::empty_context_scope());
   } else {
     const ContextScope& context_scope = ContextScope::Handle(
@@ -9252,15 +9256,46 @@
   FunctionType& closure_signature =
       FunctionType::Handle(zone, closure_function.signature());
 
-  // Set closure function's type parameters.
-  // This function cannot be local, therefore it has no generic parent.
-  // Its implicit closure function therefore has no generic parent function
-  // either. That is why it is safe to simply copy the type parameters.
-  closure_signature.SetTypeParameters(
-      TypeParameters::Handle(zone, type_parameters()));
+  // Set closure function's type parameters and result type.
+  if (IsConstructor()) {
+    // Inherit type parameters from owner class.
+    const auto& cls = Class::Handle(zone, Owner());
+    closure_signature.SetTypeParameters(
+        TypeParameters::Handle(zone, cls.type_parameters()));
+    ASSERT(closure_signature.NumTypeParameters() == cls.NumTypeParameters());
 
-  // Set closure function's result type to this result type.
-  closure_signature.set_result_type(AbstractType::Handle(zone, result_type()));
+    Type& result_type = Type::Handle(zone);
+    const Nullability result_nullability =
+        (nnbd_mode() == NNBDMode::kOptedInLib) ? Nullability::kNonNullable
+                                               : Nullability::kLegacy;
+    if (cls.IsGeneric()) {
+      TypeArguments& type_args = TypeArguments::Handle(zone);
+      const intptr_t num_type_params = cls.NumTypeParameters();
+      ASSERT(num_type_params > 0);
+      type_args = TypeArguments::New(num_type_params);
+      TypeParameter& type_param = TypeParameter::Handle(zone);
+      for (intptr_t i = 0; i < num_type_params; i++) {
+        type_param = closure_signature.TypeParameterAt(i);
+        type_args.SetTypeAt(i, type_param);
+      }
+      result_type = Type::New(cls, type_args, result_nullability);
+      result_type ^= ClassFinalizer::FinalizeType(result_type);
+    } else {
+      result_type = cls.DeclarationType();
+      result_type = result_type.ToNullability(result_nullability, Heap::kOld);
+    }
+    closure_signature.set_result_type(result_type);
+  } else {
+    // This function cannot be local, therefore it has no generic parent.
+    // Its implicit closure function therefore has no generic parent function
+    // either. That is why it is safe to simply copy the type parameters.
+    closure_signature.SetTypeParameters(
+        TypeParameters::Handle(zone, type_parameters()));
+
+    // Set closure function's result type to this result type.
+    closure_signature.set_result_type(
+        AbstractType::Handle(zone, result_type()));
+  }
 
   // Set closure function's end token to this end token.
   closure_function.set_end_token_pos(end_token_pos());
@@ -9274,8 +9309,9 @@
   // removing the receiver if this is an instance method and adding the closure
   // object as first parameter.
   const int kClosure = 1;
-  const int has_receiver = is_static() ? 0 : 1;
-  const int num_fixed_params = kClosure - has_receiver + num_fixed_parameters();
+  const int num_implicit_params = NumImplicitParameters();
+  const int num_fixed_params =
+      kClosure - num_implicit_params + num_fixed_parameters();
   const int num_opt_params = NumOptionalParameters();
   const bool has_opt_pos_params = HasOptionalPositionalParameters();
   const int num_params = num_fixed_params + num_opt_params;
@@ -9294,19 +9330,19 @@
   closure_signature.SetParameterTypeAt(0, param_type);
   closure_function.SetParameterNameAt(0, Symbols::ClosureParameter());
   for (int i = kClosure; i < num_pos_params; i++) {
-    param_type = ParameterTypeAt(has_receiver - kClosure + i);
+    param_type = ParameterTypeAt(num_implicit_params - kClosure + i);
     closure_signature.SetParameterTypeAt(i, param_type);
-    param_name = ParameterNameAt(has_receiver - kClosure + i);
+    param_name = ParameterNameAt(num_implicit_params - kClosure + i);
     // Set the name in the function for positional parameters.
     closure_function.SetParameterNameAt(i, param_name);
   }
   for (int i = num_pos_params; i < num_params; i++) {
-    param_type = ParameterTypeAt(has_receiver - kClosure + i);
+    param_type = ParameterTypeAt(num_implicit_params - kClosure + i);
     closure_signature.SetParameterTypeAt(i, param_type);
-    param_name = ParameterNameAt(has_receiver - kClosure + i);
+    param_name = ParameterNameAt(num_implicit_params - kClosure + i);
     // Set the name in the signature for named parameters.
     closure_signature.SetParameterNameAt(i, param_name);
-    if (IsRequiredAt(has_receiver - kClosure + i)) {
+    if (IsRequiredAt(num_implicit_params - kClosure + i)) {
       closure_signature.SetIsRequiredAt(i);
     }
   }
@@ -9315,7 +9351,7 @@
 
   // Change covariant parameter types to either Object? for an opted-in implicit
   // closure or to Object* for a legacy implicit closure.
-  if (!is_static()) {
+  if (!is_static() && !IsConstructor()) {
     BitVector is_covariant(zone, NumParameters());
     BitVector is_generic_covariant_impl(zone, NumParameters());
     kernel::ReadParameterCovariance(*this, &is_covariant,
@@ -9328,7 +9364,7 @@
                       : object_store->legacy_object_type();
     ASSERT(object_type.IsCanonical());
     for (intptr_t i = kClosure; i < num_params; ++i) {
-      const intptr_t original_param_index = has_receiver - kClosure + i;
+      const intptr_t original_param_index = num_implicit_params - kClosure + i;
       if (is_covariant.Contains(original_param_index) ||
           is_generic_covariant_impl.Contains(original_param_index)) {
         closure_signature.SetParameterTypeAt(i, object_type);
@@ -9340,6 +9376,7 @@
   closure_function.SetSignature(closure_signature);
   set_implicit_closure_function(closure_function);
   ASSERT(closure_function.IsImplicitClosureFunction());
+  ASSERT(HasImplicitClosureFunction());
   return closure_function.ptr();
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 5f0f6dc..eb0d9e3 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2882,13 +2882,14 @@
 
   static const char* KindToCString(UntaggedFunction::Kind kind);
 
+  bool IsConstructor() const {
+    return kind() == UntaggedFunction::kConstructor;
+  }
   bool IsGenerativeConstructor() const {
-    return (kind() == UntaggedFunction::kConstructor) && !is_static();
+    return IsConstructor() && !is_static();
   }
   bool IsImplicitConstructor() const;
-  bool IsFactory() const {
-    return (kind() == UntaggedFunction::kConstructor) && is_static();
-  }
+  bool IsFactory() const { return IsConstructor() && is_static(); }
 
   bool HasThisParameter() const {
     return IsDynamicFunction(/*allow_abstract=*/true) ||
@@ -3549,6 +3550,7 @@
   static FunctionPtr NewClosureFunctionWithKind(UntaggedFunction::Kind kind,
                                                 const String& name,
                                                 const Function& parent,
+                                                bool is_static,
                                                 TokenPosition token_pos,
                                                 const Object& owner);
 
diff --git a/tests/lib/js/external_nonjs_static_test.dart b/tests/lib/js/external_nonjs_static_test.dart
index 913d202..87c4615 100644
--- a/tests/lib/js/external_nonjs_static_test.dart
+++ b/tests/lib/js/external_nonjs_static_test.dart
@@ -7,6 +7,7 @@
 
 library external_nonjs_static_test;
 
+import 'dart:html';
 import 'package:js/js.dart';
 
 external var topLevelField;
@@ -101,43 +102,43 @@
 extension ExtensionNonJS on NonJSClass {
   external var field;
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external final finalField;
   //             ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static var staticField;
   //                  ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static final staticFinalField;
   //                    ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   external get getter;
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external set setter(_);
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   external static get staticGetter;
   //                  ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static set staticSetter(_);
   //                  ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   external method();
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static staticMethod();
   //              ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external optionalParameterMethod([int? a, int b = 0]);
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external overridenMethod();
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   nonExternalMethod() => 1;
   static nonExternalStaticMethod() => 2;
@@ -150,9 +151,29 @@
 extension ExtensionGenericNonJS<T> on GenericNonJSClass<T> {
   external T method();
   //         ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 }
 
 class GenericNonJSClass<T> {}
 
+extension ExtensionNative on HtmlElement {
+  external var field;
+  external final finalField;
+  external static var staticField;
+  external static final staticFinalField;
+
+  external get getter;
+  external set setter(_);
+
+  external static get staticGetter;
+  external static set staticSetter(_);
+
+  external method();
+  external static staticMethod();
+  external optionalParameterMethod([int? a, int b = 0]);
+
+  nonExternalMethod() => 1;
+  static nonExternalStaticMethod() => 2;
+}
+
 main() {}
diff --git a/tests/lib/js/external_static_test.dart b/tests/lib/js/external_static_test.dart
index 0a1c8db..a455de0 100644
--- a/tests/lib/js/external_static_test.dart
+++ b/tests/lib/js/external_static_test.dart
@@ -8,6 +8,7 @@
 @JS()
 library external_static_test;
 
+import 'dart:html';
 import 'package:js/js.dart';
 
 // external top level members ok in @JS() library.
@@ -89,52 +90,52 @@
 extension ExtensionNonJS on NonJSClass {
   external var field;
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external final finalField;
   //             ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static var staticField;
   //                  ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static final staticFinalField;
   //                    ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   external get getter;
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external set setter(_);
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   external static get staticGetter;
   //                  ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static set staticSetter(_);
   //                  ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   external method();
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static staticMethod();
   //              ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external optionalParameterMethod([int? a, int b = 0]);
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external overridenMethod();
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   @JS('fieldAnnotation')
   external var annotatedField;
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   @JS('memberAnnotation')
   external annotatedMethod();
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   nonExternalMethod() => 1;
   static nonExternalStaticMethod() => 2;
@@ -147,7 +148,7 @@
 extension ExtensionGenericNonJS<T> on GenericNonJSClass<T> {
   external T method();
   //         ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 }
 
 class GenericNonJSClass<T> {}
@@ -229,4 +230,24 @@
 @JS()
 class _privateJSClass {}
 
+extension ExtensionNative on HtmlElement {
+  external var field;
+  external final finalField;
+  external static var staticField;
+  external static final staticFinalField;
+
+  external get getter;
+  external set setter(_);
+
+  external static get staticGetter;
+  external static set staticSetter(_);
+
+  external method();
+  external static staticMethod();
+  external optionalParameterMethod([int? a, int b = 0]);
+
+  nonExternalMethod() => 1;
+  static nonExternalStaticMethod() => 2;
+}
+
 main() {}
diff --git a/tests/lib_2/js/external_nonjs_static_test.dart b/tests/lib_2/js/external_nonjs_static_test.dart
index 1de7e61..d0fa7df 100644
--- a/tests/lib_2/js/external_nonjs_static_test.dart
+++ b/tests/lib_2/js/external_nonjs_static_test.dart
@@ -9,6 +9,7 @@
 
 library external_nonjs_static_test;
 
+import 'dart:html';
 import 'package:js/js.dart';
 
 external get topLevelGetter;
@@ -75,27 +76,30 @@
 extension ExtensionNonJS on NonJSClass {
   external get getter;
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external set setter(_);
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   external static get staticGetter;
   //                  ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static set staticSetter(_);
   //                  ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   external method();
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static staticMethod();
   //              ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
+  external optionalParameterMethod([int a, int b = 0]);
+  //       ^
+  // [web] JS interop or Native class required for 'external' extension members.
   external overridenMethod();
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   nonExternalMethod() => 1;
   static nonExternalStaticMethod() => 2;
@@ -108,9 +112,24 @@
 extension ExtensionGenericNonJS<T> on GenericNonJSClass<T> {
   external T method();
   //         ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 }
 
 class GenericNonJSClass<T> {}
 
+extension ExtensionNative on HtmlElement {
+  external get getter;
+  external set setter(_);
+
+  external static get staticGetter;
+  external static set staticSetter(_);
+
+  external method();
+  external static staticMethod();
+  external optionalParameterMethod([int a, int b = 0]);
+
+  nonExternalMethod() => 1;
+  static nonExternalStaticMethod() => 2;
+}
+
 main() {}
diff --git a/tests/lib_2/js/external_static_test.dart b/tests/lib_2/js/external_static_test.dart
index 3f4c60b7..8084a72 100644
--- a/tests/lib_2/js/external_static_test.dart
+++ b/tests/lib_2/js/external_static_test.dart
@@ -10,6 +10,7 @@
 @JS()
 library external_static_test;
 
+import 'dart:html';
 import 'package:js/js.dart';
 
 // external top level members ok in @JS() library.
@@ -69,32 +70,35 @@
 extension ExtensionNonJS on NonJSClass {
   external get getter;
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external set setter(_);
   //           ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   external static get staticGetter;
   //                  ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static set staticSetter(_);
   //                  ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   external method();
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
   external static staticMethod();
   //              ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
+  external optionalParameterMethod([int a, int b = 0]);
+  //       ^
+  // [web] JS interop or Native class required for 'external' extension members.
   external overridenMethod();
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   @JS('memberAnnotation')
   external annotatedMethod();
   //       ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 
   nonExternalMethod() => 1;
   static nonExternalStaticMethod() => 2;
@@ -107,7 +111,7 @@
 extension ExtensionGenericNonJS<T> on GenericNonJSClass<T> {
   external T method();
   //         ^
-  // [web] JS interop class required for 'external' extension members.
+  // [web] JS interop or Native class required for 'external' extension members.
 }
 
 class GenericNonJSClass<T> {}
@@ -121,6 +125,7 @@
 
   external method();
   external static staticMethod();
+  external optionalParameterMethod([int a, int b = 0]);
 
   @JS('memberAnnotation')
   external annotatedMethod();
@@ -176,4 +181,19 @@
 @JS()
 class _privateJSClass {}
 
+extension ExtensionNative on HtmlElement {
+  external get getter;
+  external set setter(_);
+
+  external static get staticGetter;
+  external static set staticSetter(_);
+
+  external method();
+  external static staticMethod();
+  external optionalParameterMethod([int a, int b = 0]);
+
+  nonExternalMethod() => 1;
+  static nonExternalStaticMethod() => 2;
+}
+
 main() {}
diff --git a/tools/VERSION b/tools/VERSION
index 60bf783..d3f29b9 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 369
+PRERELEASE 370
 PRERELEASE_PATCH 0
\ No newline at end of file