diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d443f4fe8878..7ecc7c30d2b62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ### Dart2JS +* Removed `--no-defer-class-types` and `--no-new-deferred-split'. + ### Tools #### Dartanalyzer diff --git a/DEPS b/DEPS index 9000961a74208..7d660ce479962 100644 --- a/DEPS +++ b/DEPS @@ -144,7 +144,7 @@ vars = { "source_maps_rev": "53eb92ccfe6e64924054f83038a534b959b12b3e", "source_span_rev": "cc7c4288a83f71ecef3414199947b52a8c112c65", "sse_tag": "e5cf68975e8e87171a3dc297577aa073454a91dc", - "stack_trace_tag": "d3813ca0a77348e0faf0d6af0cc17913e36afa39", + "stack_trace_tag": "a958966148516dfa64e2b54c14492175da5cc8e1", "stagehand_tag": "v3.3.9", "stream_channel_tag": "c446774fd077c9bdbd6235a7aadc661ef60a9727", "string_scanner_rev": "1b63e6e5db5933d7be0a45da6e1129fe00262734", diff --git a/pkg/_fe_analyzer_shared/pubspec.yaml b/pkg/_fe_analyzer_shared/pubspec.yaml index 864c0a67f7114..944063b2f3ceb 100644 --- a/pkg/_fe_analyzer_shared/pubspec.yaml +++ b/pkg/_fe_analyzer_shared/pubspec.yaml @@ -1,5 +1,5 @@ name: _fe_analyzer_shared -version: 9.0.0 +version: 10.0.0 description: Logic that is shared between the front_end and analyzer packages. homepage: https://github.com/dart-lang/sdk/tree/master/pkg/_fe_analyzer_shared diff --git a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart index 2dd8cd2694718..9d762cfc22cc6 100644 --- a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart +++ b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart @@ -224,6 +224,8 @@ class ServerCapabilitiesComputer { language: 'yaml', scheme: 'file', pattern: '**/pubspec.yaml'); final analysisOptionsFile = DocumentFilter( language: 'yaml', scheme: 'file', pattern: '**/analysis_options.yaml'); + final fixDataFile = DocumentFilter( + language: 'yaml', scheme: 'file', pattern: '**/lib/fix_data.yaml'); final pluginTypes = _server.pluginManager.plugins .expand((plugin) => plugin.currentSession?.interestingFiles ?? const []) @@ -243,6 +245,7 @@ class ServerCapabilitiesComputer { ...allTypes, pubspecFile, analysisOptionsFile, + fixDataFile, }.toList(); final registrations = []; diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart index 78b882b4c91a3..66c8c45a770f1 100644 --- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart +++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart @@ -140,6 +140,11 @@ class BulkFixProcessor { LintNames.prefer_conditional_assignment: [ ReplaceWithConditionalAssignment.newInstance, ], + // TODO (pq): can produce results incompatible w/ `unnecessary_const` + // LintNames.prefer_const_constructors: [ + // AddConst.newInstance, + // ReplaceNewWithConst.newInstance, + // ], LintNames.prefer_const_constructors_in_immutables: [ AddConst.newInstance, ], diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart index 260102ce98244..2007991a865f2 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_error_code.dart @@ -15,7 +15,7 @@ class TransformSetErrorCode extends ErrorCode { * 0: the actual type of the value */ static const TransformSetErrorCode invalidValue = TransformSetErrorCode( - 'invalidValue', + 'invalid_value', "The value of '{0}' should be of type '{1}' but is of type '{2}'."); /** @@ -23,34 +23,34 @@ class TransformSetErrorCode extends ErrorCode { * 0: the missing key */ static const TransformSetErrorCode missingKey = - TransformSetErrorCode('missingKey', "Missing the required key '{0}'."); + TransformSetErrorCode('missing_key', "Missing the required key '{0}'."); /** * No parameters. */ static const TransformSetErrorCode missingTemplateEnd = TransformSetErrorCode( - 'missingTemplateEnd', "Missing the end brace for the template."); + 'missing_template_end', "Missing the end brace for the template."); /** * Parameters: * 0: the missing key */ static const TransformSetErrorCode undefinedVariable = TransformSetErrorCode( - 'undefinedVariable', "The variable '{0}' is not defined."); + 'undefined_variable', "The variable '{0}' is not defined."); /** * Parameters: * 0: the unsupported key */ - static const TransformSetErrorCode unsupportedKey = - TransformSetErrorCode('unsupportedKey', "The key '{0}' isn't supported."); + static const TransformSetErrorCode unsupportedKey = TransformSetErrorCode( + 'unsupported_key', "The key '{0}' isn't supported."); /** * Parameters: * 0: the message produced by the YAML parser */ static const TransformSetErrorCode yamlSyntaxError = - TransformSetErrorCode('yamlSyntaxError', "Parse error: {0}"); + TransformSetErrorCode('yaml_syntax_error', "Parse error: {0}"); /// Initialize a newly created error code. const TransformSetErrorCode(String name, String message, diff --git a/pkg/analysis_server/test/lsp/diagnostic_test.dart b/pkg/analysis_server/test/lsp/diagnostic_test.dart index f88804efa95d2..6364bc08fd8c7 100644 --- a/pkg/analysis_server/test/lsp/diagnostic_test.dart +++ b/pkg/analysis_server/test/lsp/diagnostic_test.dart @@ -250,6 +250,21 @@ void f() { expect(diagnostics, isNull); } + Future test_fixDataFile() async { + var fixDataPath = join(projectFolderPath, 'lib', 'fix_data.yaml'); + var fixDataUri = Uri.file(fixDataPath); + newFile(fixDataPath, content: ''' +version: latest +''').path; + + final firstDiagnosticsUpdate = waitForDiagnostics(fixDataUri); + await initialize(); + final initialDiagnostics = await firstDiagnosticsUpdate; + expect(initialDiagnostics, hasLength(1)); + expect(initialDiagnostics.first.severity, DiagnosticSeverity.Error); + expect(initialDiagnostics.first.code, 'invalid_value'); + } + Future test_fromPlugins_dartFile() async { await checkPluginErrorsForFile(mainFilePath); } diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/add_const_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/add_const_test.dart index 7962f4f9f045b..735e1c5aee098 100644 --- a/pkg/analysis_server/test/src/services/correction/fix/bulk/add_const_test.dart +++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/add_const_test.dart @@ -9,10 +9,36 @@ import 'bulk_fix_processor.dart'; void main() { defineReflectiveSuite(() { + defineReflectiveTests(AddConstToConstructorTest); defineReflectiveTests(AddConstToImmutableConstructorTest); }); } +@reflectiveTest +class AddConstToConstructorTest extends BulkFixProcessorTest { + @override + String get lintCode => LintNames.prefer_const_constructors; + + /// Disabled in BulkFixProcessor. + @failingTest + Future test_singleFile() async { + addMetaPackage(); + await resolveTestUnit(r''' +class C { + const C([C c]); +} +var c = C(C()); +'''); + // TODO (pq): results are incompatible w/ `unnecessary_const` + await assertHasFix(r''' +class C { + const C([C c]); +} +var c = const C(const C()); +'''); + } +} + @reflectiveTest class AddConstToImmutableConstructorTest extends BulkFixProcessorTest { @override diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/replace_new_with_const_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/replace_new_with_const_test.dart new file mode 100644 index 0000000000000..a6c5170be12b3 --- /dev/null +++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/replace_new_with_const_test.dart @@ -0,0 +1,41 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:analysis_server/src/services/linter/lint_names.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import 'bulk_fix_processor.dart'; + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(ReplaceNewWithConstTest); + }); +} + +@reflectiveTest +class ReplaceNewWithConstTest extends BulkFixProcessorTest { + @override + String get lintCode => LintNames.prefer_const_constructors; + + /// Disabled in BulkFixProcessor. + @failingTest + Future test_singleFile() async { + await resolveTestUnit(r''' +class C { + const C(); +} +main() { + print('${new C()} ${new C()}'); +} +'''); + await assertHasFix(r''' +class C { + const C(); +} +main() { + print('${const C()} ${const C()}'); +} +'''); + } +} diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart index fd34894e28485..5fb51b9cb1976 100644 --- a/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart +++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart @@ -49,6 +49,7 @@ import 'remove_unnecessary_new_test.dart' as remove_unnecessary_new; import 'rename_to_camel_case_test.dart' as rename_to_camel_case; import 'replace_colon_with_equals_test.dart' as replace_colon_with_equals; import 'replace_final_with_const_test.dart' as replace_final_with_const; +import 'replace_new_with_const_test.dart' as replace_new_with_const; import 'replace_null_with_closure_test.dart' as replace_null_with_closure; import 'replace_with_conditional_assignment_test.dart' as replace_with_conditional_assignment; @@ -102,6 +103,7 @@ void main() { replace_with_conditional_assignment.main(); replace_colon_with_equals.main(); replace_final_with_const.main(); + replace_new_with_const.main(); replace_null_with_closure.main(); replace_with_is_empty.main(); replace_with_tear_off.main(); diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md index 7353b55e48095..0811da9e4a83d 100644 --- a/pkg/analyzer/CHANGELOG.md +++ b/pkg/analyzer/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.40.3 +* Updated the current language version to `2.11`. +* Bug fixes: 43541, 27896, 28066, 28066, 43497, 43478, 28066, 43465, + 43462, 43439, 43162, 43397, 43200. + ## 0.40.2 * Require `meta: ^1.2.3`. diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart index 57401d2f6a22f..8f36687e85422 100644 --- a/pkg/analyzer/lib/src/dart/ast/ast.dart +++ b/pkg/analyzer/lib/src/dart/ast/ast.dart @@ -8092,8 +8092,7 @@ class PostfixExpressionImpl extends ExpressionImpl } @override - bool _extendsNullShorting(Expression child) => - operator.type != TokenType.BANG && identical(child, operand); + bool _extendsNullShorting(Expression child) => identical(child, operand); } /// An identifier that is prefixed or an access to an object property where the diff --git a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart index c22c1e45c02fe..1b4b7a048b578 100644 --- a/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart +++ b/pkg/analyzer/lib/src/dart/resolver/invocation_inference_helper.dart @@ -9,7 +9,6 @@ import 'package:analyzer/error/listener.dart'; import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/element/type.dart'; import 'package:analyzer/src/dart/element/type_algebra.dart'; -import 'package:analyzer/src/dart/element/type_provider.dart'; import 'package:analyzer/src/dart/element/type_system.dart'; import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart'; import 'package:analyzer/src/error/codes.dart'; @@ -22,7 +21,6 @@ class InvocationInferenceHelper { final ErrorReporter _errorReporter; final FlowAnalysisHelper _flowAnalysis; final TypeSystemImpl _typeSystem; - final TypeProviderImpl _typeProvider; final MigrationResolutionHooks _migrationResolutionHooks; List _typeArgumentTypes; @@ -37,7 +35,6 @@ class InvocationInferenceHelper { : _resolver = resolver, _errorReporter = errorReporter, _typeSystem = typeSystem, - _typeProvider = typeSystem.typeProvider, _flowAnalysis = flowAnalysis, _migrationResolutionHooks = migrationResolutionHooks; @@ -157,45 +154,6 @@ class InvocationInferenceHelper { return null; } - /// Given a method invocation [node], attempt to infer a better - /// type for the result if the target is dynamic and the method - /// being called is one of the object methods. - bool inferMethodInvocationObject(MethodInvocation node) { - // If we have a call like `toString()` or `libraryPrefix.toString()`, don't - // infer it. - Expression target = node.realTarget; - if (target == null || - target is SimpleIdentifier && target.staticElement is PrefixElement) { - return false; - } - DartType nodeType = node.staticInvokeType; - if (nodeType == null || - !nodeType.isDynamic || - node.argumentList.arguments.isNotEmpty) { - return false; - } - // Object methods called on dynamic targets can have their types improved. - String name = node.methodName.name; - MethodElement inferredElement = - _typeProvider.objectType.element.getMethod(name); - if (inferredElement == null || inferredElement.isStatic) { - return false; - } - inferredElement = _resolver.toLegacyElement(inferredElement); - DartType inferredType = inferredElement.type; - if (inferredType is FunctionType) { - DartType returnType = inferredType.returnType; - if (inferredType.parameters.isEmpty && - returnType is InterfaceType && - _typeProvider.nonSubtypableClasses.contains(returnType.element)) { - node.staticInvokeType = inferredType; - recordStaticType(node, inferredType.returnType); - return true; - } - } - return false; - } - /// Given an uninstantiated generic function type, referenced by the /// [identifier] in the tear-off [expression], try to infer the instantiated /// generic function type from the surrounding context. diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart index 951bde71f0561..919f54d8df97a 100644 --- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart @@ -150,7 +150,7 @@ class MethodInvocationResolver { } if (receiverType is DynamicTypeImpl) { - _resolveReceiverDynamic(node, name); + _resolveReceiverDynamic(node); return; } @@ -291,15 +291,10 @@ class MethodInvocationResolver { node.methodName.staticType, ); - // TODO(scheglov) Call this only when member lookup failed? - var inferred = _inferenceHelper.inferMethodInvocationObject(node); - - if (!inferred) { - DartType staticStaticType = _inferenceHelper.computeInvokeReturnType( - node.staticInvokeType, - ); - _inferenceHelper.recordStaticType(node, staticStaticType); - } + DartType staticStaticType = _inferenceHelper.computeInvokeReturnType( + node.staticInvokeType, + ); + _inferenceHelper.recordStaticType(node, staticStaticType); } /// Given that we are accessing a property of the given [classElement] with the @@ -387,8 +382,33 @@ class MethodInvocationResolver { _setResolution(node, member.type); } - void _resolveReceiverDynamic(MethodInvocation node, String name) { - _setDynamicResolution(node); + void _resolveReceiverDynamic(MethodInvocationImpl node) { + var nameNode = node.methodName; + + var objectElement = _typeSystem.typeProvider.objectElement; + var target = objectElement.getMethod(nameNode.name); + + var hasMatchingObjectMethod = false; + if (target is MethodElement) { + var arguments = node.argumentList.arguments; + hasMatchingObjectMethod = arguments.length == target.parameters.length && + !arguments.any((e) => e is NamedExpression); + if (hasMatchingObjectMethod) { + target = _resolver.toLegacyElement(target); + nameNode.staticElement = target; + node.staticInvokeType = target.type; + node.staticType = target.returnType; + } + } + + if (!hasMatchingObjectMethod) { + nameNode.staticType = DynamicTypeImpl.instance; + node.staticInvokeType = DynamicTypeImpl.instance; + node.staticType = DynamicTypeImpl.instance; + } + + _setExplicitTypeArgumentTypes(); + node.argumentList.accept(_resolver); } void _resolveReceiverFunctionType(MethodInvocation node, Expression receiver, diff --git a/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart index 30df9bf01957f..c5a5ea2ea8bdf 100644 --- a/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart +++ b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart @@ -232,6 +232,7 @@ class PostfixExpressionResolver { var type = _typeSystem.promoteToNonNull(operandType); _inferenceHelper.recordStaticType(node, type); + _resolver.nullShortingTermination(node); _flowAnalysis?.flow?.nonNullAssert_end(operand); } } diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml index 0cff7cf892d70..9bfb00ad45d7e 100644 --- a/pkg/analyzer/pubspec.yaml +++ b/pkg/analyzer/pubspec.yaml @@ -1,5 +1,5 @@ name: analyzer -version: 0.40.2 +version: 0.40.3 description: This package provides a library that performs static analysis of Dart code. homepage: https://github.com/dart-lang/sdk/tree/master/pkg/analyzer @@ -7,7 +7,7 @@ environment: sdk: '>=2.7.0 <3.0.0' dependencies: - _fe_analyzer_shared: ^9.0.0 + _fe_analyzer_shared: ^10.0.0 args: ^1.0.0 charcode: ^1.1.2 cli_util: '>=0.1.4 <0.3.0' diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart index 0f36155d59c34..d83065c072ae3 100644 --- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart @@ -1165,11 +1165,12 @@ main() { ''', [ error(CompileTimeErrorCode.USE_OF_VOID_RESULT, 23, 3), ]); - // TODO(scheglov) Resolve fully, or don't resolve at all. - assertMethodInvocation( + assertMethodInvocation2( findNode.methodInvocation('toString()'), - null, - 'String Function()', + element: null, + typeArgumentTypes: [], + invokeType: 'dynamic', + type: 'dynamic', ); } @@ -1182,16 +1183,16 @@ main() { ''', [ error(CompileTimeErrorCode.USE_OF_VOID_RESULT, 23, 3), ]); - // TODO(scheglov) Resolve fully, or don't resolve at all. - assertMethodInvocation( + assertMethodInvocation2( findNode.methodInvocation('toString()'), - null, - 'String Function()', + element: null, + typeArgumentTypes: [], + invokeType: 'dynamic', + type: 'dynamic', ); } test_error_useOfVoidResult_receiver_withNull() async { - var question = typeToStringWithNullability ? '?' : ''; await assertErrorsInCode(r''' main() { void foo; @@ -1200,12 +1201,12 @@ main() { ''', [ error(CompileTimeErrorCode.USE_OF_VOID_RESULT, 23, 3), ]); - // TODO(scheglov) Resolve fully, or don't resolve at all. - assertMethodInvocation( + assertMethodInvocation2( findNode.methodInvocation('toString()'), - null, - 'String Function()', - expectedType: 'String$question', + element: null, + typeArgumentTypes: [], + invokeType: 'dynamic', + type: 'dynamic', ); } @@ -1944,14 +1945,39 @@ main() { assertType(foo, 'void Function(int)'); } - test_objectMethodOnDynamic() async { + test_objectMethodOnDynamic_argumentsDontMatch() async { await assertNoErrorsInCode(r''' -main() { - var v; - v.toString(42); +void f(a, int b) { + a.toString(b); +} +'''); + assertMethodInvocation2( + findNode.methodInvocation('toString(b)'), + element: null, + typeArgumentTypes: [], + invokeType: 'dynamic', + type: 'dynamic', + ); + + assertType(findNode.simple('b);'), 'int'); + } + + test_objectMethodOnDynamic_argumentsMatch() async { + await assertNoErrorsInCode(r''' +void f(a) { + a.toString(); } '''); - _assertUnresolvedMethodInvocation('toString(42);'); + assertMethodInvocation2( + findNode.methodInvocation('toString()'), + element: elementMatcher( + objectElement.getMethod('toString'), + isLegacy: isNullSafetySdkAndLegacyLibrary, + ), + typeArgumentTypes: [], + invokeType: 'String Function()', + type: 'String', + ); } test_objectMethodOnFunction() async { diff --git a/pkg/analyzer/test/src/dart/resolution/postfix_expression_test.dart b/pkg/analyzer/test/src/dart/resolution/postfix_expression_test.dart index c4838ab3998ec..180d1e52ee7e6 100644 --- a/pkg/analyzer/test/src/dart/resolution/postfix_expression_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/postfix_expression_test.dart @@ -641,6 +641,67 @@ int g() => f(null)!; ); } + /// See https://github.com/dart-lang/language/issues/1163 + test_nullCheck_participatesNullShorting() async { + await assertErrorsInCode(''' +class A { + int zero; + int? zeroOrNull; + + A(this.zero, [this.zeroOrNull]); +} + +void test1(A? a) => a?.zero!; +void test2(A? a) => a?.zeroOrNull!; +void test3(A? a) => a?.zero!.isEven; +void test4(A? a) => a?.zeroOrNull!.isEven; + +class Foo { + Bar? bar; + + Foo(this.bar); + + Bar? operator [](int? index) => null; +} + +class Bar { + int baz; + + Bar(this.baz); + + int operator [](int index) => index; +} + +void test5(Foo? foo) => foo?.bar!; +void test6(Foo? foo) => foo?.bar!.baz; +void test7(Foo? foo, int a) => foo?.bar![a]; +void test8(Foo? foo, int? a) => foo?[a]!; +void test9(Foo? foo, int? a) => foo?[a]!.baz; +void test10(Foo? foo, int? a, int b) => foo?[a]![b]; +''', [ + error(StaticWarningCode.UNNECESSARY_NON_NULL_ASSERTION, 107, 1), + error(StaticWarningCode.UNNECESSARY_NON_NULL_ASSERTION, 173, 1), + ]); + + void assertTestType(int index, String expected) { + var function = findNode.functionDeclaration('test$index('); + var body = function.functionExpression.body as ExpressionFunctionBody; + assertType(body.expression, expected); + } + + assertTestType(1, 'int?'); + assertTestType(2, 'int?'); + assertTestType(3, 'bool?'); + assertTestType(4, 'bool?'); + + assertTestType(5, 'Bar?'); + assertTestType(6, 'int?'); + assertTestType(7, 'int?'); + assertTestType(8, 'Bar?'); + assertTestType(9, 'int?'); + assertTestType(10, 'int?'); + } + test_nullCheck_superExpression() async { await assertErrorsInCode(r''' class A { diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart index ace1d4cbf5a01..1c52968d8a02a 100644 --- a/pkg/compiler/lib/src/commandline_options.dart +++ b/pkg/compiler/lib/src/commandline_options.dart @@ -113,13 +113,6 @@ class Flags { static const String soundNullSafety = '--sound-null-safety'; static const String noSoundNullSafety = '--no-sound-null-safety'; - static const String newDeferredSplit = '--new-deferred-split'; - static const String noNewDeferredSplit = '--no-new-deferred-split'; - static const String reportInvalidInferredDeferredTypes = - '--report-invalid-deferred-types'; - static const String deferClassTypes = '--defer-class-types'; - static const String noDeferClassTypes = '--no-defer-class-types'; - /// Flag for a combination of flags for 'production' mode. static const String benchmarkingProduction = '--benchmarking-production'; diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart index 98f2f79871f62..3bd43ed2b3fdf 100644 --- a/pkg/compiler/lib/src/dart2js.dart +++ b/pkg/compiler/lib/src/dart2js.dart @@ -468,10 +468,6 @@ Future compile(List argv, new OptionHandler(Flags.disableRtiOptimization, passThrough), new OptionHandler(Flags.terse, passThrough), new OptionHandler('--deferred-map=.+', passThrough), - new OptionHandler(Flags.newDeferredSplit, passThrough), - new OptionHandler(Flags.reportInvalidInferredDeferredTypes, passThrough), - new OptionHandler(Flags.deferClassTypes, passThrough), - new OptionHandler(Flags.noDeferClassTypes, passThrough), new OptionHandler('${Flags.dumpInfo}|${Flags.dumpInfo}=.+', setDumpInfo), new OptionHandler('--disallow-unsafe-eval', ignoreOption), new OptionHandler(Option.showPackageWarnings, passThrough), diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart index d47da8e6d255f..364fe9a30aca1 100644 --- a/pkg/compiler/lib/src/deferred_load.dart +++ b/pkg/compiler/lib/src/deferred_load.dart @@ -7,6 +7,8 @@ library deferred_load; import 'dart:collection' show Queue; import 'package:front_end/src/api_unstable/dart2js.dart' as fe; +import 'package:kernel/ast.dart' as ir; +import 'package:kernel/type_environment.dart' as ir; import 'common/metrics.dart' show Metric, Metrics, CountMetric, DurationMetric; import 'common/tasks.dart' show CompilerTask; @@ -18,12 +20,13 @@ import 'constants/values.dart' show ConstantValue, ConstructedConstantValue, - TypeConstantValue, DeferredGlobalConstantValue, InstantiationConstantValue; import 'elements/types.dart'; import 'elements/entities.dart'; +import 'ir/util.dart'; import 'kernel/kelements.dart' show KLocalFunction; +import 'kernel/element_map.dart'; import 'serialization/serialization.dart'; import 'options.dart'; import 'universe/use.dart'; @@ -102,7 +105,7 @@ class _DeferredLoadTaskMetrics implements Metrics { /// For each deferred import, find elements and constants to be loaded when that /// import is loaded. Elements that are used by several deferred imports are in /// shared OutputUnits. -abstract class DeferredLoadTask extends CompilerTask { +class DeferredLoadTask extends CompilerTask { @override String get name => 'Deferred Loading'; @@ -149,17 +152,15 @@ abstract class DeferredLoadTask extends CompilerTask { final Compiler compiler; + KernelToElementMap _elementMap; + @override final _DeferredLoadTaskMetrics metrics = _DeferredLoadTaskMetrics(); bool get disableProgramSplit => compiler.options.disableProgramSplit; - bool get newDeferredSplit => compiler.options.newDeferredSplit; - bool get reportInvalidInferredDeferredTypes => - compiler.options.reportInvalidInferredDeferredTypes; - bool get deferClassTypes => compiler.options.deferClassTypes; - DeferredLoadTask(this.compiler) : super(compiler.measurer) { - _mainOutputUnit = OutputUnit(true, 'main', {}); + DeferredLoadTask(this.compiler, this._elementMap) : super(compiler.measurer) { + _mainOutputUnit = OutputUnit(true, 'main', {}); importSets.mainSet.unit = _mainOutputUnit; _allOutputUnits.add(_mainOutputUnit); } @@ -172,27 +173,6 @@ abstract class DeferredLoadTask extends CompilerTask { DiagnosticReporter get reporter => compiler.reporter; - /// Given [imports] that refer to an element from a library, determine whether - /// the element is explicitly deferred. - static bool _isExplicitlyDeferred(Iterable imports) { - // If the element is not imported explicitly, it is implicitly imported - // not deferred. - if (imports.isEmpty) return false; - // An element could potentially be loaded by several imports. If all of them - // is explicitly deferred, we say the element is explicitly deferred. - // TODO(sigurdm): We might want to give a warning if the imports do not - // agree. - return imports.every((ImportEntity import) => import.isDeferred); - } - - /// Returns every [ImportEntity] that imports [element] into [library]. - Iterable classImportsTo( - ClassEntity element, LibraryEntity library); - - /// Returns every [ImportEntity] that imports [element] into [library]. - Iterable memberImportsTo( - MemberEntity element, LibraryEntity library); - /// Collects all direct dependencies of [element]. /// /// The collected dependent elements and constants are are added to @@ -275,30 +255,37 @@ abstract class DeferredLoadTask extends CompilerTask { // they are processed as part of the class. } - /// Extract the set of constants that are used in annotations of [element]. - /// - /// If the underlying system doesn't support mirrors, then no constants are - /// added. - void collectConstantsFromMetadata( - Entity element, Set constants); - /// Extract the set of constants that are used in the body of [element]. - void collectConstantsInBody(MemberEntity element, Dependencies dependencies); + void collectConstantsInBody(MemberEntity element, Dependencies dependencies) { + ir.Member node = _elementMap.getMemberNode(element); + + // Fetch the internal node in order to skip annotations on the member. + // TODO(sigmund): replace this pattern when the kernel-ast provides a better + // way to skip annotations (issue 31565). + var visitor = new ConstantCollector( + _elementMap, _elementMap.getStaticTypeContext(element), dependencies); + if (node is ir.Field) { + node.initializer?.accept(visitor); + return; + } + + if (node is ir.Constructor) { + node.initializers.forEach((i) => i.accept(visitor)); + } + node.function?.accept(visitor); + } /// Recursively collects all the dependencies of [type]. void _collectTypeDependencies(DartType type, Dependencies dependencies, [ImportEntity import]) { - TypeDependencyVisitor(dependencies, import, commonElements, - collectClassesAndTypes: !deferClassTypes) - .visit(type); + TypeDependencyVisitor(dependencies, import, commonElements).visit(type); } void _collectTypeArgumentDependencies( Iterable typeArguments, Dependencies dependencies, [ImportEntity import]) { if (typeArguments == null) return; - TypeDependencyVisitor(dependencies, import, commonElements, - collectClassesAndTypes: !deferClassTypes) + TypeDependencyVisitor(dependencies, import, commonElements) .visitList(typeArguments); } @@ -600,62 +587,6 @@ abstract class DeferredLoadTask extends CompilerTask { /// same nodes we have already seen. _shouldAddDeferredDependency(ImportSet newSet) => newSet.length <= 1; - void _fixDependencyInfo(DependencyInfo info, List imports, - String prefix, String name, Spannable context) { - var isDeferred = _isExplicitlyDeferred(imports); - if (isDeferred) { - if (!newDeferredSplit) { - info.isDeferred = true; - info.imports = imports; - } - if (reportInvalidInferredDeferredTypes) { - reporter.reportErrorMessage(context, MessageKind.GENERIC, { - 'text': "$prefix '$name' is deferred but appears to be inferred as" - " a return type or a type parameter (dartbug.com/35311)." - }); - } - } - } - - // The following 3 methods are used to check whether the new deferred split - // algorithm and the old one match. Because of a soundness bug in the old - // algorithm the new algorithm can pull in a lot of code to the main output - // unit. This logic detects it and will make it easier for us to migrate code - // off it incrementally. - // Note: we only expect discrepancies on class-dependency-info due to how - // inferred types expose deferred types in type-variables and return types - // (Issue #35311). We added the other two methods to test our transition, but - // we don't expect to detect any mismatches there. - // - // TODO(sigmund): delete once the new implementation is on by default. - void _fixClassDependencyInfo(DependencyInfo info, ClassEntity cls, - LibraryEntity library, Spannable context) { - if (info.isDeferred) return; - if (newDeferredSplit && !reportInvalidInferredDeferredTypes) return; - var imports = classImportsTo(cls, library); - _fixDependencyInfo(info, imports, "Class", cls.name, context); - } - - void _fixMemberDependencyInfo(DependencyInfo info, MemberEntity member, - LibraryEntity library, Spannable context) { - if (info.isDeferred || compiler.options.newDeferredSplit) return; - var imports = memberImportsTo(member, library); - _fixDependencyInfo(info, imports, "Member", member.name, context); - } - - void _fixConstantDependencyInfo(DependencyInfo info, ConstantValue constant, - LibraryEntity library, Spannable context) { - if (info.isDeferred || compiler.options.newDeferredSplit) return; - if (constant is TypeConstantValue) { - var type = constant.representedType.withoutNullability; - if (type is InterfaceType) { - var imports = classImportsTo(type.element, library); - _fixDependencyInfo( - info, imports, "Class (in constant) ", type.element.name, context); - } - } - } - void _processDependencies( KClosedWorld closedWorld, LibraryEntity library, @@ -665,7 +596,6 @@ abstract class DeferredLoadTask extends CompilerTask { WorkQueue queue, Spannable context) { dependencies.classes.forEach((ClassEntity cls, DependencyInfo info) { - _fixClassDependencyInfo(info, cls, library, context); if (info.isDeferred) { if (_shouldAddDeferredDependency(newSet)) { for (ImportEntity deferredImport in info.imports) { @@ -678,7 +608,6 @@ abstract class DeferredLoadTask extends CompilerTask { }); dependencies.classType.forEach((ClassEntity cls, DependencyInfo info) { - _fixClassDependencyInfo(info, cls, library, context); if (info.isDeferred) { if (_shouldAddDeferredDependency(newSet)) { for (ImportEntity deferredImport in info.imports) { @@ -691,7 +620,6 @@ abstract class DeferredLoadTask extends CompilerTask { }); dependencies.members.forEach((MemberEntity member, DependencyInfo info) { - _fixMemberDependencyInfo(info, member, library, context); if (info.isDeferred) { if (_shouldAddDeferredDependency(newSet)) { for (ImportEntity deferredImport in info.imports) { @@ -709,7 +637,6 @@ abstract class DeferredLoadTask extends CompilerTask { dependencies.constants .forEach((ConstantValue constant, DependencyInfo info) { - _fixConstantDependencyInfo(info, constant, library, context); if (info.isDeferred) { if (_shouldAddDeferredDependency(newSet)) { for (ImportEntity deferredImport in info.imports) { @@ -948,10 +875,8 @@ abstract class DeferredLoadTask extends CompilerTask { _localFunctionToSet = null; _constantToSet = null; importSets = null; - cleanup(); return OutputUnitData( this.isProgramSplit && !disableProgramSplit, - deferClassTypes, this._mainOutputUnit, classMap, classTypeMap, @@ -964,15 +889,11 @@ abstract class DeferredLoadTask extends CompilerTask { _deferredImportDescriptions); } - /// Frees up strategy-specific temporary data. - void cleanup() {} - void beforeResolution(Uri rootLibraryUri, Iterable libraries) { measureSubtask('prepare', () { for (Uri uri in libraries) { LibraryEntity library = elementEnvironment.lookupLibrary(uri); reporter.withCurrentElement(library, () { - checkForDeferredErrorCases(library); for (ImportEntity import in elementEnvironment.getImports(library)) { if (import.isDeferred) { _deferredImportDescriptions[import] = @@ -985,13 +906,6 @@ abstract class DeferredLoadTask extends CompilerTask { }); } - /// Detects errors like duplicate uses of a prefix or using the old deferred - /// loading syntax. - /// - /// These checks are already done by the shared front-end, so they can be - /// skipped by the new compiler pipeline. - void checkForDeferredErrorCases(LibraryEntity library); - bool ignoreEntityInDump(Entity element) => false; /// Creates a textual representation of the output unit content. @@ -1014,7 +928,7 @@ abstract class DeferredLoadTask extends CompilerTask { }); _memberToSet.forEach((MemberEntity element, ImportSet importSet) { if (ignoreEntityInDump(element)) return; - var elements = elementMap.putIfAbsent(importSet.unit, () => []); + var elements = elementMap.putIfAbsent(importSet.unit, () => []); var id = element.name ?? '$element'; var cls = element.enclosingClass?.name; if (cls != null) id = '$cls.$id'; @@ -1024,7 +938,7 @@ abstract class DeferredLoadTask extends CompilerTask { }); _localFunctionToSet.forEach((Local element, ImportSet importSet) { if (ignoreEntityInDump(element)) return; - var elements = elementMap.putIfAbsent(importSet.unit, () => []); + var elements = elementMap.putIfAbsent(importSet.unit, () => []); var id = element.name ?? '$element'; var context = (element as dynamic).memberContext.name; id = element.name == null || element.name == '' ? '' : id; @@ -1037,7 +951,7 @@ abstract class DeferredLoadTask extends CompilerTask { // if they are shared, they end up duplicated anyways across output units. if (value.isPrimitive) return; constantMap - .putIfAbsent(importSet.unit, () => []) + .putIfAbsent(importSet.unit, () => []) .add(value.toStructuredText(dartTypes)); }); @@ -1464,7 +1378,6 @@ class OutputUnitData { static const String tag = 'output-unit-data'; final bool isProgramSplit; - final bool deferClassTypes; final OutputUnit mainOutputUnit; final Map _classToUnit; final Map _classTypeToUnit; @@ -1489,7 +1402,6 @@ class OutputUnitData { OutputUnitData( this.isProgramSplit, - this.deferClassTypes, this.mainOutputUnit, this._classToUnit, this._classTypeToUnit, @@ -1532,13 +1444,12 @@ class OutputUnitData { return OutputUnitData( other.isProgramSplit, - other.deferClassTypes, other.mainOutputUnit, classToUnit, classTypeToUnit, memberToUnit, // Local functions only make sense in the K-world model. - const {}, + const {}, constantToUnit, other.outputUnits, other._importDeferName, @@ -1550,7 +1461,6 @@ class OutputUnitData { factory OutputUnitData.readFromDataSource(DataSource source) { source.begin(tag); bool isProgramSplit = source.readBool(); - bool deferClassTypes = source.readBool(); List outputUnits = source.readList(() { bool isMainOutput = source.readBool(); String name = source.readString(); @@ -1589,13 +1499,12 @@ class OutputUnitData { source.end(tag); return OutputUnitData( isProgramSplit, - deferClassTypes, mainOutputUnit, classToUnit, classTypeToUnit, memberToUnit, // Local functions only make sense in the K-world model. - const {}, + const {}, constantToUnit, outputUnits, importDeferName, @@ -1607,7 +1516,6 @@ class OutputUnitData { void writeToDataSink(DataSink sink) { sink.begin(tag); sink.writeBool(isProgramSplit); - sink.writeBool(deferClassTypes); Map outputUnitIndices = {}; sink.writeList(outputUnits, (OutputUnit outputUnit) { outputUnitIndices[outputUnit] = outputUnitIndices.length; @@ -1661,18 +1569,13 @@ class OutputUnitData { // TODO(joshualitt): see above TODO regarding allowNull. OutputUnit outputUnitForClassType(ClassEntity cls, {bool allowNull: false}) { if (!isProgramSplit) return mainOutputUnit; - OutputUnit unit; - if (deferClassTypes) { - unit = _classTypeToUnit[cls]; - } else { - unit = _classToUnit[cls]; - } + OutputUnit unit = _classTypeToUnit[cls]; assert(allowNull || unit != null, 'No output unit for type $cls'); return unit ?? mainOutputUnit; } OutputUnit outputUnitForClassTypeForTesting(ClassEntity cls) => - deferClassTypes ? _classTypeToUnit[cls] : _classToUnit[cls]; + _classTypeToUnit[cls]; /// Returns the [OutputUnit] where [member] belongs. OutputUnit outputUnitForMember(MemberEntity member) { @@ -1801,10 +1704,8 @@ class OutputUnitData { Map libraryMap = mapping.putIfAbsent( description.importingUri, - () => { - "name": getName(description._importingLibrary), - "imports": >{} - }); + () => + {"name": getName(description._importingLibrary), "imports": {}}); List partFileNames = outputUnits .where((outputUnit) => !omittedUnits.contains(outputUnit)) @@ -1874,16 +1775,11 @@ class DependencyInfo { } class TypeDependencyVisitor implements DartTypeVisitor { - // If true, collect classes and types, otherwise just collect types. - // Note: When collecting classes, types are added implicitly by the - // dependencies class. - final bool collectClassesAndTypes; final Dependencies _dependencies; final ImportEntity _import; final CommonElements _commonElements; - TypeDependencyVisitor(this._dependencies, this._import, this._commonElements, - {this.collectClassesAndTypes}); + TypeDependencyVisitor(this._dependencies, this._import, this._commonElements); @override void visit(DartType type, [_]) { @@ -1906,11 +1802,7 @@ class TypeDependencyVisitor implements DartTypeVisitor { @override void visitFutureOrType(FutureOrType type, Null argument) { - if (collectClassesAndTypes) { - _dependencies.addClass(_commonElements.futureClass); - } else { - _dependencies.addClassType(_commonElements.futureClass); - } + _dependencies.addClassType(_commonElements.futureClass); visit(type.typeArgument); } @@ -1937,11 +1829,7 @@ class TypeDependencyVisitor implements DartTypeVisitor { @override void visitInterfaceType(InterfaceType type, Null argument) { visitList(type.typeArguments); - if (collectClassesAndTypes) { - _dependencies.addClass(type.element, _import); - } else { - _dependencies.addClassType(type.element, _import); - } + _dependencies.addClassType(type.element, _import); } @override @@ -1970,3 +1858,108 @@ class TypeDependencyVisitor implements DartTypeVisitor { // Nothing to add. } } + +class ConstantCollector extends ir.RecursiveVisitor { + final KernelToElementMap elementMap; + final Dependencies dependencies; + final ir.StaticTypeContext staticTypeContext; + + ConstantCollector(this.elementMap, this.staticTypeContext, this.dependencies); + + CommonElements get commonElements => elementMap.commonElements; + + void add(ir.Expression node, {bool required: true}) { + ConstantValue constant = elementMap + .getConstantValue(staticTypeContext, node, requireConstant: required); + if (constant != null) { + dependencies.addConstant( + constant, elementMap.getImport(getDeferredImport(node))); + } + } + + @override + void visitIntLiteral(ir.IntLiteral literal) {} + + @override + void visitDoubleLiteral(ir.DoubleLiteral literal) {} + + @override + void visitBoolLiteral(ir.BoolLiteral literal) {} + + @override + void visitStringLiteral(ir.StringLiteral literal) {} + + @override + void visitSymbolLiteral(ir.SymbolLiteral literal) => add(literal); + + @override + void visitNullLiteral(ir.NullLiteral literal) {} + + @override + void visitListLiteral(ir.ListLiteral literal) { + if (literal.isConst) { + add(literal); + } else { + super.visitListLiteral(literal); + } + } + + @override + void visitSetLiteral(ir.SetLiteral literal) { + if (literal.isConst) { + add(literal); + } else { + super.visitSetLiteral(literal); + } + } + + @override + void visitMapLiteral(ir.MapLiteral literal) { + if (literal.isConst) { + add(literal); + } else { + super.visitMapLiteral(literal); + } + } + + @override + void visitConstructorInvocation(ir.ConstructorInvocation node) { + if (node.isConst) { + add(node); + } else { + super.visitConstructorInvocation(node); + } + } + + @override + void visitTypeParameter(ir.TypeParameter node) { + // We avoid visiting metadata on the type parameter declaration. The bound + // cannot hold constants so we skip that as well. + } + + @override + void visitVariableDeclaration(ir.VariableDeclaration node) { + // We avoid visiting metadata on the parameter declaration by only visiting + // the initializer. The type cannot hold constants so can kan skip that + // as well. + node.initializer?.accept(this); + } + + @override + void visitTypeLiteral(ir.TypeLiteral node) { + if (node.type is! ir.TypeParameterType) add(node); + } + + @override + void visitInstantiation(ir.Instantiation node) { + // TODO(johnniwinther): The CFE should mark constant instantiations as + // constant. + add(node, required: false); + super.visitInstantiation(node); + } + + @override + void visitConstantExpression(ir.ConstantExpression node) { + add(node); + } +} diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/collector.dart b/pkg/compiler/lib/src/js_emitter/program_builder/collector.dart index 1ab31fc6f685c..bac69a0cba430 100644 --- a/pkg/compiler/lib/src/js_emitter/program_builder/collector.dart +++ b/pkg/compiler/lib/src/js_emitter/program_builder/collector.dart @@ -9,7 +9,6 @@ part of dart2js.js_emitter.program_builder; /// /// The code for the containing (used) methods must exist in the `universe`. class Collector { - final CompilerOptions _options; final JCommonElements _commonElements; final JElementEnvironment _elementEnvironment; final OutputUnitData _outputUnitData; @@ -43,7 +42,6 @@ class Collector { final List nativeClassesAndSubclasses = []; Collector( - this._options, this._commonElements, this._elementEnvironment, this._outputUnitData, @@ -105,7 +103,7 @@ class Collector { // Return the classes that are just helpers for the backend's type system. static Iterable getBackendTypeHelpers( JCommonElements commonElements) { - return [ + return [ commonElements.jsMutableArrayClass, commonElements.jsFixedArrayClass, commonElements.jsExtendableArrayClass, @@ -137,14 +135,6 @@ class Collector { } } - Map> get _outputListsForClassType { - if (_options.deferClassTypes) { - return outputClassTypeLists; - } else { - return outputClassLists; - } - } - /// Compute all the classes and typedefs that must be emitted. void computeNeededDeclarations() { Set backendTypeHelpers = @@ -157,7 +147,6 @@ class Collector { return !backendTypeHelpers.contains(cls) && _rtiNeededClasses.contains(cls) && !classesOnlyNeededForRti.contains(cls) && - _options.deferClassTypes && _outputUnitData.outputUnitForClass(cls) != _outputUnitData.outputUnitForClassType(cls); } @@ -240,7 +229,7 @@ class Collector { // 7. Sort classes needed for type checking and then add them to their // respective OutputUnits. for (ClassEntity cls in _sorter.sortClasses(neededClassTypes)) { - _outputListsForClassType + outputClassTypeLists .putIfAbsent(_outputUnitData.outputUnitForClassType(cls), () => []) .add(cls); } diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart index 53518854258ed..0d0b4382f0015 100644 --- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart +++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart @@ -135,7 +135,6 @@ class ProgramBuilder { this._rtiNeededClasses, this._mainFunction) : this.collector = new Collector( - _options, _commonElements, _elementEnvironment, _outputUnitData, @@ -653,16 +652,11 @@ class ProgramBuilder { Class _buildClass(ClassEntity cls) { bool onlyForConstructor = collector.classesOnlyNeededForConstructor.contains(cls); - bool onlyForRti = _options.deferClassTypes - ? false - : collector.classesOnlyNeededForRti.contains(cls); + // TODO(joshualitt): Can we just emit JSInteropClasses as types? + // TODO(jacobr): check whether the class has any active static fields + // if it does not we can suppress it completely. + bool onlyForRti = _nativeData.isJsInteropClass(cls); bool hasRtiField = _rtiNeed.classNeedsTypeArguments(cls); - if (_nativeData.isJsInteropClass(cls)) { - // TODO(joshualitt): Can we just emit JSInteropClasses as types? - // TODO(jacobr): check whether the class has any active static fields - // if it does not we can suppress it completely. - onlyForRti = true; - } bool onlyForConstructorOrRti = onlyForConstructor || onlyForRti; bool isClosureBaseClass = cls == _commonElements.closureClass; diff --git a/pkg/compiler/lib/src/kernel/deferred_load.dart b/pkg/compiler/lib/src/kernel/deferred_load.dart deleted file mode 100644 index 37ca71d1fb5c0..0000000000000 --- a/pkg/compiler/lib/src/kernel/deferred_load.dart +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library kernel.deferred_load_data; - -import 'package:kernel/ast.dart' as ir; -import 'package:kernel/type_environment.dart' as ir; - -import '../common_elements.dart'; -import '../compiler.dart' show Compiler; -import '../constants/values.dart'; -import '../deferred_load.dart'; -import '../elements/entities.dart'; -import '../ir/util.dart'; -import 'element_map.dart'; - -class KernelDeferredLoadTask extends DeferredLoadTask { - KernelToElementMap _elementMap; - Map> _additionalExportsSets = - >{}; - - KernelDeferredLoadTask(Compiler compiler, this._elementMap) : super(compiler); - - Iterable _findImportsTo(ir.NamedNode node, String nodeName, - ir.Library enclosingLibrary, LibraryEntity library) { - return measureSubtask('find-imports', () { - List imports = []; - ir.Library source = _elementMap.getLibraryNode(library); - if (!source.dependencies.any((d) => d.isDeferred)) return const []; - for (ir.LibraryDependency dependency in source.dependencies) { - if (dependency.isExport) continue; - if (!_isVisible(dependency.combinators, nodeName)) continue; - if (enclosingLibrary == dependency.targetLibrary || - additionalExports(dependency.targetLibrary).contains(node)) { - imports.add(_elementMap.getImport(dependency)); - } - } - return imports; - }); - } - - @override - Iterable classImportsTo( - ClassEntity element, LibraryEntity library) { - ir.Class node = _elementMap.getClassNode(element); - return _findImportsTo(node, node.name, node.enclosingLibrary, library); - } - - @override - Iterable memberImportsTo( - Entity element, LibraryEntity library) { - ir.Member node = _elementMap.getMemberNode(element); - return _findImportsTo( - node is ir.Constructor ? node.enclosingClass : node, - node is ir.Constructor ? node.enclosingClass.name : node.name.text, - node.enclosingLibrary, - library); - } - - @override - void checkForDeferredErrorCases(LibraryEntity library) { - // Nothing to do. The FE checks for error cases upfront. - } - - @override - void collectConstantsFromMetadata( - Entity element, Set constants) { - // Nothing to do. Kernel-pipeline doesn't support mirrors, so we don't need - // to track any constants from meta-data. - } - - @override - void collectConstantsInBody(MemberEntity element, Dependencies dependencies) { - ir.Member node = _elementMap.getMemberNode(element); - - // Fetch the internal node in order to skip annotations on the member. - // TODO(sigmund): replace this pattern when the kernel-ast provides a better - // way to skip annotations (issue 31565). - var visitor = new ConstantCollector( - _elementMap, _elementMap.getStaticTypeContext(element), dependencies); - if (node is ir.Field) { - node.initializer?.accept(visitor); - return; - } - - if (node is ir.Constructor) { - node.initializers.forEach((i) => i.accept(visitor)); - } - node.function?.accept(visitor); - } - - Set additionalExports(ir.Library library) { - return _additionalExportsSets[library] ??= new Set.from( - library.additionalExports.map((ir.Reference ref) => ref.node)); - } - - @override - void cleanup() { - _additionalExportsSets = null; - } -} - -/// Returns whether [name] would be visible according to the given list of -/// show/hide [combinators]. -bool _isVisible(List combinators, String name) { - for (var c in combinators) { - if (c.isShow && !c.names.contains(name)) return false; - if (c.isHide && c.names.contains(name)) return false; - } - return true; -} - -class ConstantCollector extends ir.RecursiveVisitor { - final KernelToElementMap elementMap; - final Dependencies dependencies; - final ir.StaticTypeContext staticTypeContext; - - ConstantCollector(this.elementMap, this.staticTypeContext, this.dependencies); - - CommonElements get commonElements => elementMap.commonElements; - - void add(ir.Expression node, {bool required: true}) { - ConstantValue constant = elementMap - .getConstantValue(staticTypeContext, node, requireConstant: required); - if (constant != null) { - dependencies.addConstant( - constant, elementMap.getImport(getDeferredImport(node))); - } - } - - @override - void visitIntLiteral(ir.IntLiteral literal) {} - - @override - void visitDoubleLiteral(ir.DoubleLiteral literal) {} - - @override - void visitBoolLiteral(ir.BoolLiteral literal) {} - - @override - void visitStringLiteral(ir.StringLiteral literal) {} - - @override - void visitSymbolLiteral(ir.SymbolLiteral literal) => add(literal); - - @override - void visitNullLiteral(ir.NullLiteral literal) {} - - @override - void visitListLiteral(ir.ListLiteral literal) { - if (literal.isConst) { - add(literal); - } else { - super.visitListLiteral(literal); - } - } - - @override - void visitSetLiteral(ir.SetLiteral literal) { - if (literal.isConst) { - add(literal); - } else { - super.visitSetLiteral(literal); - } - } - - @override - void visitMapLiteral(ir.MapLiteral literal) { - if (literal.isConst) { - add(literal); - } else { - super.visitMapLiteral(literal); - } - } - - @override - void visitConstructorInvocation(ir.ConstructorInvocation node) { - if (node.isConst) { - add(node); - } else { - super.visitConstructorInvocation(node); - } - } - - @override - void visitTypeParameter(ir.TypeParameter node) { - // We avoid visiting metadata on the type parameter declaration. The bound - // cannot hold constants so we skip that as well. - } - - @override - void visitVariableDeclaration(ir.VariableDeclaration node) { - // We avoid visiting metadata on the parameter declaration by only visiting - // the initializer. The type cannot hold constants so can kan skip that - // as well. - node.initializer?.accept(this); - } - - @override - void visitTypeLiteral(ir.TypeLiteral node) { - if (node.type is! ir.TypeParameterType) add(node); - } - - @override - void visitInstantiation(ir.Instantiation node) { - // TODO(johnniwinther): The CFE should mark constant instantiations as - // constant. - add(node, required: false); - super.visitInstantiation(node); - } - - @override - void visitConstantExpression(ir.ConstantExpression node) { - add(node); - } -} diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart index 478c2bce1b256..b8959e97bac6f 100644 --- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart +++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart @@ -45,7 +45,6 @@ import '../universe/resolution_world_builder.dart'; import '../universe/world_builder.dart'; import '../universe/world_impact.dart'; import '../util/enumset.dart'; -import 'deferred_load.dart'; import 'element_map.dart'; import 'element_map_impl.dart'; import 'loader.dart'; @@ -54,7 +53,7 @@ import 'loader.dart'; /// model from kernel IR nodes. class KernelFrontendStrategy extends FrontendStrategy { final NativeBasicDataBuilderImpl nativeBasicDataBuilder = - new NativeBasicDataBuilderImpl(); + NativeBasicDataBuilderImpl(); NativeBasicData _nativeBasicData; CompilerOptions _options; CompilerTask _compilerTask; @@ -87,12 +86,11 @@ class KernelFrontendStrategy extends FrontendStrategy { KernelFrontendStrategy(this._compilerTask, this._options, DiagnosticReporter reporter, env.Environment environment) { assert(_compilerTask != null); - _elementMap = - new KernelToElementMapImpl(reporter, environment, this, _options); - _modularStrategy = new KernelModularStrategy(_compilerTask, _elementMap); - _backendUsageBuilder = new BackendUsageBuilderImpl(this); - noSuchMethodRegistry = new NoSuchMethodRegistryImpl( - commonElements, new KernelNoSuchMethodResolver(_elementMap)); + _elementMap = KernelToElementMapImpl(reporter, environment, this, _options); + _modularStrategy = KernelModularStrategy(_compilerTask, _elementMap); + _backendUsageBuilder = BackendUsageBuilderImpl(this); + noSuchMethodRegistry = NoSuchMethodRegistryImpl( + commonElements, KernelNoSuchMethodResolver(_elementMap)); } NativeResolutionEnqueuer get nativeResolutionEnqueuerForTesting => @@ -139,32 +137,30 @@ class KernelFrontendStrategy extends FrontendStrategy { ResolutionEnqueuer createResolutionEnqueuer( CompilerTask task, Compiler compiler) { RuntimeTypesNeedBuilder rtiNeedBuilder = _createRuntimeTypesNeedBuilder(); - BackendImpacts impacts = - new BackendImpacts(commonElements, compiler.options); - _nativeResolutionEnqueuer = new NativeResolutionEnqueuer( + BackendImpacts impacts = BackendImpacts(commonElements, compiler.options); + _nativeResolutionEnqueuer = NativeResolutionEnqueuer( compiler.options, elementEnvironment, commonElements, _elementMap.types, - new BaseNativeClassFinder(elementEnvironment, nativeBasicData)); - _nativeDataBuilder = new NativeDataBuilderImpl(nativeBasicData); - _customElementsResolutionAnalysis = new CustomElementsResolutionAnalysis( + BaseNativeClassFinder(elementEnvironment, nativeBasicData)); + _nativeDataBuilder = NativeDataBuilderImpl(nativeBasicData); + _customElementsResolutionAnalysis = CustomElementsResolutionAnalysis( elementEnvironment, commonElements, nativeBasicData, _backendUsageBuilder); - _fieldAnalysis = new KFieldAnalysis(this); - ClassQueries classQueries = new KernelClassQueries(elementMap); + _fieldAnalysis = KFieldAnalysis(this); + ClassQueries classQueries = KernelClassQueries(elementMap); ClassHierarchyBuilder classHierarchyBuilder = - new ClassHierarchyBuilder(commonElements, classQueries); - AnnotationsDataBuilder annotationsDataBuilder = - new AnnotationsDataBuilder(); + ClassHierarchyBuilder(commonElements, classQueries); + AnnotationsDataBuilder annotationsDataBuilder = AnnotationsDataBuilder(); // TODO(johnniwinther): This is a hack. The annotation data is built while // using it. With CFE constants the annotations data can be built fully // before creating the resolution enqueuer. - AnnotationsData annotationsData = new AnnotationsDataImpl( + AnnotationsData annotationsData = AnnotationsDataImpl( compiler.options, annotationsDataBuilder.pragmaAnnotations); - ImpactTransformer impactTransformer = new JavaScriptImpactTransformer( + ImpactTransformer impactTransformer = JavaScriptImpactTransformer( elementEnvironment, commonElements, impacts, @@ -175,13 +171,12 @@ class KernelFrontendStrategy extends FrontendStrategy { rtiNeedBuilder, classHierarchyBuilder, annotationsData); - InterceptorDataBuilder interceptorDataBuilder = - new InterceptorDataBuilderImpl( - nativeBasicData, elementEnvironment, commonElements); - return new ResolutionEnqueuer( + InterceptorDataBuilder interceptorDataBuilder = InterceptorDataBuilderImpl( + nativeBasicData, elementEnvironment, commonElements); + return ResolutionEnqueuer( task, compiler.reporter, - new ResolutionEnqueuerListener( + ResolutionEnqueuerListener( compiler.options, elementEnvironment, commonElements, @@ -194,7 +189,7 @@ class KernelFrontendStrategy extends FrontendStrategy { _nativeResolutionEnqueuer, _fieldAnalysis, compiler.deferredLoadTask), - new ResolutionWorldBuilderImpl( + ResolutionWorldBuilderImpl( _options, elementMap, elementEnvironment, @@ -212,7 +207,7 @@ class KernelFrontendStrategy extends FrontendStrategy { const StrongModeWorldStrategy(), classHierarchyBuilder, classQueries), - new KernelWorkItemBuilder( + KernelWorkItemBuilder( _compilerTask, elementMap, nativeBasicData, @@ -251,8 +246,8 @@ class KernelFrontendStrategy extends FrontendStrategy { void registerLoadedLibraries(KernelResult kernelResult) { _elementMap.addComponent(kernelResult.component); _irAnnotationData = processAnnotations( - new ModularCore(kernelResult.component, _elementMap.constantEvaluator)); - _annotationProcessor = new KernelAnnotationProcessor( + ModularCore(kernelResult.component, _elementMap.constantEvaluator)); + _annotationProcessor = KernelAnnotationProcessor( elementMap, nativeBasicDataBuilder, _irAnnotationData); for (Uri uri in kernelResult.libraries) { LibraryEntity library = elementEnvironment.lookupLibrary(uri); @@ -280,7 +275,7 @@ class KernelFrontendStrategy extends FrontendStrategy { @override DeferredLoadTask createDeferredLoadTask(Compiler compiler) => - new KernelDeferredLoadTask(compiler, _elementMap); + DeferredLoadTask(compiler, _elementMap); @override FunctionEntity computeMain(WorldImpactBuilder impactBuilder) { @@ -290,7 +285,7 @@ class KernelFrontendStrategy extends FrontendStrategy { RuntimeTypesNeedBuilder _createRuntimeTypesNeedBuilder() { return _runtimeTypesNeedBuilder ??= _options.disableRtiOptimization ? const TrivialRuntimeTypesNeedBuilder() - : new RuntimeTypesNeedBuilderImpl(elementEnvironment); + : RuntimeTypesNeedBuilderImpl(elementEnvironment); } RuntimeTypesNeedBuilder get runtimeTypesNeedBuilderForTesting => @@ -326,12 +321,12 @@ class KernelWorkItemBuilder implements WorkItemBuilder { this._fieldAnalysis, this._modularStrategy, this._irAnnotationData) - : _nativeMemberResolver = new KernelNativeMemberResolver( + : _nativeMemberResolver = KernelNativeMemberResolver( _elementMap, nativeBasicData, nativeDataBuilder); @override WorkItem createWorkItem(MemberEntity entity) { - return new KernelWorkItem( + return KernelWorkItem( _compilerTask, _elementMap, _impactTransformer, @@ -404,7 +399,7 @@ class KernelWorkItem implements WorkItem { ResolutionImpact impact = _elementMap.computeWorldImpact( element, scopeModel.variableScopeModel, - new Set.from( + Set.from( annotations.iterable(PragmaAnnotation.values)), impactBuilderData: impactBuilderData); WorldImpact worldImpact = @@ -440,8 +435,8 @@ class KernelModularStrategy extends ModularStrategy { @override ModularMemberData getModularMemberData( ir.Member node, EnumSet annotations) { - ScopeModel scopeModel = _compilerTask.measureSubtask('closures', - () => new ScopeModel.from(node, _elementMap.constantEvaluator)); + ScopeModel scopeModel = _compilerTask.measureSubtask( + 'closures', () => ScopeModel.from(node, _elementMap.constantEvaluator)); ImpactBuilderData impactBuilderData; if (useImpactDataForTesting) { // TODO(johnniwinther): Always create and use the [ImpactBuilderData]. @@ -449,8 +444,8 @@ class KernelModularStrategy extends ModularStrategy { // depend on metadata, so these parts of the impact data need to be // computed during conversion to [ResolutionImpact]. impactBuilderData = _compilerTask.measureSubtask('worldImpact', () { - ImpactBuilder builder = new ImpactBuilder( - new ir.StaticTypeContext(node, _elementMap.typeEnvironment), + ImpactBuilder builder = ImpactBuilder( + ir.StaticTypeContext(node, _elementMap.typeEnvironment), _elementMap.classHierarchy, scopeModel.variableScopeModel, useAsserts: _elementMap.options.enableUserAssertions, @@ -459,6 +454,6 @@ class KernelModularStrategy extends ModularStrategy { return builder.computeImpact(node); }); } - return new ModularMemberData(scopeModel, impactBuilderData); + return ModularMemberData(scopeModel, impactBuilderData); } } diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart index f8c790105ad96..f11a3bb09108e 100644 --- a/pkg/compiler/lib/src/options.dart +++ b/pkg/compiler/lib/src/options.dart @@ -142,32 +142,6 @@ class CompilerOptions implements DiagnosticOptions { /// libraries are subdivided. Uri deferredMapUri; - /// Whether to apply the new deferred split fixes. The fixes improve on - /// performance and fix a soundness issue with inferred types. The latter will - /// move more code to the main output unit, because of that we are not - /// enabling the feature by default right away. - /// - /// When [reportInvalidInferredDeferredTypes] shows no errors, we expect this - /// flag to produce the same or better results than the current unsound - /// implementation. - bool newDeferredSplit = true; // default value. - bool _newDeferredSplit = false; - bool _noNewDeferredSplit = false; - - /// Show errors when a deferred type is inferred as a return type of a closure - /// or in a type parameter. Those cases cause the compiler today to behave - /// unsoundly by putting the code in a deferred output unit. In the future - /// when [newDeferredSplit] is on by default, those cases will be treated - /// soundly and will cause more code to be moved to the main output unit. - /// - /// This flag is presented to help developers find and fix the affected code. - bool reportInvalidInferredDeferredTypes = false; - - /// Whether to defer load class types. - bool deferClassTypes = true; // default value. - bool _deferClassTypes = false; - bool _noDeferClassTypes = false; - /// Whether to disable inlining during the backend optimizations. // TODO(sigmund): negate, so all flags are positive bool disableInlining = false; @@ -443,12 +417,6 @@ class CompilerOptions implements DiagnosticOptions { _extractStringOption(options, '--build-id=', _UNDETERMINED_BUILD_ID) ..compileForServer = _hasOption(options, Flags.serverMode) ..deferredMapUri = _extractUriOption(options, '--deferred-map=') - .._newDeferredSplit = _hasOption(options, Flags.newDeferredSplit) - .._noNewDeferredSplit = _hasOption(options, Flags.noNewDeferredSplit) - ..reportInvalidInferredDeferredTypes = - _hasOption(options, Flags.reportInvalidInferredDeferredTypes) - .._deferClassTypes = _hasOption(options, Flags.deferClassTypes) - .._noDeferClassTypes = _hasOption(options, Flags.noDeferClassTypes) ..fatalWarnings = _hasOption(options, Flags.fatalWarnings) ..terseDiagnostics = _hasOption(options, Flags.terse) ..suppressWarnings = _hasOption(options, Flags.suppressWarnings) @@ -555,14 +523,6 @@ class CompilerOptions implements DiagnosticOptions { throw ArgumentError("'${Flags.soundNullSafety}' requires the " "'non-nullable' experiment to be enabled"); } - if (_deferClassTypes && _noDeferClassTypes) { - throw ArgumentError("'${Flags.deferClassTypes}' incompatible with " - "'${Flags.noDeferClassTypes}'"); - } - if (_newDeferredSplit && _noNewDeferredSplit) { - throw ArgumentError("'${Flags.newDeferredSplit}' incompatible with " - "'${Flags.noNewDeferredSplit}'"); - } } void deriveOptions() { @@ -620,12 +580,6 @@ class CompilerOptions implements DiagnosticOptions { if (_disableMinification) { enableMinification = false; } - - if (_deferClassTypes) deferClassTypes = true; - if (_noDeferClassTypes) deferClassTypes = false; - - if (_newDeferredSplit) newDeferredSplit = true; - if (_noNewDeferredSplit) newDeferredSplit = false; } /// Returns `true` if warnings and hints are shown for all packages. diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart index 7cc2d4d359778..a317787ea4f61 100644 --- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart @@ -2455,7 +2455,10 @@ class BodyBuilder extends ScopeListener isConst: isConst, isLate: isLate, isRequired: isRequired, - hasDeclaredInitializer: initializer != null) + hasDeclaredInitializer: initializer != null, + isStaticLate: libraryBuilder.isNonNullableByDefault && + isFinal && + initializer == null) ..fileOffset = identifier.charOffset ..fileEqualsOffset = offsetForToken(equalsToken); typeInferrer?.assignedVariables?.declare(variable); @@ -2728,7 +2731,14 @@ class BodyBuilder extends ScopeListener @override void handleForInitializerLocalVariableDeclaration(Token token, bool forIn) { debugEvent("ForInitializerLocalVariableDeclaration"); - if (!forIn) { + if (forIn) { + // If the declaration is of the form `for (final x in ...)`, then we may + // have erroneously set the `isStaticLate` flag, so un-set it. + Object declaration = peek(); + if (declaration is VariableDeclarationImpl) { + declaration.isStaticLate = false; + } + } else { // This is matched by the call to [deferNode] in [endForStatement] or // [endForControlFlow]. typeInferrer?.assignedVariables?.beginNode(); diff --git a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart index df79b098030c1..6b8510c78888d 100644 --- a/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart +++ b/pkg/front_end/lib/src/fasta/kernel/internal_ast.dart @@ -1426,6 +1426,14 @@ class VariableDeclarationImpl extends VariableDeclaration { // TODO(ahe): Investigate if this can be removed. final bool isLocalFunction; + /// Whether the variable is final with no initializer in a null safe library. + /// + /// Such variables behave similar to those declared with the `late` keyword, + /// except that the don't have lazy evaluation semantics, and it is statically + /// verified by the front end that they are always assigned before they are + /// used. + bool isStaticLate; + VariableDeclarationImpl(String name, this.functionNestingLevel, {this.forSyntheticToken: false, this.hasDeclaredInitializer: false, @@ -1437,7 +1445,8 @@ class VariableDeclarationImpl extends VariableDeclaration { bool isCovariant: false, bool isLocalFunction: false, bool isLate: false, - bool isRequired: false}) + bool isRequired: false, + this.isStaticLate: false}) : isImplicitlyTyped = type == null, isLocalFunction = isLocalFunction, super(name, @@ -1455,6 +1464,7 @@ class VariableDeclarationImpl extends VariableDeclaration { functionNestingLevel = 0, isImplicitlyTyped = false, isLocalFunction = false, + isStaticLate = false, hasDeclaredInitializer = true, super.forValue(initializer); @@ -1463,6 +1473,7 @@ class VariableDeclarationImpl extends VariableDeclaration { functionNestingLevel = 0, isImplicitlyTyped = true, isLocalFunction = false, + isStaticLate = false, hasDeclaredInitializer = true, super.forValue(initializer); @@ -1491,6 +1502,12 @@ class VariableDeclarationImpl extends VariableDeclaration { // lowering is enabled. DartType lateType; + @override + bool get isAssignable { + if (isStaticLate) return true; + return super.isAssignable; + } + @override void toTextInternal(AstPrinter printer) { printer.writeVariableDeclaration(this, diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status index 3a3eecabf9534..980ddebaa0dcd 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -334,8 +334,6 @@ FieldNonNullableWithoutInitializerWarning/example: Fail FinalAndCovariant/part_wrapped_script2: Fail FinalAndCovariant/script2: Fail FinalFieldWithoutInitializer/example: Fail -FinalNotAssignedError/script: Fail # Fasta reports too many errors -FinalNotAssignedError/part_wrapped_script: Fail # Fasta reports too many errors ForInLoopElementTypeNotAssignable/example: Fail ForInLoopExactlyOneVariable/analyzerCode: Fail # The analyzer doesn't recover well. ForInLoopExactlyOneVariable/part_wrapped_statement: Fail diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt index 1521039b3d2fd..091095c3ed5de 100644 --- a/pkg/front_end/test/spell_checking_list_common.txt +++ b/pkg/front_end/test/spell_checking_list_common.txt @@ -1018,6 +1018,7 @@ equivalent equivalents erasure erroneous +erroneously error errors escape diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart index 34fd13952219c..5a8f2bb9419d5 100644 --- a/pkg/nnbd_migration/test/api_test.dart +++ b/pkg/nnbd_migration/test/api_test.dart @@ -1710,7 +1710,6 @@ C f(List a) => a; await _checkSingleFileChanges(content, expected); } - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39609') Future test_dynamic_dispatch_to_object_method() async { var content = ''' String f(dynamic x) => x.toString(); @@ -1787,7 +1786,6 @@ main() { await _checkSingleFileChanges(content, expected); } - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39609') Future test_dynamic_toString() async { var content = ''' String f(dynamic x) => x.toString(); diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart index 48932cde2e52a..bad218cca3b7b 100644 --- a/pkg/vm/lib/transformations/type_flow/analysis.dart +++ b/pkg/vm/lib/transformations/type_flow/analysis.dart @@ -548,9 +548,8 @@ class _DispatchableInvocation extends _Invocation { TypeFlowAnalysis typeFlowAnalysis) { final TFClass cls = receiver.cls; - Member target = typeFlowAnalysis.hierarchyCache.hierarchy.getDispatchTarget( - cls.classNode, selector.name, - setter: selector.isSetter); + Member target = + (cls as _TFClassImpl).getDispatchTarget(selector, typeFlowAnalysis); if (target != null) { if (kPrintTrace) { @@ -934,6 +933,7 @@ class _DynamicTargetSet extends _DependencyTracker { class _TFClassImpl extends TFClass { final Set<_TFClassImpl> supertypes; // List of super-types including this. final Set<_TFClassImpl> _allocatedSubtypes = new Set<_TFClassImpl>(); + final Map _dispatchTargets = {}; final _DependencyTracker dependencyTracker = new _DependencyTracker(); /// Flag indicating if this class has a noSuchMethod() method not inherited @@ -977,6 +977,18 @@ class _TFClassImpl extends TFClass { _specializedConeType = null; // Reset cached specialization. } + Member getDispatchTarget( + Selector selector, TypeFlowAnalysis typeFlowAnalysis) { + Member target = _dispatchTargets[selector]; + if (target == null) { + target = typeFlowAnalysis.hierarchyCache.hierarchy.getDispatchTarget( + classNode, selector.name, + setter: selector.isSetter); + _dispatchTargets[selector] = target; + } + return target; + } + String dump() => "$this {supers: $supertypes}"; } diff --git a/pkg/vm/lib/transformations/type_flow/types.dart b/pkg/vm/lib/transformations/type_flow/types.dart index 01c219a8a8f96..e7e0c1d45ccb4 100644 --- a/pkg/vm/lib/transformations/type_flow/types.dart +++ b/pkg/vm/lib/transformations/type_flow/types.dart @@ -25,6 +25,10 @@ class TFClass { /// instances specific to given [TypeHierarchy]. TFClass(this.id, this.classNode); + /// Returns ConcreteType corresponding to this class without + /// any extra attributes. + ConcreteType get concreteType => ConcreteType(this); + @override int get hashCode => id; @@ -457,12 +461,40 @@ class SetType extends Type { return types; } + bool _isSubList(List sublist, List list) { + int i1 = 0; + int i2 = 0; + while ((i1 < sublist.length) && (i2 < list.length)) { + final t1 = sublist[i1]; + final t2 = list[i2]; + if (identical(t1, t2)) { + ++i1; + ++i2; + } else if (t1.cls.id > t2.cls.id) { + ++i2; + } else { + return false; + } + } + return i1 == sublist.length; + } + @override Type union(Type other, TypeHierarchy typeHierarchy) { + if (identical(this, other)) return this; if (other.order < this.order) { return other.union(this, typeHierarchy); } if (other is SetType) { + if (types.length >= other.types.length) { + if (_isSubList(other.types, types)) { + return this; + } + } else { + if (_isSubList(types, other.types)) { + return other; + } + } return new SetType(_unionLists(types, other.types)); } else if (other is ConcreteType) { return types.contains(other) @@ -479,6 +511,7 @@ class SetType extends Type { @override Type intersection(Type other, TypeHierarchy typeHierarchy) { + if (identical(this, other)) return this; if (other.order < this.order) { return other.intersection(this, typeHierarchy); } @@ -559,6 +592,7 @@ class ConeType extends Type { @override Type union(Type other, TypeHierarchy typeHierarchy) { + if (identical(this, other)) return this; if (other.order < this.order) { return other.union(this, typeHierarchy); } @@ -582,6 +616,7 @@ class ConeType extends Type { @override Type intersection(Type other, TypeHierarchy typeHierarchy) { + if (identical(this, other)) return this; if (other.order < this.order) { return other.intersection(this, typeHierarchy); } @@ -640,7 +675,8 @@ class ConcreteType extends Type implements Comparable { assert(typeArgs == null || typeArgs.any((t) => t is RuntimeType)); } - ConcreteType get raw => new ConcreteType(cls, null); + ConcreteType get raw => cls.concreteType; + bool get isRaw => typeArgs == null && constant == null; @override Class getConcreteClass(TypeHierarchy typeHierarchy) => cls.classNode; diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 1df37c68ea621..5c2a3a94d1110 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -207,8 +207,8 @@ source_set("dart_api") { public_configs = [ ":dart_public_config" ] sources = [ "include/dart_api.h", + "include/dart_api_dl.c", "include/dart_api_dl.h", - "include/dart_native_api.c", "include/dart_native_api.h", "include/dart_tools_api.h", "include/dart_version.h", diff --git a/tools/VERSION b/tools/VERSION index c4cc230ae8d63..b5f40fb2447a5 100644 --- a/tools/VERSION +++ b/tools/VERSION @@ -27,5 +27,5 @@ CHANNEL dev MAJOR 2 MINOR 11 PATCH 0 -PRERELEASE 165 +PRERELEASE 166 PRERELEASE_PATCH 0 \ No newline at end of file