From 28f95fcd244d4ebeccfa43dc9286ec4aed8d2326 Mon Sep 17 00:00:00 2001 From: Jens Johansen Date: Fri, 12 Jul 2019 10:40:55 +0000 Subject: [PATCH] [cfe] Track dependencies in the incremental compiler The objective of this CL is to allow the incremental compiler to track which dill input libraries it actually used (in order to - in a modular context - be able to ignore changes to dependencies we didn't really use). This is done by: * Making the dill library builder lazy (and mark if it has been used). * Making kernels class hierarchy record which classes it has been asked questions about. * Add special handling to redirecting factory constructors as they bypass the fasta-builders and directly use the kernel ast. Please note that: * This has to be enabled, but kernels class hierarchy always records which classes it was asked about (even if disabled, or not running though the incremental compiler). There might potentially be some overhead to this (though setting a bool to true is probably comparably cheap - we did just do a map lookup). * This was designed to be used together with modules (e.g. setModulesToLoadOnNextComputeDelta) - as used via for instance api_unstable/bazel_worker.dart. It might not function the way you'd expect in other circumstances. * The incremental compiler (potentially) gives you the full kernel tree despite not having marked all of those libraries as 'used'. If a client use such libraries it is the clients responsibility to take that into account when answering any questions about this libraries/dill files was used. * This feature works on the library level. In practice it is most likely needed at the dill-filename level. A translation from library to dill-filename is up to the client. * This is a new feature, and we cannot promise that 100% of actually used libraries are marked. If you find used but un-marked libraries please report a bug. Change-Id: I01d7ff95b9baac9550b77d8e09ea772d43173641 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107280 Commit-Queue: Jens Johansen Reviewed-by: Johnni Winther --- .../src/fasta/dill/dill_library_builder.dart | 65 +++++++- .../lib/src/fasta/dill/dill_loader.dart | 7 +- .../lib/src/fasta/incremental_compiler.dart | 106 +++++++++++- .../lib/src/fasta/kernel/body_builder.dart | 56 ++++++- .../kernel/redirecting_factory_body.dart | 15 +- .../test/fasta/ambiguous_export_test.dart | 2 +- .../test/incremental_load_from_dill_test.dart | 35 +++- .../changing_modules.yaml | 7 + .../changing_modules_2.yaml | 6 + .../changing_modules_3.yaml | 4 + .../changing_modules_4.yaml | 78 +++++++++ .../changing_modules_5.yaml | 80 +++++++++ .../changing_modules_6.yaml | 86 ++++++++++ .../changing_modules_7.yaml | 123 ++++++++++++++ .../changing_modules_8.yaml | 61 +++++++ .../crash_test_1.yaml | 22 +++ pkg/kernel/lib/class_hierarchy.dart | 153 +++++++++++------- 17 files changed, 830 insertions(+), 76 deletions(-) create mode 100644 pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_4.yaml create mode 100644 pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_5.yaml create mode 100644 pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_6.yaml create mode 100644 pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_7.yaml create mode 100644 pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_8.yaml create mode 100644 pkg/front_end/testcases/incremental_initialize_from_dill/crash_test_1.yaml diff --git a/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart b/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart index f1d4ff2d411..d577befd927 100644 --- a/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart +++ b/pkg/front_end/lib/src/fasta/dill/dill_library_builder.dart @@ -51,6 +51,31 @@ import 'dill_loader.dart' show DillLoader; import 'dill_type_alias_builder.dart' show DillTypeAliasBuilder; +class LazyLibraryScope extends Scope { + DillLibraryBuilder libraryBuilder; + + LazyLibraryScope(Map local, + Map setters, Scope parent, String debugName, + {bool isModifiable: true}) + : super(local, setters, parent, debugName, isModifiable: isModifiable); + + LazyLibraryScope.top({bool isModifiable: false}) + : this({}, {}, null, "top", + isModifiable: isModifiable); + + Map get local { + if (libraryBuilder == null) throw new StateError("No library builder."); + libraryBuilder.ensureLoaded(); + return super.local; + } + + Map get setters { + if (libraryBuilder == null) throw new StateError("No library builder."); + libraryBuilder.ensureLoaded(); + return super.setters; + } +} + class DillLibraryBuilder extends LibraryBuilder { final Library library; @@ -64,8 +89,38 @@ class DillLibraryBuilder extends LibraryBuilder { bool exportsAlreadyFinalized = false; + // TODO(jensj): These 4 booleans could potentially be merged into a single + // state field. + bool isReadyToBuild = false; + bool isReadyToFinalizeExports = false; + bool isBuilt = false; + bool isBuiltAndMarked = false; + DillLibraryBuilder(this.library, this.loader) - : super(library.fileUri, new Scope.top(), new Scope.top()); + : super(library.fileUri, new LazyLibraryScope.top(), + new LazyLibraryScope.top()) { + LazyLibraryScope lazyScope = scope; + lazyScope.libraryBuilder = this; + LazyLibraryScope lazyExportScope = exportScope; + lazyExportScope.libraryBuilder = this; + } + + void ensureLoaded() { + if (!isReadyToBuild) throw new StateError("Not ready to build."); + isBuiltAndMarked = true; + if (isBuilt) return; + isBuilt = true; + library.classes.forEach(addClass); + library.procedures.forEach(addMember); + library.typedefs.forEach(addTypedef); + library.fields.forEach(addMember); + + if (isReadyToFinalizeExports) { + finalizeExports(); + } else { + throw new StateError("Not ready to finalize exports."); + } + } @override bool get isSynthetic => library.isSynthetic; @@ -174,6 +229,14 @@ class DillLibraryBuilder extends LibraryBuilder { return library.name ?? ""; } + void markAsReadyToBuild() { + isReadyToBuild = true; + } + + void markAsReadyToFinalizeExports() { + isReadyToFinalizeExports = true; + } + void finalizeExports() { if (exportsAlreadyFinalized) return; exportsAlreadyFinalized = true; diff --git a/pkg/front_end/lib/src/fasta/dill/dill_loader.dart b/pkg/front_end/lib/src/fasta/dill/dill_loader.dart index aa5785bddc6..ea0fc73f11e 100644 --- a/pkg/front_end/lib/src/fasta/dill/dill_loader.dart +++ b/pkg/front_end/lib/src/fasta/dill/dill_loader.dart @@ -61,10 +61,7 @@ class DillLoader extends Loader { if (builder.library == null) { unhandled("null", "builder.library", 0, builder.fileUri); } - builder.library.classes.forEach(builder.addClass); - builder.library.procedures.forEach(builder.addMember); - builder.library.typedefs.forEach(builder.addTypedef); - builder.library.fields.forEach(builder.addMember); + builder.markAsReadyToBuild(); } Future buildBody(DillLibraryBuilder builder) { @@ -74,7 +71,7 @@ class DillLoader extends Loader { void finalizeExports() { builders.forEach((Uri uri, LibraryBuilder builder) { DillLibraryBuilder library = builder; - library.finalizeExports(); + library.markAsReadyToFinalizeExports(); }); } diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart index 6c84d0b229d..1d2713eaa52 100644 --- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart +++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart @@ -15,7 +15,8 @@ import 'package:kernel/binary/ast_from_binary.dart' CanonicalNameSdkError, InvalidKernelVersionError; -import 'package:kernel/class_hierarchy.dart' show ClassHierarchy; +import 'package:kernel/class_hierarchy.dart' + show ClassHierarchy, ClosedWorldClassHierarchy; import 'package:kernel/kernel.dart' show @@ -32,6 +33,7 @@ import 'package:kernel/kernel.dart' ProcedureKind, ReturnStatement, Source, + Supertype, TreeNode, TypeParameter; @@ -68,6 +70,8 @@ import 'fasta_codes.dart' import 'hybrid_file_system.dart' show HybridFileSystem; +import 'kernel/kernel_builder.dart' show ClassHierarchyBuilder; + import 'kernel/kernel_library_builder.dart' show KernelLibraryBuilder; import 'kernel/kernel_shadow_ast.dart' show VariableDeclarationJudgment; @@ -90,6 +94,8 @@ class IncrementalCompiler implements IncrementalKernelGenerator { final Ticker ticker; final bool outlineOnly; + bool trackNeededDillLibraries = false; + Set neededDillLibraries; Set invalidatedUris = new Set(); @@ -301,6 +307,19 @@ class IncrementalCompiler implements IncrementalKernelGenerator { uriTranslator); userCode.loader.hierarchy = hierarchy; + if (trackNeededDillLibraries) { + // Reset dill loaders and kernel class hierarchy. + for (LibraryBuilder builder in dillLoadedData.loader.builders.values) { + if (builder is DillLibraryBuilder) { + builder.isBuiltAndMarked = false; + } + } + + if (hierarchy is ClosedWorldClassHierarchy) { + hierarchy.resetUsed(); + } + } + for (LibraryBuilder library in reusedLibraries) { userCode.loader.builders[library.uri] = library; if (library.uri.scheme == "dart" && library.uri.path == "core") { @@ -324,8 +343,23 @@ class IncrementalCompiler implements IncrementalKernelGenerator { componentWithDill = await userCode.buildComponent(verify: c.options.verify); } + hierarchy ??= userCode.loader.hierarchy; recordNonFullComponentForTesting(componentWithDill); + if (trackNeededDillLibraries) { + // Which dill builders were built? + neededDillLibraries = new Set(); + for (LibraryBuilder builder in dillLoadedData.loader.builders.values) { + if (builder is DillLibraryBuilder) { + if (builder.isBuiltAndMarked) { + neededDillLibraries.add(builder.library); + } + } + } + + updateNeededDillLibraresWithHierarchy( + hierarchy, userCode.loader.builderHierarchy); + } if (componentWithDill != null) { this.invalidatedUris.clear(); @@ -388,6 +422,76 @@ class IncrementalCompiler implements IncrementalKernelGenerator { }); } + /// Allows for updating the list of needed libraries. + /// + /// Useful if a class hierarchy has been used externally. + /// Currently there are two different class hierarchies which is unfortunate. + /// For now this method allows the 'ClassHierarchyBuilder' to be null. + /// + /// TODO(jensj,CFE in general): Eventually we should get to a point where we + /// only have one class hierarchy. + /// TODO(jensj): This could probably be a utility method somewhere instead + /// (though handling of the case where all bets are off should probably still + /// live locally). + void updateNeededDillLibraresWithHierarchy( + ClassHierarchy hierarchy, ClassHierarchyBuilder builderHierarchy) { + if (hierarchy is ClosedWorldClassHierarchy && !hierarchy.allBetsOff) { + neededDillLibraries ??= new Set(); + Set classes = new Set(); + List worklist = new List(); + // Get all classes touched by kernel class hierarchy. + List usedClasses = hierarchy.getUsedClasses(); + worklist.addAll(usedClasses); + classes.addAll(usedClasses); + + // Get all classes touched by fasta class hierarchy. + if (builderHierarchy != null) { + for (Class c in builderHierarchy.nodes.keys) { + if (classes.add(c)) worklist.add(c); + } + } + + // Get all supers etc. + while (worklist.isNotEmpty) { + Class c = worklist.removeLast(); + for (Supertype supertype in c.implementedTypes) { + if (classes.add(supertype.classNode)) { + worklist.add(supertype.classNode); + } + } + if (c.mixedInType != null) { + if (classes.add(c.mixedInType.classNode)) { + worklist.add(c.mixedInType.classNode); + } + } + if (c.supertype != null) { + if (classes.add(c.supertype.classNode)) { + worklist.add(c.supertype.classNode); + } + } + } + + // Add any libraries that was used or was in the "parent-chain" of a + // used class. + for (Class c in classes) { + Library library = c.enclosingLibrary; + // Only add if loaded from a dill file. + if (dillLoadedData.loader.builders.containsKey(library.importUri)) { + neededDillLibraries.add(library); + } + } + } else { + // Cannot track in other kernel class hierarchies or + // if all bets are off: Add everything. + neededDillLibraries = new Set(); + for (LibraryBuilder builder in dillLoadedData.loader.builders.values) { + if (builder is DillLibraryBuilder) { + neededDillLibraries.add(builder.library); + } + } + } + } + /// Internal method. void invalidateNotKeptUserBuilders(Set invalidatedUris) { if (modulesToLoad != null && userBuilders != null) { 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 8f8605e7cdf..aaea6ab83f3 100644 --- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart @@ -8,6 +8,8 @@ import 'dart:core' hide MapEntry; import '../constant_context.dart' show ConstantContext; +import '../dill/dill_library_builder.dart' show DillLibraryBuilder; + import '../fasta_codes.dart' as fasta; import '../fasta_codes.dart' show LocatedMessage, Message, noLength, Template; @@ -133,7 +135,7 @@ const noLocation = null; const invalidCollectionElement = const Object(); abstract class BodyBuilder extends ScopeListener - implements ExpressionGeneratorHelper { + implements ExpressionGeneratorHelper, EnsureLoaded { // TODO(ahe): Rename [library] to 'part'. @override final KernelLibraryBuilder library; @@ -903,6 +905,44 @@ abstract class BodyBuilder extends ScopeListener finishVariableMetadata(); } + /// Ensure that the containing library of the [member] has been loaded. + /// + /// This is for instance important for lazy dill library builders where this + /// method has to be called to ensure that + /// a) The library has been fully loaded (and for instance any internal + /// transformation needed has been performed); and + /// b) The library is correctly marked as being used to allow for proper + /// 'dependency pruning'. + void ensureLoaded(Member member) { + if (member == null) return; + Library ensureLibraryLoaded = member.enclosingLibrary; + LibraryBuilder builder = + library.loader.builders[ensureLibraryLoaded.importUri] ?? + library.loader.target.dillTarget.loader + .builders[ensureLibraryLoaded.importUri]; + if (builder is DillLibraryBuilder) { + builder.ensureLoaded(); + } + } + + /// Check if the containing library of the [member] has been loaded. + /// + /// This is designed for use with asserts. + /// See [ensureLoaded] for a description of what 'loaded' means and the ideas + /// behind that. + bool isLoaded(Member member) { + if (member == null) return true; + Library ensureLibraryLoaded = member.enclosingLibrary; + LibraryBuilder builder = + library.loader.builders[ensureLibraryLoaded.importUri] ?? + library.loader.target.dillTarget.loader + .builders[ensureLibraryLoaded.importUri]; + if (builder is DillLibraryBuilder) { + return builder.isBuiltAndMarked; + } + return true; + } + void resolveRedirectingFactoryTargets() { for (StaticInvocation invocation in redirectingFactoryInvocations) { // If the invocation was invalid, it or its parent has already been @@ -927,7 +967,7 @@ abstract class BodyBuilder extends ScopeListener Expression replacementNode; RedirectionTarget redirectionTarget = - getRedirectionTarget(initialTarget, legacyMode: legacyMode); + getRedirectionTarget(initialTarget, this, legacyMode: legacyMode); Member resolvedTarget = redirectionTarget?.target; if (resolvedTarget == null) { @@ -3355,7 +3395,7 @@ abstract class BodyBuilder extends ScopeListener int charLength: noLength}) { // The argument checks for the initial target of redirecting factories // invocations are skipped in Dart 1. - if (!legacyMode || !isRedirectingFactory(target)) { + if (!legacyMode || !isRedirectingFactory(target, helper: this)) { List typeParameters = target.function.typeParameters; if (target is Constructor) { assert(!target.enclosingClass.isAbstract); @@ -3664,7 +3704,7 @@ abstract class BodyBuilder extends ScopeListener (target is Procedure && target.kind == ProcedureKind.Factory)) { Expression invocation; - if (legacyMode && isRedirectingFactory(target)) { + if (legacyMode && isRedirectingFactory(target, helper: this)) { // In legacy mode the checks that are done in [buildStaticInvocation] // on the initial target of a redirecting factory invocation should // be skipped. So we build the invocation nodes directly here without @@ -3690,7 +3730,8 @@ abstract class BodyBuilder extends ScopeListener charLength: nameToken.length); } - if (invocation is StaticInvocation && isRedirectingFactory(target)) { + if (invocation is StaticInvocation && + isRedirectingFactory(target, helper: this)) { redirectingFactoryInvocations.add(invocation); } @@ -5408,6 +5449,11 @@ abstract class BodyBuilder extends ScopeListener } } +abstract class EnsureLoaded { + void ensureLoaded(Member member); + bool isLoaded(Member member); +} + class Operator { final Token token; String get name => token.stringValue; diff --git a/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart b/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart index bcd7ccaa0df..99a1bb6e9dd 100644 --- a/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart +++ b/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart @@ -23,6 +23,8 @@ import 'package:kernel/ast.dart' import 'package:kernel/type_algebra.dart' show Substitution; +import 'body_builder.dart' show EnsureLoaded; + const String letName = "#redirecting_factory"; class RedirectingFactoryBody extends ExpressionStatement { @@ -114,7 +116,8 @@ class RedirectingFactoryBody extends ExpressionStatement { } } -bool isRedirectingFactory(Member member) { +bool isRedirectingFactory(Member member, {EnsureLoaded helper}) { + assert(helper == null || helper.isLoaded(member)); return member is Procedure && member.function.body is RedirectingFactoryBody; } @@ -129,7 +132,8 @@ class RedirectionTarget { RedirectionTarget(this.target, this.typeArguments); } -RedirectionTarget getRedirectionTarget(Procedure member, {bool legacyMode}) { +RedirectionTarget getRedirectionTarget(Procedure member, EnsureLoaded helper, + {bool legacyMode}) { List typeArguments = []..length = member.function.typeParameters.length; for (int i = 0; i < typeArguments.length; i++) { @@ -142,11 +146,14 @@ RedirectionTarget getRedirectionTarget(Procedure member, {bool legacyMode}) { Member tortoise = member; RedirectingFactoryBody tortoiseBody = getRedirectingFactoryBody(tortoise); Member hare = tortoiseBody?.target; + helper.ensureLoaded(hare); RedirectingFactoryBody hareBody = getRedirectingFactoryBody(hare); while (tortoise != hare) { - if (tortoiseBody?.isUnresolved ?? true) + if (tortoiseBody?.isUnresolved ?? true) { return new RedirectionTarget(tortoise, typeArguments); + } Member nextTortoise = tortoiseBody.target; + helper.ensureLoaded(nextTortoise); List nextTypeArguments = tortoiseBody.typeArguments; if (!legacyMode && nextTypeArguments == null) { nextTypeArguments = []; @@ -173,7 +180,9 @@ RedirectionTarget getRedirectionTarget(Procedure member, {bool legacyMode}) { tortoise = nextTortoise; tortoiseBody = getRedirectingFactoryBody(tortoise); + helper.ensureLoaded(hareBody?.target); hare = getRedirectingFactoryBody(hareBody?.target)?.target; + helper.ensureLoaded(hare); hareBody = getRedirectingFactoryBody(hare); } return null; diff --git a/pkg/front_end/test/fasta/ambiguous_export_test.dart b/pkg/front_end/test/fasta/ambiguous_export_test.dart index 8123c39da11..abe0fcc947f 100644 --- a/pkg/front_end/test/fasta/ambiguous_export_test.dart +++ b/pkg/front_end/test/fasta/ambiguous_export_test.dart @@ -32,7 +32,7 @@ main() async { target.loader.appendLibraries(component); DillLibraryBuilder builder = target.loader.read(library.importUri, -1); await target.loader.buildOutline(builder); - builder.finalizeExports(); + builder.markAsReadyToFinalizeExports(); var mainExport = builder.exportScope.local["main"]; Expect.isTrue(mainExport is InvalidTypeBuilder); }); diff --git a/pkg/front_end/test/incremental_load_from_dill_test.dart b/pkg/front_end/test/incremental_load_from_dill_test.dart index 3c68a615dfd..4a7025c29af 100644 --- a/pkg/front_end/test/incremental_load_from_dill_test.dart +++ b/pkg/front_end/test/incremental_load_from_dill_test.dart @@ -405,6 +405,7 @@ Future newWorldTest(List worlds, Map modules, bool omitPlatform) async { } } bool outlineOnly = world["outlineOnly"] == true; + bool skipOutlineBodyCheck = world["skipOutlineBodyCheck"] == true; if (brandNewWorld) { if (world["fromComponent"] == true) { compiler = new TestIncrementalCompiler.fromComponent( @@ -427,6 +428,7 @@ Future newWorldTest(List worlds, Map modules, bool omitPlatform) async { if (modulesToUse != null) { compiler.setModulesToLoadOnNextComputeDelta(modulesToUse); compiler.invalidateAllSources(); + compiler.trackNeededDillLibraries = true; } Stopwatch stopwatch = new Stopwatch()..start(); @@ -434,7 +436,7 @@ Future newWorldTest(List worlds, Map modules, bool omitPlatform) async { entryPoints: entries, fullComponent: brandNewWorld ? false : (noFullComponent ? false : true), simulateTransformer: world["simulateTransformer"]); - if (outlineOnly) { + if (outlineOnly && !skipOutlineBodyCheck) { for (Library lib in component.libraries) { for (Class c in lib.classes) { for (Procedure p in c.procedures) { @@ -457,6 +459,7 @@ Future newWorldTest(List worlds, Map modules, bool omitPlatform) async { print("Compile took ${stopwatch.elapsedMilliseconds} ms"); checkExpectedContent(world, component); + checkNeededDillLibraries(world, compiler.neededDillLibraries, base); if (!noFullComponent) { Set allLibraries = new Set(); @@ -657,6 +660,36 @@ void checkExpectedContent(YamlMap world, Component component) { } } +void checkNeededDillLibraries( + YamlMap world, Set neededDillLibraries, Uri base) { + if (world["neededDillLibraries"] != null) { + List actualContent = new List(); + for (Library lib in neededDillLibraries) { + if (lib.importUri.scheme == "dart") continue; + actualContent.add(lib.importUri); + } + + List expectedContent = new List(); + for (String entry in world["neededDillLibraries"]) { + expectedContent.add(base.resolve(entry)); + } + + doThrow() { + throw "Expected and actual content not the same.\n" + "Expected $expectedContent.\n" + "Got $actualContent"; + } + + if (actualContent.length != expectedContent.length) doThrow(); + Set notInExpected = + actualContent.toSet().difference(expectedContent.toSet()); + Set notInActual = + expectedContent.toSet().difference(actualContent.toSet()); + if (notInExpected.isNotEmpty) doThrow(); + if (notInActual.isNotEmpty) doThrow(); + } +} + String componentToStringSdkFiltered(Component node) { Component c = new Component(); List dartUris = new List(); diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules.yaml index 74d1d072e2d..47752b22c07 100644 --- a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules.yaml +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules.yaml @@ -78,6 +78,9 @@ worlds: modules: - example_0.1.0 expectedLibraryCount: 3 + neededDillLibraries: + # Needs 'b.dart' only! + - package:example/b.dart expectedContent: org-dartlang-test:///main.dart: - Procedure main @@ -103,6 +106,8 @@ worlds: - example_0.1.0 - foo_1 expectedLibraryCount: 4 + neededDillLibraries: + - package:foo/foo.dart expectedContent: org-dartlang-test:///main.dart: - Procedure main @@ -131,6 +136,8 @@ worlds: - example_0.1.1 - foo_2 expectedLibraryCount: 5 + neededDillLibraries: + - package:foo/foo.dart expectedContent: org-dartlang-test:///main.dart: - Procedure main diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_2.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_2.yaml index f2a10a82120..201d9c03d5e 100644 --- a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_2.yaml +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_2.yaml @@ -43,6 +43,9 @@ worlds: - foo - example_1 expectedLibraryCount: 3 + neededDillLibraries: + - package:foo/foo.dart + - package:example/a.dart expectedContent: org-dartlang-test:///main.dart: - Procedure main @@ -62,6 +65,9 @@ worlds: - foo - example_2 expectedLibraryCount: 3 + neededDillLibraries: + - package:foo/foo.dart + - package:example/a.dart expectedContent: org-dartlang-test:///main.dart: - Procedure main diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_3.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_3.yaml index c009ea1474d..a98a4f57fc4 100644 --- a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_3.yaml +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_3.yaml @@ -39,6 +39,8 @@ worlds: package:foo/foo.dart: - Field foo - Field lalala_SimulateTransformer + neededDillLibraries: + - package:foo/foo.dart - entry: main.dart worldType: updated expectInitializeFromDill: false @@ -54,6 +56,8 @@ worlds: modules: - foo2 expectedLibraryCount: 2 + neededDillLibraries: + - package:foo/foo.dart expectedContent: org-dartlang-test:///main.dart: - Procedure main diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_4.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_4.yaml new file mode 100644 index 00000000000..6869e85d6da --- /dev/null +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_4.yaml @@ -0,0 +1,78 @@ +# Copyright (c) 2019, 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.md file. + +# Compile an application with a number of modules. +# Try out "trickery" in the module dependencies to force needing more or less +# dill libraries. + +type: newworld +strong: true +modules: + moduleB: + moduleB/b.dart: | + import 'package:moduleC/c.dart'; + export 'package:moduleC/c.dart' show baz; + + var mya2 = new A2(); + + class A2 extends A3 { + int bar = 42; + } + moduleB/.packages: | + moduleB:. + moduleC:../moduleC + moduleD:../moduleD + moduleC: + moduleC/c.dart: | + import 'package:moduleD/d.dart'; + + String baz = "42"; + String baz2 = baz3; + + class A3 { + int foo = 42; + } + moduleC/.packages: | + moduleC:. + moduleD:../moduleD + moduleD: + moduleD/d.dart: | + String baz3 = "baz3"; + moduleD/.packages: | + moduleD:. +worlds: + - entry: a.dart + fromComponent: true + sources: + a.dart: | + import "package:moduleB/b.dart"; + + String foo = baz; + var x = mya2.bar; + .packages: | + moduleB:moduleB + moduleC:moduleC + moduleD:moduleD + modules: + - moduleB + - moduleC + - moduleD + expectedLibraryCount: 4 + neededDillLibraries: + # 'd.dart' not needed. + - package:moduleB/b.dart + - package:moduleC/c.dart + expectedContent: + org-dartlang-test:///a.dart: + - Field foo + - Field x + package:moduleB/b.dart: + - Class A2 + - Field mya2 + package:moduleC/c.dart: + - Class A3 + - Field baz + - Field baz2 + package:moduleD/d.dart: + - Field baz3 \ No newline at end of file diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_5.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_5.yaml new file mode 100644 index 00000000000..449b3195dd9 --- /dev/null +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_5.yaml @@ -0,0 +1,80 @@ +# Copyright (c) 2019, 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.md file. + +# Compile an application with a number of modules. +# Try out "trickery" in the module dependencies to force needing more or less +# dill libraries. + +type: newworld +strong: true +modules: + moduleB: + moduleB/b.dart: | + import 'package:moduleC/c.dart'; + + var bVar = 42; + var bVarFromC = baz2; + var mya2 = new A2(); + + class A2 extends A3 { + int bar = 42; + } + moduleB/.packages: | + moduleB:. + moduleC:../moduleC + moduleD:../moduleD + moduleC: + moduleC/c.dart: | + import 'package:moduleD/d.dart'; + + String baz = "42"; + String baz2 = baz3; + + class A3 { + int foo = 42; + } + moduleC/.packages: | + moduleC:. + moduleD:../moduleD + moduleD: + moduleD/d.dart: | + String baz3 = "baz3"; + moduleD/.packages: | + moduleD:. +worlds: + - entry: a.dart + fromComponent: true + sources: + a.dart: | + import "package:moduleB/b.dart"; + + var foo = bVar; + var foo2 = bVarFromC; + .packages: | + moduleB:moduleB + moduleC:moduleC + moduleD:moduleD + modules: + - moduleB + - moduleC + - moduleD + expectedLibraryCount: 4 + neededDillLibraries: + # Only b.dart needed. + - package:moduleB/b.dart + expectedContent: + org-dartlang-test:///a.dart: + - Field foo + - Field foo2 + package:moduleB/b.dart: + - Class A2 + - Field bVar + - Field bVarFromC + - Field mya2 + package:moduleC/c.dart: + - Class A3 + - Field baz + - Field baz2 + package:moduleD/d.dart: + - Field baz3 \ No newline at end of file diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_6.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_6.yaml new file mode 100644 index 00000000000..89367ec7454 --- /dev/null +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_6.yaml @@ -0,0 +1,86 @@ +# Copyright (c) 2019, 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.md file. + +# Compile an application with a number of modules. +# Try out "trickery" in the module dependencies to force needing more or less +# dill libraries. + +type: newworld +strong: true +modules: + moduleB: + moduleB/b.dart: | + import 'package:moduleC/c.dart'; + + var bVar = 42; + var bVarFromC = baz2; + var mya2 = new A2(); + + class A2 extends A3 { + int bar = 42; + } + moduleB/.packages: | + moduleB:. + moduleC:../moduleC + moduleD:../moduleD + moduleC: + moduleC/c.dart: | + import 'package:moduleD/d.dart'; + + String baz = "42"; + String baz2 = baz3; + + class A3 { + int foo = 42; + } + moduleC/.packages: | + moduleC:. + moduleD:../moduleD + moduleD: + moduleD/d.dart: | + String baz3 = "baz3"; + moduleD/.packages: | + moduleD:. +worlds: + - entry: a.dart + fromComponent: true + sources: + a.dart: | + import "package:moduleB/b.dart"; + + var foo = bVar; + var foo2 = bVarFromC; + + class A1 extends A2 { + String fooMethod() => "42!"; + } + .packages: | + moduleB:moduleB + moduleC:moduleC + moduleD:moduleD + modules: + - moduleB + - moduleC + - moduleD + expectedLibraryCount: 4 + neededDillLibraries: + # A class in 'a.dart' has a super in 'c.dart'. + - package:moduleB/b.dart + - package:moduleC/c.dart + expectedContent: + org-dartlang-test:///a.dart: + - Field foo + - Field foo2 + - Class A1 + package:moduleB/b.dart: + - Class A2 + - Field bVar + - Field bVarFromC + - Field mya2 + package:moduleC/c.dart: + - Class A3 + - Field baz + - Field baz2 + package:moduleD/d.dart: + - Field baz3 \ No newline at end of file diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_7.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_7.yaml new file mode 100644 index 00000000000..3ad421d05a9 --- /dev/null +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_7.yaml @@ -0,0 +1,123 @@ +# Copyright (c) 2019, 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.md file. + +# Compile an application with a number of modules. +# Try out "trickery" in the module dependencies to force needing more or less +# dill libraries. + +type: newworld +strong: true +modules: + module: + module/lib1.dart: | + import 'lib3.dart'; + + class Class1 { + Class3a get c3a {} + } + module/lib2.dart: | + import 'lib4.dart'; + + class Class2 { + void cl4() { + Class4 class4 = new Class4(); + print(class4); + } + } + module/lib3.dart: | + class Class3a { + Class3b get c3b {} + } + + class Class3b { + String str() {} + } + module/lib4.dart: | + class Class4 {} + module/.packages: | + module:. +worlds: + - entry: compileme.dart + fromComponent: true + sources: + compileme.dart: | + import 'package:module/lib1.dart'; + import 'package:module/lib2.dart'; + + main() { + Class2 class2 = new Class2(); + print(class2); + } + + class Foo { + Class1 class1() { + Class1 class1 = new Class1(); + class1.c3a.c3b.str(); + return class1; + } + } + .packages: | + module:module + modules: + - module + expectedLibraryCount: 5 + neededDillLibraries: + - package:module/lib1.dart + - package:module/lib2.dart + - package:module/lib3.dart + expectedContent: + org-dartlang-test:///compileme.dart: + - Class Foo + - Procedure main + package:module/lib1.dart: + - Class Class1 + package:module/lib2.dart: + - Class Class2 + package:module/lib3.dart: + - Class Class3a + - Class Class3b + package:module/lib4.dart: + - Class Class4 + - entry: compileme.dart + outlineOnly: true + skipOutlineBodyCheck: true + sources: + compileme.dart: | + import 'package:module/lib1.dart'; + import 'package:module/lib2.dart'; + + main() { + Class2 class2 = new Class2(); + print(class2); + } + + class Foo { + Class1 class1() { + Class1 class1 = new Class1(); + class1.c3a.c3b.str(); + return class1; + } + } + .packages: | + module:module + modules: + - module + expectedLibraryCount: 5 + neededDillLibraries: + # This is the outline version. It doesn't use lib3.dart. + - package:module/lib1.dart + - package:module/lib2.dart + expectedContent: + org-dartlang-test:///compileme.dart: + - Class Foo + - Procedure main + package:module/lib1.dart: + - Class Class1 + package:module/lib2.dart: + - Class Class2 + package:module/lib3.dart: + - Class Class3a + - Class Class3b + package:module/lib4.dart: + - Class Class4 diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_8.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_8.yaml new file mode 100644 index 00000000000..dbe8c91d712 --- /dev/null +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/changing_modules_8.yaml @@ -0,0 +1,61 @@ +# Copyright (c) 2019, 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.md file. + +# Compile an application with a number of modules. +# Try out "trickery" in the module dependencies to force needing more or less +# dill libraries. + +type: newworld +strong: true +modules: + module: + module/lib1.dart: | + import 'lib2.dart'; + + abstract class XSet { + factory XSet.identity() = XLinkedHashSet.identity; + } + module/lib2.dart: | + import 'lib1.dart'; + import 'lib3.dart'; + + class XLinkedHashSet implements XSet { + factory XLinkedHashSet.identity() = XIdentityHashSet; + } + module/lib3.dart: | + import 'lib2.dart'; + + class XIdentityHashSet implements XLinkedHashSet { + XIdentityHashSet(); + } + module/.packages: | + module:. +worlds: + - entry: compileme.dart + fromComponent: true + sources: + compileme.dart: | + import 'package:module/lib1.dart'; + + main() { + XSet history = new XSet.identity(); + } + .packages: | + module:module + modules: + - module + expectedLibraryCount: 4 + neededDillLibraries: + - package:module/lib1.dart + - package:module/lib2.dart + - package:module/lib3.dart + expectedContent: + org-dartlang-test:///compileme.dart: + - Procedure main + package:module/lib1.dart: + - Class XSet + package:module/lib2.dart: + - Class XLinkedHashSet + package:module/lib3.dart: + - Class XIdentityHashSet diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/crash_test_1.yaml b/pkg/front_end/testcases/incremental_initialize_from_dill/crash_test_1.yaml new file mode 100644 index 00000000000..404e5a520b3 --- /dev/null +++ b/pkg/front_end/testcases/incremental_initialize_from_dill/crash_test_1.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2019, 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.md file. + +# This have crashed at one point (assertion error). + +type: newworld +strong: true +worlds: + - entry: main.dart + sources: + main.dart: | + class C { + C(); + factory C.e4() async = C; + } + + void main() { + var c = new C.e4(); + } + expectedLibraryCount: 1 + errors: true diff --git a/pkg/kernel/lib/class_hierarchy.dart b/pkg/kernel/lib/class_hierarchy.dart index ed156b124aa..7be58d15c99 100644 --- a/pkg/kernel/lib/class_hierarchy.dart +++ b/pkg/kernel/lib/class_hierarchy.dart @@ -332,17 +332,18 @@ class _ClassInfoSubtype { class _ClosedWorldClassHierarchySubtypes implements ClassHierarchySubtypes { final ClosedWorldClassHierarchy hierarchy; final List _classesByTopDownIndex; - final Map _infoFor = {}; + final Map _infoMap = {}; bool invalidated = false; _ClosedWorldClassHierarchySubtypes(this.hierarchy) - : _classesByTopDownIndex = new List(hierarchy._infoFor.length) { - if (hierarchy._infoFor.isNotEmpty) { - for (Class class_ in hierarchy._infoFor.keys) { - _infoFor[class_] = new _ClassInfoSubtype(hierarchy._infoFor[class_]); + : _classesByTopDownIndex = new List(hierarchy._infoMap.length) { + hierarchy.allBetsOff = true; + if (hierarchy._infoMap.isNotEmpty) { + for (Class class_ in hierarchy._infoMap.keys) { + _infoMap[class_] = new _ClassInfoSubtype(hierarchy._infoMap[class_]); } - _topDownSortVisit(_infoFor[hierarchy._infoFor.keys.first]); + _topDownSortVisit(_infoMap[hierarchy._infoMap.keys.first]); } } @@ -356,17 +357,17 @@ class _ClosedWorldClassHierarchySubtypes implements ClassHierarchySubtypes { _classesByTopDownIndex[index] = subInfo.classInfo.classNode; var subtypeSetBuilder = new _IntervalListBuilder()..addSingleton(index); for (_ClassInfo subtype in subInfo.classInfo.directExtenders) { - _ClassInfoSubtype subtypeInfo = _infoFor[subtype.classNode]; + _ClassInfoSubtype subtypeInfo = _infoMap[subtype.classNode]; _topDownSortVisit(subtypeInfo); subtypeSetBuilder.addIntervalList(subtypeInfo.subtypeIntervalList); } for (_ClassInfo subtype in subInfo.classInfo.directMixers) { - _ClassInfoSubtype subtypeInfo = _infoFor[subtype.classNode]; + _ClassInfoSubtype subtypeInfo = _infoMap[subtype.classNode]; _topDownSortVisit(subtypeInfo); subtypeSetBuilder.addIntervalList(subtypeInfo.subtypeIntervalList); } for (_ClassInfo subtype in subInfo.classInfo.directImplementers) { - _ClassInfoSubtype subtypeInfo = _infoFor[subtype.classNode]; + _ClassInfoSubtype subtypeInfo = _infoMap[subtype.classNode]; _topDownSortVisit(subtypeInfo); subtypeSetBuilder.addIntervalList(subtypeInfo.subtypeIntervalList); } @@ -399,7 +400,7 @@ class _ClosedWorldClassHierarchySubtypes implements ClassHierarchySubtypes { ClassSet getSubtypesOf(Class class_) { if (invalidated) throw "This datastructure has been invalidated"; Set result = new Set(); - Uint32List list = _infoFor[class_].subtypeIntervalList; + Uint32List list = _infoMap[class_].subtypeIntervalList; for (int i = 0; i < list.length; i += 2) { int from = list[i]; int to = list[i + 1]; @@ -433,17 +434,49 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { } /// The insert order is important. - final Map _infoFor = + final Map _infoMap = new LinkedHashMap(); + + _ClassInfo infoFor(Class c) { + _ClassInfo info = _infoMap[c]; + info?.used = true; + return info; + } + + List getUsedClasses() { + List result = new List(); + for (_ClassInfo classInfo in _infoMap.values) { + if (classInfo.used) { + result.add(classInfo.classNode); + } + } + return result; + } + + void resetUsed() { + for (_ClassInfo classInfo in _infoMap.values) { + classInfo.used = false; + } + allBetsOff = false; + } + final Set knownLibraries = new Set(); + bool allBetsOff = false; /// Recorded errors for classes we have already calculated the class hierarchy /// for, but will have to be reissued when re-using the calculation. final Map> _recordedAmbiguousSupertypes = new LinkedHashMap>(); - Iterable get classes => _infoFor.keys; - int get numberOfClasses => _infoFor.length; + Iterable get classes { + allBetsOff = true; + return _infoMap.keys; + } + + int get numberOfClasses { + allBetsOff = true; + return _infoMap.length; + } _ClosedWorldClassHierarchySubtypes _cachedClassHierarchySubtypes; @@ -461,24 +494,25 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { @override Iterable getOrderedClasses(Iterable unordered) { var unorderedSet = unordered.toSet(); - return _infoFor.keys.where(unorderedSet.contains); + for (Class c in unordered) _infoMap[c]?.used = true; + return _infoMap.keys.where(unorderedSet.contains); } @override bool isSubclassOf(Class subclass, Class superclass) { if (identical(subclass, superclass)) return true; - return _infoFor[subclass].isSubclassOf(_infoFor[superclass]); + return infoFor(subclass).isSubclassOf(infoFor(superclass)); } @override bool isSubtypeOf(Class subtype, Class superclass) { if (identical(subtype, superclass)) return true; - return _infoFor[subtype].isSubtypeOf(_infoFor[superclass]); + return infoFor(subtype).isSubtypeOf(infoFor(superclass)); } @override bool isUsedAsMixin(Class class_) { - return _infoFor[class_].directMixers.isNotEmpty; + return infoFor(class_).directMixers.isNotEmpty; } List<_ClassInfo> _getRankedSuperclassInfos(_ClassInfo info) { @@ -494,7 +528,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { lastInfo = nextInfo; var classNode = nextInfo.classNode; void addToHeap(Supertype supertype) { - heap.add(_infoFor[supertype.classNode]); + heap.add(infoFor(supertype.classNode)); } if (classNode.supertype != null) addToHeap(classNode.supertype); @@ -526,8 +560,8 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { // Compute the list of superclasses for both types, with the above // optimization. - _ClassInfo info1 = _infoFor[type1.classNode]; - _ClassInfo info2 = _infoFor[type2.classNode]; + _ClassInfo info1 = infoFor(type1.classNode); + _ClassInfo info2 = infoFor(type2.classNode); List<_ClassInfo> classes1; List<_ClassInfo> classes2; if (identical(info1, info2) || info1.isSubtypeOf(info2)) { @@ -603,11 +637,11 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { @override Supertype getClassAsInstanceOf(Class class_, Class superclass) { if (identical(class_, superclass)) return class_.asThisSupertype; - _ClassInfo info = _infoFor[class_]; + _ClassInfo info = infoFor(class_); if (info == null) { throw "${class_.fileUri}: No class info for ${class_.name}"; } - _ClassInfo superInfo = _infoFor[superclass]; + _ClassInfo superInfo = infoFor(superclass); if (superInfo == null) { throw "${superclass.fileUri}: No class info for ${superclass.name}"; } @@ -626,7 +660,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { @override Member getDispatchTarget(Class class_, Name name, {bool setter: false}) { - _ClassInfo info = _infoFor[class_]; + _ClassInfo info = infoFor(class_); List list = setter ? info.implementedSetters : info.implementedGettersAndCalls; return ClassHierarchy.findMemberByName(list, name); @@ -634,7 +668,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { @override List getDispatchTargets(Class class_, {bool setters: false}) { - _ClassInfo info = _infoFor[class_]; + _ClassInfo info = infoFor(class_); return setters ? info.implementedSetters : info.implementedGettersAndCalls; } @@ -646,12 +680,12 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { @override List getInterfaceMembers(Class class_, {bool setters: false}) { - return _buildInterfaceMembers(class_, _infoFor[class_], setters: setters); + return _buildInterfaceMembers(class_, infoFor(class_), setters: setters); } @override List getDeclaredMembers(Class class_, {bool setters: false}) { - var info = _infoFor[class_]; + var info = infoFor(class_); return setters ? info.declaredSetters : info.declaredGettersAndCalls; } @@ -659,7 +693,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { void forEachOverridePair(Class class_, callback(Member declaredMember, Member interfaceMember, bool isSetter), {bool crossGettersSetters: false}) { - _ClassInfo info = _infoFor[class_]; + _ClassInfo info = infoFor(class_); for (var supertype in class_.supers) { var superclass = supertype.classNode; var superGetters = getInterfaceMembers(superclass); @@ -697,7 +731,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { @override List genericSupertypesOf(Class class_) { - final supertypes = _infoFor[class_].genericSuperTypes; + final supertypes = infoFor(class_).genericSuperTypes; if (supertypes == null) return const []; // Multiple supertypes can arise from ambiguous supertypes. The first // supertype is the real one; the others are purely informational. @@ -712,18 +746,18 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { for (Library lib in removedLibraries) { if (!knownLibraries.contains(lib)) continue; for (Class class_ in lib.classes) { - _ClassInfo info = _infoFor[class_]; + _ClassInfo info = _infoMap[class_]; if (class_.supertype != null) { - _infoFor[class_.supertype.classNode]?.directExtenders?.remove(info); + _infoMap[class_.supertype.classNode]?.directExtenders?.remove(info); } if (class_.mixedInType != null) { - _infoFor[class_.mixedInType.classNode]?.directMixers?.remove(info); + _infoMap[class_.mixedInType.classNode]?.directMixers?.remove(info); } for (var supertype in class_.implementedTypes) { - _infoFor[supertype.classNode]?.directImplementers?.remove(info); + _infoMap[supertype.classNode]?.directImplementers?.remove(info); } - _infoFor.remove(class_); + _infoMap.remove(class_); _recordedAmbiguousSupertypes.remove(class_); } knownLibraries.remove(lib); @@ -776,7 +810,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { Set<_ClassInfo> processedClasses = new Set<_ClassInfo>(); List<_ClassInfo> worklist = <_ClassInfo>[]; for (Class class_ in classes) { - _ClassInfo info = _infoFor[class_]; + _ClassInfo info = infoFor(class_); worklist.add(info); } @@ -791,7 +825,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { infos.addAll(processedClasses); } else { for (Class class_ in classes) { - _ClassInfo info = _infoFor[class_]; + _ClassInfo info = infoFor(class_); infos.add(info); } } @@ -816,11 +850,11 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { } bool sanityCheckAlsoKnowsParentAndLibrary() { - for (Class c in _infoFor.keys) { + for (Class c in _infoMap.keys) { if (!knownLibraries.contains(c.enclosingLibrary)) { throw new StateError("Didn't know library of $c (from ${c.fileUri})"); } - if (c.supertype != null && _infoFor[c.supertype.classNode] == null) { + if (c.supertype != null && _infoMap[c.supertype.classNode] == null) { throw new StateError("Didn't know parent of $c (from ${c.fileUri})"); } } @@ -837,7 +871,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { if (type.classNode == superclass) { return superclass.asThisSupertype; } - var map = _infoFor[type.classNode]?.genericSuperTypes; + var map = infoFor(type.classNode)?.genericSuperTypes; return map == null ? null : map[superclass]?.first; } @@ -850,7 +884,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { knownLibraries.add(library); } - _initializeTopologicallySortedClasses(_infoFor.keys, 0); + _initializeTopologicallySortedClasses(_infoMap.keys, 0); } /// - Build index of direct children. @@ -864,15 +898,15 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { Iterable classes, int expectedStartingTopologicalIndex) { int i = expectedStartingTopologicalIndex; for (Class class_ in classes) { - _ClassInfo info = _infoFor[class_]; + _ClassInfo info = _infoMap[class_]; if (class_.supertype != null) { - _infoFor[class_.supertype.classNode].directExtenders.add(info); + _infoMap[class_.supertype.classNode].directExtenders.add(info); } if (class_.mixedInType != null) { - _infoFor[class_.mixedInType.classNode].directMixers.add(info); + _infoMap[class_.mixedInType.classNode].directMixers.add(info); } for (var supertype in class_.implementedTypes) { - _infoFor[supertype.classNode].directImplementers.add(info); + _infoMap[supertype.classNode].directImplementers.add(info); } _collectSupersForClass(class_); @@ -909,7 +943,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { int _topSortIndex = 0; int _topologicalSortVisit(Class classNode, Set beingVisited, {List orderedList}) { - var info = _infoFor[classNode]; + var info = _infoMap[classNode]; if (info != null) { return info.depth; } @@ -943,7 +977,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { _buildImplementedMembers(classNode, info); info.topologicalIndex = _topSortIndex++; - _infoFor[classNode] = info; + _infoMap[classNode] = info; orderedList?.add(classNode); beingVisited.remove(classNode); return info.depth = superDepth + 1; @@ -951,7 +985,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { void _buildDeclaredMembers(Class classNode, _ClassInfo info) { if (classNode.mixedInType != null) { - _ClassInfo mixedInfo = _infoFor[classNode.mixedInType.classNode]; + _ClassInfo mixedInfo = _infoMap[classNode.mixedInType.classNode]; List declaredGettersAndCalls = []; for (Member mixinMember in mixedInfo.declaredGettersAndCalls) { @@ -1004,7 +1038,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { if (classNode.supertype == null) { inheritedMembers = inheritedSetters = const []; } else { - _ClassInfo superInfo = _infoFor[classNode.supertype.classNode]; + _ClassInfo superInfo = _infoMap[classNode.supertype.classNode]; inheritedMembers = superInfo.implementedGettersAndCalls; inheritedSetters = superInfo.implementedSetters; } @@ -1030,7 +1064,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { void inheritFrom(Supertype type) { if (type == null) return; List inherited = _buildInterfaceMembers( - type.classNode, _infoFor[type.classNode], + type.classNode, _infoMap[type.classNode], setters: setters); inherited = _getUnshadowedInheritedMembers(declared, inherited); allInheritedMembers = @@ -1136,7 +1170,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { } void _recordSuperTypes(_ClassInfo subInfo, Supertype supertype) { - _ClassInfo superInfo = _infoFor[supertype.classNode]; + _ClassInfo superInfo = _infoMap[supertype.classNode]; if (supertype.typeArguments.isEmpty) { if (superInfo.genericSuperTypes == null) return; // Copy over the super type entries. @@ -1169,7 +1203,7 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { /// Note that the super class and super types of the class must already have /// had their supers collected. void _collectSupersForClass(Class class_) { - _ClassInfo info = _infoFor[class_]; + _ClassInfo info = _infoMap[class_]; var superclassSetBuilder = new _IntervalListBuilder() ..addSingleton(info.topologicalIndex); @@ -1177,20 +1211,20 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { ..addSingleton(info.topologicalIndex); if (class_.supertype != null) { - _ClassInfo supertypeInfo = _infoFor[class_.supertype.classNode]; + _ClassInfo supertypeInfo = _infoMap[class_.supertype.classNode]; superclassSetBuilder .addIntervalList(supertypeInfo.superclassIntervalList); supertypeSetBuilder.addIntervalList(supertypeInfo.supertypeIntervalList); } if (class_.mixedInType != null) { - _ClassInfo mixedInTypeInfo = _infoFor[class_.mixedInType.classNode]; + _ClassInfo mixedInTypeInfo = _infoMap[class_.mixedInType.classNode]; supertypeSetBuilder .addIntervalList(mixedInTypeInfo.supertypeIntervalList); } for (Supertype supertype in class_.implementedTypes) { - _ClassInfo supertypeInfo = _infoFor[supertype.classNode]; + _ClassInfo supertypeInfo = _infoMap[supertype.classNode]; supertypeSetBuilder.addIntervalList(supertypeInfo.supertypeIntervalList); } @@ -1205,8 +1239,8 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { /// internal data structure is. List getExpenseHistogram() { var result = []; - for (Class class_ in _infoFor.keys) { - var info = _infoFor[class_]; + for (Class class_ in _infoMap.keys) { + var info = _infoMap[class_]; int intervals = info.supertypeIntervalList.length ~/ 2; if (intervals >= result.length) { int oldLength = result.length; @@ -1226,8 +1260,8 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { double getCompressionRatio() { int intervals = 0; int sizes = 0; - for (Class class_ in _infoFor.keys) { - var info = _infoFor[class_]; + for (Class class_ in _infoMap.keys) { + var info = _infoMap[class_]; intervals += (info.superclassIntervalList.length + info.supertypeIntervalList.length) ~/ 2; @@ -1241,8 +1275,8 @@ class ClosedWorldClassHierarchy implements ClassHierarchy { /// Returns the number of entries in hash tables storing hierarchy data. int getSuperTypeHashTableSize() { int sum = 0; - for (Class class_ in _infoFor.keys) { - sum += _infoFor[class_].genericSuperTypes?.length ?? 0; + for (Class class_ in _infoMap.keys) { + sum += _infoMap[class_].genericSuperTypes?.length ?? 0; } return sum; } @@ -1334,6 +1368,7 @@ int _intervalListSize(Uint32List intervalList) { } class _ClassInfo { + bool used = false; final Class classNode; int topologicalIndex = 0; int depth = 0;