[dart2js] Move determination of fragments to emitter phase.
Change-Id: I0aaf02a8900445fc76b87de17e21b6488ca2fe46 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/191240 Reviewed-by: Stephen Adams <sra@google.com> Commit-Queue: Joshua Litt <joshualitt@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
7f3f3e66ac
commit
fd4eefc22d
@@ -14,8 +14,7 @@ import '../compiler_new.dart' show OutputType;
|
||||
import 'common/metrics.dart' show Metric, Metrics, CountMetric, DurationMetric;
|
||||
import 'common/tasks.dart' show CompilerTask;
|
||||
import 'common.dart';
|
||||
import 'common_elements.dart'
|
||||
show CommonElements, ElementEnvironment, KElementEnvironment;
|
||||
import 'common_elements.dart' show CommonElements, KElementEnvironment;
|
||||
import 'compiler.dart' show Compiler;
|
||||
import 'constants/values.dart'
|
||||
show
|
||||
@@ -89,13 +88,13 @@ class _DeferredLoadTaskMetrics implements Metrics {
|
||||
String get namespace => 'deferred_load';
|
||||
|
||||
DurationMetric time = DurationMetric('time');
|
||||
CountMetric hunkListElements = CountMetric('hunkListElements');
|
||||
CountMetric outputUnitElements = CountMetric('outputUnitElements');
|
||||
|
||||
@override
|
||||
Iterable<Metric> get primary => [time];
|
||||
|
||||
@override
|
||||
Iterable<Metric> get secondary => [hunkListElements];
|
||||
Iterable<Metric> get secondary => [outputUnitElements];
|
||||
}
|
||||
|
||||
/// For each deferred import, find elements and constants to be loaded when that
|
||||
@@ -119,7 +118,7 @@ class DeferredLoadTask extends CompilerTask {
|
||||
|
||||
/// A cache of the result of calling `computeImportDeferName` on the keys of
|
||||
/// this map.
|
||||
final Map<ImportEntity, String> _importDeferName = {};
|
||||
final Map<ImportEntity, String> importDeferName = {};
|
||||
|
||||
/// A mapping from classes to their import set.
|
||||
Map<ClassEntity, ImportSet> _classToSet = {};
|
||||
@@ -655,6 +654,7 @@ class DeferredLoadTask extends CompilerTask {
|
||||
counter++;
|
||||
importSet.unit = unit;
|
||||
_allOutputUnits.add(unit);
|
||||
metrics.outputUnitElements.add(1);
|
||||
}
|
||||
|
||||
// Generate an output unit for all import sets that are associated with an
|
||||
@@ -669,8 +669,7 @@ class DeferredLoadTask extends CompilerTask {
|
||||
_allOutputUnits.sort();
|
||||
}
|
||||
|
||||
Map<String, List<OutputUnit>> _setupHunksToLoad() {
|
||||
Map<String, List<OutputUnit>> hunksToLoad = {};
|
||||
void _setupImportNames() {
|
||||
Set<String> usedImportNames = {};
|
||||
|
||||
for (ImportEntity import in _allDeferredImports) {
|
||||
@@ -679,38 +678,8 @@ class DeferredLoadTask extends CompilerTask {
|
||||
// Note: tools that process the json file to build multi-part initial load
|
||||
// bundles depend on the fact that makeUnique appends only digits, or a
|
||||
// period followed by digits.
|
||||
_importDeferName[import] = makeUnique(result, usedImportNames, '.');
|
||||
importDeferName[import] = makeUnique(result, usedImportNames, '.');
|
||||
}
|
||||
|
||||
// Sort the output units in descending order of the number of imports they
|
||||
// include.
|
||||
|
||||
// The loading of the output units must be ordered because a superclass
|
||||
// needs to be initialized before its subclass.
|
||||
// But a class can only depend on another class in an output unit shared by
|
||||
// a strict superset of the imports:
|
||||
// By contradiction: Assume a class C in output unit shared by imports in
|
||||
// the set S1 = (lib1,.., lib_n) depends on a class D in an output unit
|
||||
// shared by S2 such that S2 not a superset of S1. Let lib_s be a library in
|
||||
// S1 not in S2. lib_s must depend on C, and then in turn on D. Therefore D
|
||||
// is not in the right output unit.
|
||||
List<OutputUnit> sortedOutputUnits = _allOutputUnits.reversed.toList();
|
||||
|
||||
// For each deferred import we find out which outputUnits to load.
|
||||
for (ImportEntity import in _allDeferredImports) {
|
||||
// We expect to find an entry for any call to `loadLibrary`, even if
|
||||
// there is no code to load. In that case, the entry will be an empty
|
||||
// list.
|
||||
hunksToLoad[_importDeferName[import]] = [];
|
||||
for (OutputUnit outputUnit in sortedOutputUnits) {
|
||||
if (outputUnit == _mainOutputUnit) continue;
|
||||
if (outputUnit.imports.contains(import)) {
|
||||
hunksToLoad[_importDeferName[import]].add(outputUnit);
|
||||
metrics.hunkListElements.add(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hunksToLoad;
|
||||
}
|
||||
|
||||
/// Returns a name for a deferred import.
|
||||
@@ -880,7 +849,7 @@ class DeferredLoadTask extends CompilerTask {
|
||||
|
||||
OutputUnitData _buildResult() {
|
||||
_createOutputUnits();
|
||||
Map<String, List<OutputUnit>> hunksToLoad = _setupHunksToLoad();
|
||||
_setupImportNames();
|
||||
if (compiler.options.deferredGraphUri != null) {
|
||||
_dumpDeferredGraph();
|
||||
}
|
||||
@@ -911,8 +880,7 @@ class DeferredLoadTask extends CompilerTask {
|
||||
localFunctionMap,
|
||||
constantMap,
|
||||
_allOutputUnits,
|
||||
_importDeferName,
|
||||
hunksToLoad,
|
||||
importDeferName,
|
||||
_deferredImportDescriptions);
|
||||
}
|
||||
|
||||
@@ -1031,10 +999,10 @@ class ImportDescription {
|
||||
/// The prefix this import is imported as.
|
||||
final String prefix;
|
||||
|
||||
final LibraryEntity _importingLibrary;
|
||||
final LibraryEntity importingLibrary;
|
||||
|
||||
ImportDescription.internal(
|
||||
this.importingUri, this.prefix, this._importingLibrary);
|
||||
this.importingUri, this.prefix, this.importingLibrary);
|
||||
|
||||
ImportDescription(
|
||||
ImportEntity import, LibraryEntity importingLibrary, Uri mainLibraryUri)
|
||||
@@ -1412,20 +1380,11 @@ class OutputUnitData {
|
||||
final Map<Local, OutputUnit> _localFunctionToUnit;
|
||||
final Map<ConstantValue, OutputUnit> _constantToUnit;
|
||||
final List<OutputUnit> outputUnits;
|
||||
final Map<ImportEntity, String> _importDeferName;
|
||||
|
||||
/// A mapping from the name of a defer import to all the output units it
|
||||
/// depends on in a list of lists to be loaded in the order they appear.
|
||||
///
|
||||
/// For example {"lib1": [[lib1_lib2_lib3], [lib1_lib2, lib1_lib3],
|
||||
/// [lib1]]} would mean that in order to load "lib1" first the hunk
|
||||
/// lib1_lib2_lib2 should be loaded, then the hunks lib1_lib2 and lib1_lib3
|
||||
/// can be loaded in parallel. And finally lib1 can be loaded.
|
||||
final Map<String, List<OutputUnit>> hunksToLoad;
|
||||
final Map<ImportEntity, String> importDeferName;
|
||||
|
||||
/// Because the token-stream is forgotten later in the program, we cache a
|
||||
/// description of each deferred import.
|
||||
final Map<ImportEntity, ImportDescription> _deferredImportDescriptions;
|
||||
final Map<ImportEntity, ImportDescription> deferredImportDescriptions;
|
||||
|
||||
OutputUnitData(
|
||||
this.isProgramSplit,
|
||||
@@ -1436,9 +1395,8 @@ class OutputUnitData {
|
||||
this._localFunctionToUnit,
|
||||
this._constantToUnit,
|
||||
this.outputUnits,
|
||||
this._importDeferName,
|
||||
this.hunksToLoad,
|
||||
this._deferredImportDescriptions);
|
||||
this.importDeferName,
|
||||
this.deferredImportDescriptions);
|
||||
|
||||
// Creates J-world data from the K-world data.
|
||||
factory OutputUnitData.from(
|
||||
@@ -1461,12 +1419,12 @@ class OutputUnitData {
|
||||
Map<ConstantValue, OutputUnit> constantToUnit =
|
||||
convertConstantMap(other._constantToUnit);
|
||||
Map<ImportEntity, ImportDescription> deferredImportDescriptions = {};
|
||||
other._deferredImportDescriptions
|
||||
other.deferredImportDescriptions
|
||||
.forEach((ImportEntity import, ImportDescription description) {
|
||||
deferredImportDescriptions[import] = ImportDescription.internal(
|
||||
description.importingUri,
|
||||
description.prefix,
|
||||
convertLibrary(description._importingLibrary));
|
||||
convertLibrary(description.importingLibrary));
|
||||
});
|
||||
|
||||
return OutputUnitData(
|
||||
@@ -1479,8 +1437,7 @@ class OutputUnitData {
|
||||
const {},
|
||||
constantToUnit,
|
||||
other.outputUnits,
|
||||
other._importDeferName,
|
||||
other.hunksToLoad,
|
||||
other.importDeferName,
|
||||
deferredImportDescriptions);
|
||||
}
|
||||
|
||||
@@ -1511,11 +1468,6 @@ class OutputUnitData {
|
||||
});
|
||||
Map<ImportEntity, String> importDeferName =
|
||||
source.readImportMap(source.readString);
|
||||
Map<String, List<OutputUnit>> hunksToLoad = source.readStringMap(() {
|
||||
return source.readList(() {
|
||||
return outputUnits[source.readInt()];
|
||||
});
|
||||
});
|
||||
Map<ImportEntity, ImportDescription> deferredImportDescriptions =
|
||||
source.readImportMap(() {
|
||||
String importingUri = source.readString();
|
||||
@@ -1535,7 +1487,6 @@ class OutputUnitData {
|
||||
constantToUnit,
|
||||
outputUnits,
|
||||
importDeferName,
|
||||
hunksToLoad,
|
||||
deferredImportDescriptions);
|
||||
}
|
||||
|
||||
@@ -1564,18 +1515,12 @@ class OutputUnitData {
|
||||
sink.writeConstantMap(_constantToUnit, (OutputUnit outputUnit) {
|
||||
sink.writeInt(outputUnitIndices[outputUnit]);
|
||||
});
|
||||
sink.writeImportMap(_importDeferName, sink.writeString);
|
||||
sink.writeStringMap(hunksToLoad, (List<OutputUnit> outputUnits) {
|
||||
sink.writeList(
|
||||
outputUnits,
|
||||
(OutputUnit outputUnit) =>
|
||||
sink.writeInt(outputUnitIndices[outputUnit]));
|
||||
});
|
||||
sink.writeImportMap(_deferredImportDescriptions,
|
||||
sink.writeImportMap(importDeferName, sink.writeString);
|
||||
sink.writeImportMap(deferredImportDescriptions,
|
||||
(ImportDescription importDescription) {
|
||||
sink.writeString(importDescription.importingUri);
|
||||
sink.writeString(importDescription.prefix);
|
||||
sink.writeLibrary(importDescription._importingLibrary);
|
||||
sink.writeLibrary(importDescription.importingLibrary);
|
||||
});
|
||||
sink.end(tag);
|
||||
}
|
||||
@@ -1690,7 +1635,7 @@ class OutputUnitData {
|
||||
|
||||
/// Returns the unique name for the given deferred [import].
|
||||
String getImportDeferName(Spannable node, ImportEntity import) {
|
||||
String name = _importDeferName[import];
|
||||
String name = importDeferName[import];
|
||||
if (name == null) {
|
||||
throw SpannableAssertionFailure(node, "No deferred name for $import.");
|
||||
}
|
||||
@@ -1699,48 +1644,7 @@ class OutputUnitData {
|
||||
|
||||
/// Returns the names associated with each deferred import in [unit].
|
||||
Iterable<String> getImportNames(OutputUnit unit) {
|
||||
return unit.imports.map((i) => _importDeferName[i]);
|
||||
}
|
||||
|
||||
/// Returns a json-style map for describing what files that are loaded by a
|
||||
/// given deferred import.
|
||||
/// The mapping is structured as:
|
||||
/// library uri -> {"name": library name, "files": (prefix -> list of files)}
|
||||
/// Where
|
||||
///
|
||||
/// - <library uri> is the relative uri of the library making a deferred
|
||||
/// import.
|
||||
/// - <library name> is the name of the library, or "<unnamed>" if it is
|
||||
/// unnamed.
|
||||
/// - <prefix> is the `as` prefix used for a given deferred import.
|
||||
/// - <list of files> is a list of the filenames the must be loaded when that
|
||||
/// import is loaded.
|
||||
Map<String, Map<String, dynamic>> computeDeferredMap(
|
||||
CompilerOptions options, ElementEnvironment elementEnvironment,
|
||||
{Set<OutputUnit> omittedUnits}) {
|
||||
omittedUnits ??= Set();
|
||||
Map<String, Map<String, dynamic>> mapping = {};
|
||||
|
||||
_deferredImportDescriptions.keys.forEach((ImportEntity import) {
|
||||
List<OutputUnit> outputUnits = hunksToLoad[_importDeferName[import]];
|
||||
ImportDescription description = _deferredImportDescriptions[import];
|
||||
String getName(LibraryEntity library) {
|
||||
var name = elementEnvironment.getLibraryName(library);
|
||||
return name == '' ? '<unnamed>' : name;
|
||||
}
|
||||
|
||||
Map<String, dynamic> libraryMap = mapping.putIfAbsent(
|
||||
description.importingUri,
|
||||
() =>
|
||||
{"name": getName(description._importingLibrary), "imports": {}});
|
||||
|
||||
List<String> partFileNames = outputUnits
|
||||
.where((outputUnit) => !omittedUnits.contains(outputUnit))
|
||||
.map((outputUnit) => deferredPartFileName(options, outputUnit.name))
|
||||
.toList();
|
||||
libraryMap["imports"][_importDeferName[import]] = partFileNames;
|
||||
});
|
||||
return mapping;
|
||||
return unit.imports.map((i) => importDeferName[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -651,8 +651,11 @@ class DumpInfoTask extends CompilerTask implements InfoReporter {
|
||||
}
|
||||
}
|
||||
|
||||
result.deferredFiles = closedWorld.outputUnitData
|
||||
.computeDeferredMap(compiler.options, closedWorld.elementEnvironment);
|
||||
var fragmentsToLoad =
|
||||
compiler.backendStrategy.emitterTask.emitter.fragmentsToLoad;
|
||||
var fragmentMerger =
|
||||
compiler.backendStrategy.emitterTask.emitter.fragmentMerger;
|
||||
result.deferredFiles = fragmentMerger.computeDeferredMap(fragmentsToLoad);
|
||||
stopwatch.stop();
|
||||
|
||||
result.program = new ProgramInfo(
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
library dart2js.js_emitter.code_emitter_task;
|
||||
|
||||
import '../common.dart';
|
||||
import '../common/metrics.dart' show Metric, Metrics, CountMetric;
|
||||
import '../common/tasks.dart' show CompilerTask;
|
||||
import '../compiler.dart' show Compiler;
|
||||
import '../constants/values.dart';
|
||||
@@ -50,6 +51,9 @@ class CodeEmitterTask extends CompilerTask {
|
||||
/// Contains a list of all classes that are emitted.
|
||||
Set<ClassEntity> neededClasses;
|
||||
|
||||
@override
|
||||
final _EmitterMetrics metrics = _EmitterMetrics();
|
||||
|
||||
CodeEmitterTask(this._compiler, this._generateSourceMap)
|
||||
: super(_compiler.measurer);
|
||||
|
||||
@@ -209,6 +213,12 @@ abstract class Emitter implements ModularEmitter {
|
||||
|
||||
List<fragment_merger.PreFragment> get preDeferredFragmentsForTesting;
|
||||
|
||||
/// A map of loadId to list of [FinalizedFragments].
|
||||
Map<String, List<fragment_merger.FinalizedFragment>> get fragmentsToLoad;
|
||||
|
||||
/// The [FragmentMerger] itself.
|
||||
fragment_merger.FragmentMerger get fragmentMerger;
|
||||
|
||||
/// Uses the [programBuilder] to generate a model of the program, emits
|
||||
/// the program, and returns the size of the generated output.
|
||||
int emitProgram(ProgramBuilder programBuilder, CodegenWorld codegenWorld);
|
||||
@@ -228,3 +238,16 @@ abstract class Emitter implements ModularEmitter {
|
||||
/// Returns the size of the code generated for a given output [unit].
|
||||
int generatedSize(OutputUnit unit);
|
||||
}
|
||||
|
||||
class _EmitterMetrics implements Metrics {
|
||||
@override
|
||||
String get namespace => 'emitter';
|
||||
|
||||
CountMetric hunkListElements = CountMetric('hunkListElements');
|
||||
|
||||
@override
|
||||
Iterable<Metric> get primary => [];
|
||||
|
||||
@override
|
||||
Iterable<Metric> get secondary => [hunkListElements];
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ class Program {
|
||||
final bool needsNativeSupport;
|
||||
final bool hasSoftDeferredClasses;
|
||||
|
||||
/// A map from load id to the list of fragments that need to be loaded.
|
||||
final Map<String, List<Fragment>> loadMap;
|
||||
|
||||
// If this field is not `null` then its value must be emitted in the embedded
|
||||
// global `TYPE_TO_INTERCEPTOR_MAP`. The map references constants and classes.
|
||||
final js.Expression typeToInterceptorMap;
|
||||
@@ -33,7 +30,7 @@ class Program {
|
||||
final MetadataCollector _metadataCollector;
|
||||
final Iterable<js.TokenFinalizer> finalizers;
|
||||
|
||||
Program(this.fragments, this.holders, this.loadMap, this.typeToInterceptorMap,
|
||||
Program(this.fragments, this.holders, this.typeToInterceptorMap,
|
||||
this._metadataCollector, this.finalizers,
|
||||
{this.needsNativeSupport,
|
||||
this.outputContainsConstantList,
|
||||
|
||||
@@ -273,8 +273,8 @@ class ProgramBuilder {
|
||||
finalizers.add(namingFinalizer as js.TokenFinalizer);
|
||||
}
|
||||
|
||||
return new Program(fragments, holders, _buildLoadMap(),
|
||||
_buildTypeToInterceptorMap(), _task.metadataCollector, finalizers,
|
||||
return new Program(fragments, holders, _buildTypeToInterceptorMap(),
|
||||
_task.metadataCollector, finalizers,
|
||||
needsNativeSupport: needsNativeSupport,
|
||||
outputContainsConstantList: collector.outputContainsConstantList,
|
||||
hasSoftDeferredClasses: _notSoftDeferred != null);
|
||||
@@ -348,18 +348,6 @@ class ProgramBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a map from loadId to outputs-to-load.
|
||||
Map<String, List<Fragment>> _buildLoadMap() {
|
||||
Map<String, List<Fragment>> loadMap = <String, List<Fragment>>{};
|
||||
_closedWorld.outputUnitData.hunksToLoad
|
||||
.forEach((String loadId, List<OutputUnit> outputUnits) {
|
||||
loadMap[loadId] = outputUnits
|
||||
.map((OutputUnit unit) => _outputs[unit])
|
||||
.toList(growable: false);
|
||||
});
|
||||
return loadMap;
|
||||
}
|
||||
|
||||
js.Expression _buildTypeToInterceptorMap() {
|
||||
InterceptorStubGenerator stubGenerator = new InterceptorStubGenerator(
|
||||
_commonElements,
|
||||
|
||||
@@ -7,7 +7,6 @@ library dart2js.js_emitter.startup_emitter;
|
||||
import '../../../compiler_new.dart';
|
||||
import '../../common.dart';
|
||||
import '../../common/codegen.dart';
|
||||
import '../../common/tasks.dart';
|
||||
import '../../constants/values.dart';
|
||||
import '../../deferred_load.dart' show OutputUnit;
|
||||
import '../../dump_info.dart';
|
||||
@@ -20,7 +19,7 @@ import '../../js_backend/runtime_types_new.dart' show RecipeEncoder;
|
||||
import '../../options.dart';
|
||||
import '../../universe/codegen_world_builder.dart' show CodegenWorld;
|
||||
import '../../world.dart' show JClosedWorld;
|
||||
import '../js_emitter.dart' show Emitter, ModularEmitter;
|
||||
import '../js_emitter.dart' show CodeEmitterTask, Emitter, ModularEmitter;
|
||||
import '../model.dart';
|
||||
import '../native_emitter.dart';
|
||||
import '../program_builder/program_builder.dart' show ProgramBuilder;
|
||||
@@ -150,7 +149,7 @@ class EmitterImpl extends ModularEmitterBase implements Emitter {
|
||||
final DiagnosticReporter _reporter;
|
||||
final JClosedWorld _closedWorld;
|
||||
final RecipeEncoder _rtiRecipeEncoder;
|
||||
final CompilerTask _task;
|
||||
final CodeEmitterTask _task;
|
||||
ModelEmitter _emitter;
|
||||
final NativeEmitter _nativeEmitter;
|
||||
|
||||
@@ -160,6 +159,12 @@ class EmitterImpl extends ModularEmitterBase implements Emitter {
|
||||
@override
|
||||
List<PreFragment> preDeferredFragmentsForTesting;
|
||||
|
||||
@override
|
||||
Map<String, List<FinalizedFragment>> fragmentsToLoad;
|
||||
|
||||
@override
|
||||
FragmentMerger fragmentMerger;
|
||||
|
||||
EmitterImpl(
|
||||
CompilerOptions options,
|
||||
this._reporter,
|
||||
@@ -201,6 +206,11 @@ class EmitterImpl extends ModularEmitterBase implements Emitter {
|
||||
}
|
||||
return _task.measureSubtask('emit program', () {
|
||||
var size = _emitter.emitProgram(program, codegenWorld);
|
||||
fragmentsToLoad = _emitter.fragmentsToLoad;
|
||||
fragmentMerger = _emitter.fragmentMerger;
|
||||
fragmentsToLoad.values.forEach((fragments) {
|
||||
_task.metrics.hunkListElements.add(fragments.length);
|
||||
});
|
||||
if (retainDataForTesting) {
|
||||
preDeferredFragmentsForTesting =
|
||||
_emitter.preDeferredFragmentsForTesting;
|
||||
|
||||
@@ -730,7 +730,9 @@ class FragmentEmitter {
|
||||
}
|
||||
|
||||
js.Statement emitMainFragment(
|
||||
Program program, DeferredLoadingState deferredLoadingState) {
|
||||
Program program,
|
||||
Map<String, List<FinalizedFragment>> fragmentsToLoad,
|
||||
DeferredLoadingState deferredLoadingState) {
|
||||
MainFragment fragment = program.fragments.first;
|
||||
|
||||
Iterable<Holder> nonStaticStateHolders =
|
||||
@@ -778,8 +780,8 @@ class FragmentEmitter {
|
||||
'constants': emitConstants(fragment),
|
||||
'staticNonFinalFields': emitStaticNonFinalFields(fragment),
|
||||
'lazyStatics': emitLazilyInitializedStatics(fragment),
|
||||
'embeddedGlobalsPart1':
|
||||
emitEmbeddedGlobalsPart1(program, deferredLoadingState),
|
||||
'embeddedGlobalsPart1': emitEmbeddedGlobalsPart1(
|
||||
program, fragmentsToLoad, deferredLoadingState),
|
||||
'embeddedGlobalsPart2':
|
||||
emitEmbeddedGlobalsPart2(program, deferredLoadingState),
|
||||
'typeRules': emitTypeRules(fragment),
|
||||
@@ -1874,10 +1876,10 @@ class FragmentEmitter {
|
||||
// array of hashes indexed by part.
|
||||
// [deferredLoadHashes] may have missing entries to indicate empty parts.
|
||||
void finalizeDeferredLoadingData(
|
||||
Map<String, List<FinalizedFragment>> loadMap,
|
||||
Map<String, List<FinalizedFragment>> fragmentsToLoad,
|
||||
Map<FinalizedFragment, String> deferredLoadHashes,
|
||||
DeferredLoadingState deferredLoadingState) {
|
||||
if (loadMap.isEmpty) return;
|
||||
if (fragmentsToLoad.isEmpty) return;
|
||||
|
||||
Map<FinalizedFragment, int> fragmentIndexes = {};
|
||||
List<String> fragmentUris = [];
|
||||
@@ -1885,7 +1887,8 @@ class FragmentEmitter {
|
||||
|
||||
List<js.Property> libraryPartsMapEntries = [];
|
||||
|
||||
loadMap.forEach((String loadId, List<FinalizedFragment> fragmentList) {
|
||||
fragmentsToLoad
|
||||
.forEach((String loadId, List<FinalizedFragment> fragmentList) {
|
||||
List<js.Expression> indexes = [];
|
||||
for (FinalizedFragment fragment in fragmentList) {
|
||||
String fragmentHash = deferredLoadHashes[fragment];
|
||||
@@ -1971,10 +1974,12 @@ class FragmentEmitter {
|
||||
|
||||
/// Emits all embedded globals.
|
||||
js.Statement emitEmbeddedGlobalsPart1(
|
||||
Program program, DeferredLoadingState deferredLoadingState) {
|
||||
Program program,
|
||||
Map<String, List<FinalizedFragment>> fragmentsToLoad,
|
||||
DeferredLoadingState deferredLoadingState) {
|
||||
List<js.Property> globals = [];
|
||||
|
||||
if (program.loadMap.isNotEmpty) {
|
||||
if (fragmentsToLoad.isNotEmpty) {
|
||||
globals
|
||||
.addAll(emitEmbeddedGlobalsForDeferredLoading(deferredLoadingState));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:collection';
|
||||
import '../../common_elements.dart' show ElementEnvironment;
|
||||
import '../../deferred_load.dart'
|
||||
show ImportDescription, OutputUnit, OutputUnitData, deferredPartFileName;
|
||||
import '../../elements/entities.dart';
|
||||
import '../../deferred_load.dart' show OutputUnit;
|
||||
import '../../js/js.dart' as js;
|
||||
import '../../js/size_estimator.dart';
|
||||
@@ -24,6 +28,7 @@ class PreFragment {
|
||||
final List<js.Statement> nativeSupport = [];
|
||||
final Set<PreFragment> successors = {};
|
||||
final Set<PreFragment> predecessors = {};
|
||||
FinalizedFragment finalizedFragment;
|
||||
int size = 0;
|
||||
|
||||
PreFragment(
|
||||
@@ -103,8 +108,8 @@ class PreFragment {
|
||||
}
|
||||
|
||||
FinalizedFragment finalize(
|
||||
Program program, Map<Fragment, FinalizedFragment> fragmentMap) {
|
||||
FinalizedFragment finalizedFragment;
|
||||
Program program, Map<OutputUnit, FinalizedFragment> outputUnitMap) {
|
||||
assert(finalizedFragment == null);
|
||||
var seedFragment = fragments.first;
|
||||
var seedOutputUnit = seedFragment.outputUnit;
|
||||
|
||||
@@ -128,7 +133,7 @@ class PreFragment {
|
||||
lazyInitializers.first,
|
||||
nativeSupport.first,
|
||||
program.metadataTypesForOutputUnit(seedOutputUnit));
|
||||
fragmentMap[seedFragment] = finalizedFragment;
|
||||
outputUnitMap[seedOutputUnit] = finalizedFragment;
|
||||
} else {
|
||||
List<OutputUnit> outputUnits = [seedOutputUnit];
|
||||
List<Library> libraries = [];
|
||||
@@ -156,8 +161,8 @@ class PreFragment {
|
||||
js.Block(lazyInitializers),
|
||||
js.Block(nativeSupport),
|
||||
program.metadataTypesForOutputUnit(seedOutputUnit));
|
||||
for (var fragment in fragments) {
|
||||
fragmentMap[fragment] = finalizedFragment;
|
||||
for (var outputUnit in outputUnits) {
|
||||
outputUnitMap[outputUnit] = finalizedFragment;
|
||||
}
|
||||
}
|
||||
return finalizedFragment;
|
||||
@@ -294,28 +299,32 @@ class _Partition {
|
||||
|
||||
class FragmentMerger {
|
||||
final CompilerOptions _options;
|
||||
final ElementEnvironment _elementEnvironment;
|
||||
final OutputUnitData outputUnitData;
|
||||
int totalSize = 0;
|
||||
|
||||
FragmentMerger(this._options);
|
||||
FragmentMerger(this._options, this._elementEnvironment, this.outputUnitData);
|
||||
|
||||
// Converts a map of (loadId, List<fragments>) to a map of
|
||||
// Converts a map of (loadId, List<OutputUnit>) to a map of
|
||||
// (loadId, List<FinalizedFragment>).
|
||||
static Map<String, List<FinalizedFragment>> processLoadMap(
|
||||
Map<String, List<Fragment>> programLoadMap,
|
||||
Map<Fragment, FinalizedFragment> fragmentMap) {
|
||||
Map<String, List<FinalizedFragment>> loadMap = {};
|
||||
programLoadMap.forEach((loadId, fragments) {
|
||||
Map<String, List<FinalizedFragment>> computeFragmentsToLoad(
|
||||
Map<String, List<OutputUnit>> outputUnitsToLoad,
|
||||
Map<OutputUnit, FinalizedFragment> outputUnitMap,
|
||||
Set<OutputUnit> omittedOutputUnits) {
|
||||
Map<String, List<FinalizedFragment>> fragmentsToLoad = {};
|
||||
outputUnitsToLoad.forEach((loadId, outputUnits) {
|
||||
Set<FinalizedFragment> unique = {};
|
||||
List<FinalizedFragment> finalizedFragments = [];
|
||||
loadMap[loadId] = finalizedFragments;
|
||||
for (var fragment in fragments) {
|
||||
var finalizedFragment = fragmentMap[fragment];
|
||||
fragmentsToLoad[loadId] = finalizedFragments;
|
||||
for (var outputUnit in outputUnits) {
|
||||
if (omittedOutputUnits.contains(outputUnit)) continue;
|
||||
var finalizedFragment = outputUnitMap[outputUnit];
|
||||
if (unique.add(finalizedFragment)) {
|
||||
finalizedFragments.add(finalizedFragment);
|
||||
}
|
||||
}
|
||||
});
|
||||
return loadMap;
|
||||
return fragmentsToLoad;
|
||||
}
|
||||
|
||||
/// Given a list of OutputUnits sorted by their import entites,
|
||||
@@ -353,22 +362,22 @@ class FragmentMerger {
|
||||
}
|
||||
|
||||
/// Attachs predecessors and successors to each PreFragment.
|
||||
void attachDependencies(Map<Fragment, PreFragment> fragmentMap,
|
||||
/// Expects outputUnits to be sorted.
|
||||
void attachDependencies(
|
||||
List<OutputUnit> outputUnits,
|
||||
Map<Fragment, PreFragment> fragmentMap,
|
||||
List<PreFragment> preDeferredFragments) {
|
||||
// Create a map of OutputUnit to Fragment.
|
||||
Map<OutputUnit, Fragment> outputUnitMap = {};
|
||||
List<OutputUnit> allOutputUnits = [];
|
||||
for (var preFragment in preDeferredFragments) {
|
||||
var fragment = preFragment.fragments.single;
|
||||
var outputUnit = fragment.outputUnit;
|
||||
outputUnitMap[outputUnit] = fragment;
|
||||
allOutputUnits.add(outputUnit);
|
||||
totalSize += preFragment.size;
|
||||
}
|
||||
allOutputUnits.sort();
|
||||
|
||||
// Get a list of direct edges and then attach them to PreFragments.
|
||||
var allEdges = createDirectEdges(allOutputUnits);
|
||||
var allEdges = createDirectEdges(outputUnits);
|
||||
allEdges.forEach((outputUnit, edges) {
|
||||
var predecessor = fragmentMap[outputUnitMap[outputUnit]];
|
||||
for (var edge in edges) {
|
||||
@@ -449,4 +458,79 @@ class FragmentMerger {
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
/// Computes load lists using a list of sorted OutputUnits.
|
||||
Map<String, List<OutputUnit>> computeOutputUnitsToLoad(
|
||||
List<OutputUnit> outputUnits) {
|
||||
// Sort the output units in descending order of the number of imports they
|
||||
// include.
|
||||
|
||||
// The loading of the output units must be ordered because a superclass
|
||||
// needs to be initialized before its subclass.
|
||||
// But a class can only depend on another class in an output unit shared by
|
||||
// a strict superset of the imports:
|
||||
// By contradiction: Assume a class C in output unit shared by imports in
|
||||
// the set S1 = (lib1,.., lib_n) depends on a class D in an output unit
|
||||
// shared by S2 such that S2 not a superset of S1. Let lib_s be a library in
|
||||
// S1 not in S2. lib_s must depend on C, and then in turn on D. Therefore D
|
||||
// is not in the right output unit.
|
||||
List<OutputUnit> sortedOutputUnits = outputUnits.reversed.toList();
|
||||
|
||||
Map<String, List<OutputUnit>> outputUnitsToLoad = {};
|
||||
for (var import in outputUnitData.deferredImportDescriptions.keys) {
|
||||
var loadId = outputUnitData.importDeferName[import];
|
||||
List<OutputUnit> loadList = [];
|
||||
for (var outputUnit in sortedOutputUnits) {
|
||||
assert(!outputUnit.isMainOutput);
|
||||
if (outputUnit.imports.contains(import)) {
|
||||
loadList.add(outputUnit);
|
||||
}
|
||||
}
|
||||
outputUnitsToLoad[loadId] = loadList;
|
||||
}
|
||||
return outputUnitsToLoad;
|
||||
}
|
||||
|
||||
/// Returns a json-style map for describing what files that are loaded by a
|
||||
/// given deferred import.
|
||||
/// The mapping is structured as:
|
||||
/// library uri -> {"name": library name, "files": (prefix -> list of files)}
|
||||
/// Where
|
||||
///
|
||||
/// - <library uri> is the import uri of the library making a deferred
|
||||
/// import.
|
||||
/// - <library name> is the name of the library, or "<unnamed>" if it is
|
||||
/// unnamed.
|
||||
/// - <prefix> is the `as` prefix used for a given deferred import.
|
||||
/// - <list of files> is a list of the filenames the must be loaded when that
|
||||
/// import is loaded.
|
||||
/// TODO(joshualitt): the library name is unused and should be removed. This
|
||||
/// will be a breaking change.
|
||||
Map<String, Map<String, dynamic>> computeDeferredMap(
|
||||
Map<String, List<FinalizedFragment>> fragmentsToLoad) {
|
||||
Map<String, Map<String, dynamic>> mapping = {};
|
||||
|
||||
outputUnitData.deferredImportDescriptions.keys
|
||||
.forEach((ImportEntity import) {
|
||||
var importDeferName = outputUnitData.importDeferName[import];
|
||||
List<FinalizedFragment> fragments = fragmentsToLoad[importDeferName];
|
||||
ImportDescription description =
|
||||
outputUnitData.deferredImportDescriptions[import];
|
||||
String getName(LibraryEntity library) {
|
||||
var name = _elementEnvironment.getLibraryName(library);
|
||||
return name == '' ? '<unnamed>' : name;
|
||||
}
|
||||
|
||||
Map<String, dynamic> libraryMap = mapping.putIfAbsent(
|
||||
description.importingUri,
|
||||
() => {"name": getName(description.importingLibrary), "imports": {}});
|
||||
|
||||
List<String> partFileNames = fragments
|
||||
.map((fragment) =>
|
||||
deferredPartFileName(_options, fragment.canonicalOutputUnit.name))
|
||||
.toList();
|
||||
libraryMap["imports"][importDeferName] = partFileNames;
|
||||
});
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,15 @@ class ModelEmitter {
|
||||
|
||||
List<PreFragment> preDeferredFragmentsForTesting;
|
||||
|
||||
/// A mapping from the name of a defer import to all the fragments it
|
||||
/// depends on in a list of lists to be loaded in the order they appear.
|
||||
///
|
||||
/// For example {"lib1": [[lib1_lib2_lib3], [lib1_lib2, lib1_lib3],
|
||||
/// [lib1]]} would mean that in order to load "lib1" first the hunk
|
||||
/// lib1_lib2_lib2 should be loaded, then the hunks lib1_lib2 and lib1_lib3
|
||||
/// can be loaded in parallel. And fially lib1 can be loaded.
|
||||
Map<String, List<FinalizedFragment>> fragmentsToLoad;
|
||||
|
||||
/// For deferred loading we communicate the initializers via this global var.
|
||||
static const String deferredInitializersGlobal =
|
||||
r"$__dart_deferred_initializers__";
|
||||
@@ -130,7 +139,8 @@ class ModelEmitter {
|
||||
RecipeEncoder rtiRecipeEncoder,
|
||||
this._shouldGenerateSourceMap)
|
||||
: _constantOrdering = new ConstantOrdering(_closedWorld.sorter),
|
||||
fragmentMerger = FragmentMerger(_options) {
|
||||
fragmentMerger = FragmentMerger(_options,
|
||||
_closedWorld.elementEnvironment, _closedWorld.outputUnitData) {
|
||||
this.constantEmitter = new ConstantEmitter(
|
||||
_options,
|
||||
_namer,
|
||||
@@ -213,11 +223,8 @@ class ModelEmitter {
|
||||
_closedWorld,
|
||||
codegenWorld);
|
||||
|
||||
var deferredLoadingState = new DeferredLoadingState();
|
||||
js.Statement mainCode =
|
||||
fragmentEmitter.emitMainFragment(program, deferredLoadingState);
|
||||
|
||||
// In order to get size estimates, we partially emit deferred fragments.
|
||||
List<OutputUnit> outputUnits = [];
|
||||
List<PreFragment> preDeferredFragments = [];
|
||||
Map<DeferredFragment, PreFragment> preFragmentMap = {};
|
||||
_task.measureSubtask('emit prefragments', () {
|
||||
@@ -225,15 +232,23 @@ class ModelEmitter {
|
||||
var preFragment =
|
||||
fragmentEmitter.emitPreFragment(fragment, shouldMergeFragments);
|
||||
preFragmentMap[fragment] = preFragment;
|
||||
outputUnits.add(fragment.outputUnit);
|
||||
preDeferredFragments.add(preFragment);
|
||||
}
|
||||
});
|
||||
|
||||
// Sort output units so they are in a canonical order and generate a map of
|
||||
// loadId to list of OutputUnits to load.
|
||||
outputUnits.sort();
|
||||
var outputUnitsToLoad =
|
||||
fragmentMerger.computeOutputUnitsToLoad(outputUnits);
|
||||
|
||||
// If we are going to merge, then we attach dependencies to each PreFragment
|
||||
// and merge.
|
||||
if (shouldMergeFragments) {
|
||||
preDeferredFragments = _task.measureSubtask('merge fragments', () {
|
||||
fragmentMerger.attachDependencies(preFragmentMap, preDeferredFragments);
|
||||
fragmentMerger.attachDependencies(
|
||||
outputUnits, preFragmentMap, preDeferredFragments);
|
||||
return fragmentMerger.mergeFragments(preDeferredFragments);
|
||||
});
|
||||
}
|
||||
@@ -243,11 +258,12 @@ class ModelEmitter {
|
||||
preDeferredFragmentsForTesting = preDeferredFragments;
|
||||
}
|
||||
|
||||
Map<DeferredFragment, FinalizedFragment> fragmentMap = {};
|
||||
// Finalize and emit fragments.
|
||||
Map<OutputUnit, FinalizedFragment> outputUnitMap = {};
|
||||
Map<FinalizedFragment, js.Expression> deferredFragmentsCode = {};
|
||||
for (var preDeferredFragment in preDeferredFragments) {
|
||||
var finalizedFragment =
|
||||
preDeferredFragment.finalize(program, fragmentMap);
|
||||
preDeferredFragment.finalize(program, outputUnitMap);
|
||||
js.Expression fragmentCode = fragmentEmitter.emitDeferredFragment(
|
||||
finalizedFragment, program.holders);
|
||||
if (fragmentCode != null) {
|
||||
@@ -257,6 +273,17 @@ class ModelEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// With all deferred fragments finalized, we can now compute a map of
|
||||
// loadId to the files(FinalizedFragments) which need to be loaded.
|
||||
fragmentsToLoad = fragmentMerger.computeFragmentsToLoad(
|
||||
outputUnitsToLoad, outputUnitMap, omittedOutputUnits);
|
||||
|
||||
// Emit main Fragment.
|
||||
var deferredLoadingState = new DeferredLoadingState();
|
||||
js.Statement mainCode = fragmentEmitter.emitMainFragment(
|
||||
program, fragmentsToLoad, deferredLoadingState);
|
||||
|
||||
// Count tokens and run finalizers.
|
||||
js.TokenCounter counter = new js.TokenCounter();
|
||||
deferredFragmentsCode.values.forEach(counter.countTokens);
|
||||
counter.countTokens(mainCode);
|
||||
@@ -274,10 +301,8 @@ class ModelEmitter {
|
||||
|
||||
// Now that we have written the deferred hunks, we can create the deferred
|
||||
// loading data.
|
||||
Map<String, List<FinalizedFragment>> loadMap =
|
||||
FragmentMerger.processLoadMap(program.loadMap, fragmentMap);
|
||||
fragmentEmitter.finalizeDeferredLoadingData(
|
||||
loadMap, hunkHashes, deferredLoadingState);
|
||||
fragmentsToLoad, hunkHashes, deferredLoadingState);
|
||||
|
||||
_task.measureSubtask('write fragments', () {
|
||||
writeMainFragment(mainFragment, mainCode,
|
||||
@@ -498,9 +523,7 @@ class ModelEmitter {
|
||||
// data.
|
||||
mapping["_comment"] = "This mapping shows which compiled `.js` files are "
|
||||
"needed for a given deferred library import.";
|
||||
mapping.addAll(_closedWorld.outputUnitData.computeDeferredMap(
|
||||
_options, _closedWorld.elementEnvironment,
|
||||
omittedUnits: omittedOutputUnits));
|
||||
mapping.addAll(fragmentMerger.computeDeferredMap(fragmentsToLoad));
|
||||
_outputProvider.createOutputSink(
|
||||
_options.deferredMapUri.path, '', OutputType.deferredMap)
|
||||
..add(const JsonEncoder.withIndent(" ").convert(mapping))
|
||||
|
||||
@@ -45,8 +45,7 @@
|
||||
},
|
||||
"pkg/compiler/lib/src/deferred_load.dart": {
|
||||
"Dynamic access of 'memberContext'.": 1,
|
||||
"Dynamic access of 'name'.": 1,
|
||||
"Dynamic invocation of '[]='.": 1
|
||||
"Dynamic access of 'name'.": 1
|
||||
},
|
||||
"pkg/compiler/lib/src/inferrer/typemasks/container_type_mask.dart": {
|
||||
"Dynamic access of 'isNullable'.": 2,
|
||||
@@ -197,6 +196,9 @@
|
||||
"Dynamic invocation of '[]='.": 1,
|
||||
"Dynamic invocation of 'add'.": 1
|
||||
},
|
||||
"pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart": {
|
||||
"Dynamic invocation of '[]='.": 1
|
||||
},
|
||||
"pkg/js_ast/lib/src/builder.dart": {
|
||||
"Dynamic invocation of 'call'.": 2
|
||||
},
|
||||
|
||||
@@ -11,9 +11,18 @@
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:compiler/src/compiler.dart';
|
||||
import 'package:compiler/src/deferred_load.dart';
|
||||
import 'package:compiler/src/js_emitter/startup_emitter/fragment_merger.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
import '../helpers/memory_compiler.dart';
|
||||
|
||||
List<OutputUnit> collectOutputUnits(List<FinalizedFragment> fragments) {
|
||||
List<OutputUnit> outputUnits = [];
|
||||
for (var fragment in fragments) {
|
||||
outputUnits.addAll(fragment.outputUnits);
|
||||
}
|
||||
return outputUnits;
|
||||
}
|
||||
|
||||
void main() {
|
||||
asyncTest(() async {
|
||||
CompilationResult result =
|
||||
@@ -59,12 +68,11 @@ void main() {
|
||||
// InputElement is native, so it should be in the mainOutputUnit.
|
||||
Expect.equals(mainOutputUnit, outputUnitForClass(inputElement));
|
||||
|
||||
var hunksToLoad = closedWorld.outputUnitData.hunksToLoad;
|
||||
|
||||
var hunksLib1 = hunksToLoad["lib1"];
|
||||
var hunksLib2 = hunksToLoad["lib2"];
|
||||
var hunksLib4_1 = hunksToLoad["lib4_1"];
|
||||
var hunksLib4_2 = hunksToLoad["lib4_2"];
|
||||
var hunksToLoad = backendStrategy.emitterTask.emitter.fragmentsToLoad;
|
||||
var hunksLib1 = collectOutputUnits(hunksToLoad["lib1"]);
|
||||
var hunksLib2 = collectOutputUnits(hunksToLoad["lib2"]);
|
||||
var hunksLib4_1 = collectOutputUnits(hunksToLoad["lib4_1"]);
|
||||
var hunksLib4_2 = collectOutputUnits(hunksToLoad["lib4_2"]);
|
||||
Expect.listEquals([ou_lib1_lib2, ou_lib1], hunksLib1);
|
||||
Expect.listEquals([ou_lib1_lib2, ou_lib2], hunksLib2);
|
||||
Expect.listEquals([ou_lib4_1], hunksLib4_1);
|
||||
|
||||
@@ -4,27 +4,24 @@
|
||||
|
||||
// @dart = 2.7
|
||||
|
||||
import 'dart:convert';
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:compiler/compiler_new.dart';
|
||||
import 'package:compiler/src/world.dart';
|
||||
import '../helpers/memory_source_file_helper.dart';
|
||||
import '../helpers/memory_compiler.dart';
|
||||
|
||||
void testLoadMap() async {
|
||||
var collector = new OutputCollector();
|
||||
CompilationResult result = await runCompiler(
|
||||
await runCompiler(
|
||||
memorySourceFiles: MEMORY_SOURCE_FILES,
|
||||
options: ['--deferred-map=deferred_map.json'],
|
||||
outputProvider: collector);
|
||||
CompilerImpl compiler = result.compiler;
|
||||
JClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
|
||||
// Ensure a mapping file is output.
|
||||
Expect.isNotNull(
|
||||
collector.getOutput("deferred_map.json", OutputType.deferredMap));
|
||||
var deferredMap =
|
||||
collector.getOutput("deferred_map.json", OutputType.deferredMap);
|
||||
Expect.isNotNull(deferredMap);
|
||||
var mapping = jsonDecode(deferredMap);
|
||||
|
||||
Map mapping = closedWorld.outputUnitData
|
||||
.computeDeferredMap(compiler.options, closedWorld.elementEnvironment);
|
||||
// Test structure of mapping.
|
||||
Expect.equals("<unnamed>", mapping["main.dart"]["name"]);
|
||||
Expect.equals(2, mapping["main.dart"]["imports"]["lib1"].length);
|
||||
|
||||
@@ -31,9 +31,9 @@ import 'lib2.dart' deferred as lib2;
|
||||
/*member: main:member_unit=main{}*/
|
||||
void main() {
|
||||
lib1.loadLibrary().then(/*closure_unit=main{}*/ (_) {
|
||||
new lib1.C1();
|
||||
print(new lib1.C1());
|
||||
lib2.loadLibrary().then(/*closure_unit=main{}*/ (_) {
|
||||
new lib2.C2();
|
||||
print(new lib2.C2());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,15 +16,18 @@ class Foo {
|
||||
x = DateTime.now().millisecond;
|
||||
}
|
||||
/*member: Foo.method:member_unit=1{libB}*/
|
||||
@pragma('dart2js:noInline')
|
||||
int method() => x;
|
||||
}
|
||||
|
||||
/*member: isFoo:member_unit=3{libA, libB, libC}*/
|
||||
@pragma('dart2js:noInline')
|
||||
bool isFoo(o) {
|
||||
return o is Foo;
|
||||
}
|
||||
|
||||
/*member: callFooMethod:member_unit=1{libB}*/
|
||||
@pragma('dart2js:noInline')
|
||||
int callFooMethod() {
|
||||
return Foo().method();
|
||||
}
|
||||
@@ -33,6 +36,7 @@ typedef int FunFoo(Foo a);
|
||||
typedef int FunFunFoo(FunFoo b, int c);
|
||||
|
||||
/*member: isFunFunFoo:member_unit=3{libA, libB, libC}*/
|
||||
@pragma('dart2js:noInline')
|
||||
bool isFunFunFoo(o) {
|
||||
return o is FunFunFoo;
|
||||
}
|
||||
@@ -64,6 +68,7 @@ class Coo<T> {}
|
||||
class Doo<T> extends Coo<T> with Boo<T> {}
|
||||
|
||||
/*member: createDooFunFunFoo:member_unit=2{libC}*/
|
||||
@pragma('dart2js:noInline')
|
||||
createDooFunFunFoo() => Doo<FunFunFoo>();
|
||||
|
||||
/*class: B:
|
||||
@@ -120,6 +125,7 @@ class D2 {}
|
||||
class D3 = D2 with D1;
|
||||
|
||||
/*member: isMega:member_unit=6{libA}*/
|
||||
@pragma('dart2js:noInline')
|
||||
bool isMega(o) {
|
||||
return o is B2 || o is C3 || o is D3;
|
||||
}
|
||||
|
||||
@@ -5,15 +5,13 @@
|
||||
/*spec.library:
|
||||
output_units=[
|
||||
f1: {units: [3{libA, libB, libC}], usedBy: [], needs: []},
|
||||
f2: {units: [4{libA, libC}], usedBy: [], needs: []},
|
||||
f3: {units: [6{libA}], usedBy: [], needs: []},
|
||||
f4: {units: [5{libB, libC}], usedBy: [], needs: []},
|
||||
f5: {units: [1{libB}], usedBy: [], needs: []},
|
||||
f6: {units: [2{libC}], usedBy: [], needs: []}],
|
||||
f2: {units: [6{libA}], usedBy: [], needs: []},
|
||||
f3: {units: [1{libB}], usedBy: [], needs: []},
|
||||
f4: {units: [2{libC}], usedBy: [], needs: []}],
|
||||
steps=[
|
||||
libA=(f1, f2, f3),
|
||||
libB=(f1, f4, f5),
|
||||
libC=(f1, f4, f2, f6)]
|
||||
libA=(f1, f2),
|
||||
libB=(f1, f3),
|
||||
libC=(f1, f4)]
|
||||
*/
|
||||
|
||||
/*two-frag.library:
|
||||
|
||||
@@ -2,24 +2,13 @@
|
||||
// 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.
|
||||
|
||||
/*spec.library:
|
||||
/*library:
|
||||
output_units=[
|
||||
f1: {units: [3{lib1, lib3}], usedBy: [], needs: []},
|
||||
f2: {units: [1{lib1}], usedBy: [], needs: []},
|
||||
f3: {units: [2{lib3}], usedBy: [], needs: []}],
|
||||
f1: {units: [1{lib1}], usedBy: [], needs: []},
|
||||
f2: {units: [2{lib3}], usedBy: [], needs: []}],
|
||||
steps=[
|
||||
lib1=(f1, f2),
|
||||
lib3=(f1, f3)]
|
||||
*/
|
||||
|
||||
/*two-frag|three-frag.library:
|
||||
output_units=[
|
||||
f1: {units: [3{lib1, lib3}], usedBy: [], needs: [2, 3]},
|
||||
f2: {units: [1{lib1}], usedBy: [1], needs: []},
|
||||
f3: {units: [2{lib3}], usedBy: [1], needs: []}],
|
||||
steps=[
|
||||
lib1=(f1, f2),
|
||||
lib3=(f1, f3)]
|
||||
lib1=(f1),
|
||||
lib3=(f2)]
|
||||
*/
|
||||
|
||||
// @dart = 2.7
|
||||
|
||||
@@ -15,7 +15,6 @@ import 'package:compiler/src/elements/entities.dart';
|
||||
import 'package:compiler/src/ir/util.dart';
|
||||
import 'package:compiler/src/js_model/element_map.dart';
|
||||
import 'package:compiler/src/js_model/js_world.dart';
|
||||
import 'package:compiler/src/js_emitter/model.dart';
|
||||
import 'package:compiler/src/js_emitter/startup_emitter/fragment_merger.dart';
|
||||
import 'package:compiler/src/kernel/kernel_strategy.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
@@ -87,23 +86,18 @@ String outputUnitString(OutputUnit unit) {
|
||||
}
|
||||
|
||||
Map<String, List<PreFragment>> buildPreFragmentMap(
|
||||
Map<String, List<Fragment>> loadMap,
|
||||
Map<String, List<FinalizedFragment>> fragmentsToLoad,
|
||||
List<PreFragment> preDeferredFragments) {
|
||||
Map<DeferredFragment, PreFragment> fragmentMap = {};
|
||||
Map<FinalizedFragment, PreFragment> fragmentMap = {};
|
||||
for (var preFragment in preDeferredFragments) {
|
||||
for (var fragment in preFragment.fragments) {
|
||||
assert(!fragmentMap.containsKey(fragment));
|
||||
fragmentMap[fragment] = preFragment;
|
||||
}
|
||||
fragmentMap[preFragment.finalizedFragment] = preFragment;
|
||||
}
|
||||
|
||||
Map<String, List<PreFragment>> preFragmentMap = {};
|
||||
loadMap.forEach((loadId, fragments) {
|
||||
Set<PreFragment> preFragments = {};
|
||||
fragmentsToLoad.forEach((loadId, fragments) {
|
||||
List<PreFragment> preFragments = [];
|
||||
for (var fragment in fragments) {
|
||||
preFragments.add(fragmentMap[fragment]);
|
||||
}
|
||||
assert(!preFragmentMap.containsKey(loadId));
|
||||
preFragmentMap[loadId] = preFragments.toList();
|
||||
});
|
||||
return preFragmentMap;
|
||||
@@ -160,10 +154,10 @@ class OutputUnitDataComputer extends DataComputer<Features> {
|
||||
ir.Library node = frontendStrategy.elementMap.getLibraryNode(library);
|
||||
List<PreFragment> preDeferredFragments = compiler
|
||||
.backendStrategy.emitterTask.emitter.preDeferredFragmentsForTesting;
|
||||
Program program =
|
||||
compiler.backendStrategy.emitterTask.emitter.programForTesting;
|
||||
Map<String, List<FinalizedFragment>> fragmentsToLoad =
|
||||
compiler.backendStrategy.emitterTask.emitter.fragmentsToLoad;
|
||||
Map<String, List<PreFragment>> preFragmentMap =
|
||||
buildPreFragmentMap(program.loadMap, preDeferredFragments);
|
||||
buildPreFragmentMap(fragmentsToLoad, preDeferredFragments);
|
||||
PreFragmentsIrComputer(compiler.reporter, actualMap, preFragmentMap)
|
||||
.computeForLibrary(node);
|
||||
}
|
||||
@@ -208,13 +202,15 @@ class PreFragmentsIrComputer extends IrDataExtractor<Features> {
|
||||
List<OutputUnit> supplied = [];
|
||||
List<int> usedBy = [];
|
||||
for (var dependent in preFragment.successors) {
|
||||
assert(preFragmentIndices.containsKey(dependent));
|
||||
usedBy.add(preFragmentIndices[dependent]);
|
||||
if (preFragmentIndices.containsKey(dependent)) {
|
||||
usedBy.add(preFragmentIndices[dependent]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var dependency in preFragment.predecessors) {
|
||||
assert(preFragmentIndices.containsKey(dependency));
|
||||
needs.add(preFragmentIndices[dependency]);
|
||||
if (preFragmentIndices.containsKey(dependency)) {
|
||||
needs.add(preFragmentIndices[dependency]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var fragment in preFragment.fragments) {
|
||||
|
||||
Reference in New Issue
Block a user