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 2ccb2992d06d8..01dd119547bac 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 @@ const MessageCode messageJsInteropEnclosingClassJSAnnotationContext = message: r"""This is the enclosing class."""); // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. -const Code codeJsInteropExternalExtensionMemberNotOnJSClass = - messageJsInteropExternalExtensionMemberNotOnJSClass; +const Code 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 756793097bc51..975fca74f5541 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 @@ import 'package:_fe_analyzer_shared/src/messages/codes.dart' messageJsInteropAnonymousFactoryPositionalParameters, messageJsInteropEnclosingClassJSAnnotation, messageJsInteropEnclosingClassJSAnnotationContext, - messageJsInteropExternalExtensionMemberNotOnJSClass, + messageJsInteropExternalExtensionMemberOnTypeInvalid, messageJsInteropExternalMemberNotJSAnnotated, messageJsInteropIndexNotSupported, messageJsInteropNamedParameters, @@ -282,13 +282,14 @@ class JsInteropChecks extends RecursiveVisitor { /// [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 @@ class JsInteropChecks extends RecursiveVisitor { /// 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 @@ class JsInteropChecks extends RecursiveVisitor { } 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 7f60edde323de..bd175b9b61c35 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 @@ class AnalysisOptionsGenerator extends YamlCompletionGenerator { 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 aec3305e1963a..af4dab41dc226 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/context/source.dart'; 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 @@ class LibraryAnalyzer { } // 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 a068ad740f686..4387e37a8ae97 100644 --- a/pkg/analyzer/lib/src/dart/ast/utilities.dart +++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart @@ -1306,6 +1306,12 @@ class DeferredLibraryReferenceDetector extends RecursiveAstVisitor { /// /// 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 @@ class LinterExceptionHandler { // 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 0b1635ca08035..327af6ec07abd 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 @@ class LibraryAnalyzer { } // 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 7f04b643fdea1..c6788420e8b19 100644 --- a/pkg/analyzer/lib/src/generated/engine.dart +++ b/pkg/analyzer/lib/src/generated/engine.dart @@ -281,6 +281,10 @@ class AnalysisOptionsImpl implements AnalysisOptions { /// 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 @@ class AnalysisOptionsImpl implements AnalysisOptions { if (options is AnalysisOptionsImpl) { implicitCasts = options.implicitCasts; implicitDynamic = options.implicitDynamic; + propagateLinterExceptions = options.propagateLinterExceptions; strictInference = options.strictInference; strictRawTypes = options.strictRawTypes; } @@ -382,6 +387,7 @@ class AnalysisOptionsImpl implements AnalysisOptions { // 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 25afec451b6a0..0794a4c0de7d0 100644 --- a/pkg/analyzer/lib/src/lint/linter_visitor.dart +++ b/pkg/analyzer/lib/src/lint/linter_visitor.dart @@ -19,7 +19,7 @@ class LinterVisitor extends RecursiveAstVisitor { 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 28b4452e1d038..2efaaa143c435 100644 --- a/pkg/analyzer/lib/src/task/options.dart +++ b/pkg/analyzer/lib/src/task/options.dart @@ -151,6 +151,8 @@ class AnalyzerOptions { /// Ways to say `include`. static const List includeSynonyms = ['include', 'true']; + static const String propagateLinterExceptions = 'propagate-linter-exceptions'; + /// Ways to say `true` or `false`. static const List trueOrFalse = ['true', 'false']; @@ -163,6 +165,7 @@ class AnalyzerOptions { language, optionalChecks, plugins, + propagateLinterExceptions, strong_mode, ]; @@ -805,6 +808,9 @@ class _OptionsProcessor { 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 f0a3fd2da8126..44a7634f67e18 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 @@ environment: ); 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 930fce7eb0653..54fd3b5bcb619 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 @@ import 'package:pedantic/pedantic.dart'; /// 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 @@ class AssetFileSystem implements FileSystem { // 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 @@ class AssetFileSystemEntity implements FileSystemEntity { }); } - /// 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 _runWithClient( Future 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 1cdbd66154381..86a0cc206776d 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 @@ class ExpressionCompilerWorker { /// receive port corresponding to [sendPort]. static Future createAndStart(List 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>(), 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 @@ class ExpressionCompilerWorker { /// /// Completes when the [requestStream] closes and we finish handling the /// requests. - Future start() async { + Future run() async { await for (var request in requestStream) { try { var command = request['command'] as String; @@ -275,6 +281,11 @@ class ExpressionCompilerWorker { _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> _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 94a6543a5b469..ca481dc978b5b 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 @@ FutureOr noisyHandler(Request request) { return Response.internalServerError(); } +int _attempts = 0; FutureOr unreliableHandler(Request request) { final uri = request.requestedUri; final headers = { @@ -74,7 +74,7 @@ FutureOr unreliableHandler(Request request) { ...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 @@ void main() async { tearDownAll(() async { await expectLater(server.close(), completes); + fileSystem.close(); }); test('can tell if file exists', () async { @@ -148,12 +149,10 @@ void main() async { 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 @@ void main() async { tearDownAll(() async { await expectLater(server.close(), completes); + fileSystem.close(); }); test('can tell if file exists', () async { @@ -225,15 +225,12 @@ void main() async { }); 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 @@ void main() async { tearDownAll(() async { await expectLater(server.close(), completes); + fileSystem.close(); }); test('can tell if file exists', () async { @@ -287,12 +285,10 @@ void main() async { 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 @@ void main() async { tearDownAll(() async { await expectLater(server.close(), completes); + fileSystem.close(); }); test('cannot tell if file exists', () async { @@ -341,3 +338,11 @@ void main() async { }); }); } + +// 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 _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 9a06c7717c7c9..c54916bbab6b0 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 @@ abstract class TestDriver { soundNullSafety: soundNullSafety, verbose: verbose, ); - workerDone = worker.start(); + workerDone = worker.run(); } Future 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 ba2bc5527d72f..8eb1f8df4e1dd 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -514,8 +514,8 @@ JsInteropDartClassExtendsJSClass/analyzerCode: Fail # Web compiler specific 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 0873cbade0f43..e94eac1041eb1 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml @@ -4914,8 +4914,8 @@ JsInteropEnclosingClassJSAnnotationContext: 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 7c958cedde1bd..2518fb5fcd315 100644 --- a/pkg/front_end/testcases/strong.status +++ b/pkg/front_end/testcases/strong.status @@ -10,8 +10,6 @@ dart2js/late_statics: SemiFuzzFailure # dartbug.com/45854 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 9f5f24a5ba119..7ea4ce046614e 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 803d18f70e1ee..8256003f5b9a5 100644 --- a/pkg/front_end/testcases/weak.status +++ b/pkg/front_end/testcases/weak.status @@ -13,8 +13,6 @@ dart2js/late_statics: SemiFuzzFailure # dartbug.com/45854 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 d2157f7506989..9905fcc4cfe93 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 @@ class ConstantAllocationCollector extends ConstantVisitor { @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 82bebeccb450a..e964f61c1dea9 100644 --- a/pkg/vm/lib/transformations/type_flow/transformer.dart +++ b/pkg/vm/lib/transformations/type_flow/transformer.dart @@ -1938,6 +1938,11 @@ class _TreeShakerConstantVisitor extends ConstantVisitor { shaker.addUsedMember(constant.target); } + @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 f2c7656312ca0..6040fb124b777 100644 --- a/runtime/vm/compiler/frontend/constant_reader.cc +++ b/runtime/vm/compiler/frontend/constant_reader.cc @@ -353,6 +353,18 @@ InstancePtr ConstantReader::ReadConstantInternal(intptr_t constant_index) { 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 9dd149c899920..257ee0e546aa9 100644 --- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc +++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc @@ -5299,7 +5299,9 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionNode( 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 d35850ea32571..06b2e7efc151d 100644 --- a/runtime/vm/compiler/frontend/kernel_to_il.cc +++ b/runtime/vm/compiler/frontend/kernel_to_il.cc @@ -3425,13 +3425,45 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction( 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 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction( // 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 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction( 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 34249a3fb9b19..0205fe40bba56 100644 --- a/runtime/vm/compiler/frontend/scope_builder.cc +++ b/runtime/vm/compiler/frontend/scope_builder.cc @@ -1452,9 +1452,6 @@ void ScopeBuilder::VisitFunctionType(bool simple) { 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 @@ void ScopeBuilder::VisitTypeParameterType() { 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 3e1f7885e254d..0df5c45f6f229 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -7519,7 +7519,7 @@ FunctionPtr Function::GetOutermostFunction() const { } FunctionPtr Function::implicit_closure_function() const { - if (IsClosureFunction() || IsFactory() || IsDispatcherOrImplicitAccessor() || + if (IsClosureFunction() || IsDispatcherOrImplicitAccessor() || IsFieldInitializer() || IsFfiTrampoline()) { return Function::null(); } @@ -7689,6 +7689,7 @@ void Function::SetForwardingChecks(const Array& checks) const { // 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::New(const FunctionType& signature, 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 @@ FunctionPtr Function::NewClosureFunctionWithKind(UntaggedFunction::Kind kind, : 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 @@ FunctionPtr Function::NewClosureFunction(const String& name, // 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 @@ FunctionPtr Function::NewImplicitClosureFunction(const String& name, 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 @@ FunctionPtr Function::ImplicitClosureFunction() const { 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 @@ FunctionPtr Function::ImplicitClosureFunction() const { 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()); + + 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 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 @@ FunctionPtr Function::ImplicitClosureFunction() const { // 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 @@ FunctionPtr Function::ImplicitClosureFunction() const { 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 @@ FunctionPtr Function::ImplicitClosureFunction() const { // 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 @@ FunctionPtr Function::ImplicitClosureFunction() const { : 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 @@ FunctionPtr Function::ImplicitClosureFunction() const { 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 5f0f6dccb2040..eb0d9e3f6b0b1 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -2882,13 +2882,14 @@ class Function : public Object { 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 @@ class Function : public Object { 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 913d20278f028..87c46150619ac 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 @@ class AnonymousClass { 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 @@ class NonJSClass { extension ExtensionGenericNonJS on GenericNonJSClass { 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 {} +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 0a1c8dbfc071b..a455de087ff77 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 @@ class AnonymousClass { 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 @@ class NonJSClass { extension ExtensionGenericNonJS on GenericNonJSClass { 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 {} @@ -229,4 +230,24 @@ extension ExtensionPrivateJS on _privateJSClass { @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 1de7e61e2bab9..d0fa7dfb6742b 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 @@ class AnonymousClass { 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 @@ class NonJSClass { extension ExtensionGenericNonJS on GenericNonJSClass { 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 {} +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 3f4c60b73e859..8084a7219dbc5 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 @@ class AnonymousClass { 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 @@ class NonJSClass { extension ExtensionGenericNonJS on GenericNonJSClass { 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 {} @@ -121,6 +125,7 @@ extension ExtensionJS on JSClass { external method(); external static staticMethod(); + external optionalParameterMethod([int a, int b = 0]); @JS('memberAnnotation') external annotatedMethod(); @@ -176,4 +181,19 @@ extension ExtensionPrivateJS on _privateJSClass { @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 60bf783b3f306..d3f29b95ef893 100644 --- a/tools/VERSION +++ b/tools/VERSION @@ -27,5 +27,5 @@ CHANNEL dev MAJOR 2 MINOR 14 PATCH 0 -PRERELEASE 369 +PRERELEASE 370 PRERELEASE_PATCH 0 \ No newline at end of file