[dart2js] Upstream deferred_loading_test_helper.
Change-Id: I24b06251902d142afd1a5c62a8137459a283348e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/206663
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Joshua Litt <joshualitt@google.com>
diff --git a/pkg/compiler/test/deferred_loading/data/shadowed_types/lib_shared.dart b/pkg/compiler/test/deferred_loading/data/shadowed_types/lib_shared.dart
index 5b20db1..09e4bf0 100644
--- a/pkg/compiler/test/deferred_loading/data/shadowed_types/lib_shared.dart
+++ b/pkg/compiler/test/deferred_loading/data/shadowed_types/lib_shared.dart
@@ -4,7 +4,7 @@
/*class: A:
class_unit=1{libb},
- type_unit=2{libb, liba}
+ type_unit=2{liba, libb}
*/
/*member: A.:member_unit=1{libb}*/
class A {}
@@ -25,7 +25,7 @@
/*class: D:
class_unit=1{libb},
- type_unit=2{libb, liba}
+ type_unit=2{liba, libb}
*/
/*member: D.:member_unit=1{libb}*/
class D {}
diff --git a/pkg/compiler/test/deferred_loading/data/shadowed_types/main.dart b/pkg/compiler/test/deferred_loading/data/shadowed_types/main.dart
index d32f920..9c29878 100644
--- a/pkg/compiler/test/deferred_loading/data/shadowed_types/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/shadowed_types/main.dart
@@ -6,7 +6,7 @@
a_pre_fragments=[
p1: {units: [3{liba}], usedBy: [], needs: []},
p2: {units: [1{libb}], usedBy: [], needs: []},
- p3: {units: [2{libb, liba}], usedBy: [], needs: []}],
+ p3: {units: [2{liba, libb}], usedBy: [], needs: []}],
b_finalized_fragments=[
f1: [3{liba}],
f2: [1{libb}]],
@@ -19,7 +19,7 @@
a_pre_fragments=[
p1: {units: [3{liba}], usedBy: [p3], needs: []},
p2: {units: [1{libb}], usedBy: [p3], needs: []},
- p3: {units: [2{libb, liba}], usedBy: [], needs: [p1, p2]}],
+ p3: {units: [2{liba, libb}], usedBy: [], needs: [p1, p2]}],
b_finalized_fragments=[
f1: [3{liba}],
f2: [1{libb}]],
diff --git a/pkg/compiler/test/deferred_loading/deferred_loading_test.dart b/pkg/compiler/test/deferred_loading/deferred_loading_test.dart
index 621ba83..7ef31f8 100644
--- a/pkg/compiler/test/deferred_loading/deferred_loading_test.dart
+++ b/pkg/compiler/test/deferred_loading/deferred_loading_test.dart
@@ -5,24 +5,10 @@
// @dart = 2.7
import 'dart:io' hide Link;
-import 'package:_fe_analyzer_shared/src/testing/features.dart';
import 'package:async_helper/async_helper.dart';
-import 'package:compiler/src/closure.dart';
-import 'package:compiler/src/common.dart';
-import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/deferred_load.dart';
-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/startup_emitter/fragment_merger.dart';
-import 'package:compiler/src/kernel/kernel_strategy.dart';
-import 'package:expect/expect.dart';
-import '../equivalence/id_equivalence.dart';
import '../equivalence/id_equivalence_helper.dart';
-import 'package:compiler/src/constants/values.dart';
-
-import 'package:kernel/ast.dart' as ir;
+import 'deferred_loading_test_helper.dart';
/// Add in options to pass to the compiler like
/// `Flags.disableTypeInference` or `Flags.disableInlining`
@@ -44,342 +30,3 @@
[twoDeferredFragmentConfig, threeDeferredFragmentConfig]);
});
}
-
-// For ease of testing and making our tests easier to read, we impose an
-// artificial constraint of requiring every deferred import use a different
-// named prefix per test. We enforce this constraint here by checking that no
-// prefix name responds to two different libraries.
-Map<String, Uri> importPrefixes = {};
-
-String importPrefixString(OutputUnit unit) {
- StringBuffer sb = StringBuffer();
- bool first = true;
- for (ImportEntity import in unit.imports) {
- if (!first) sb.write(', ');
- sb.write('${import.name}');
- first = false;
- Expect.isTrue(import.isDeferred);
-
- if (importPrefixes.containsKey(import.name)) {
- var existing = importPrefixes[import.name];
- var current = import.enclosingLibraryUri;
- Expect.equals(
- existing,
- current,
- '\n Duplicate prefix \'${import.name}\' used in both:\n'
- ' - $existing and\n'
- ' - $current.\n'
- ' We require using unique prefixes on these tests to make '
- 'the expectations more readable.');
- }
- importPrefixes[import.name] = import.enclosingLibraryUri;
- }
- return sb.toString();
-}
-
-/// Create a consistent string representation of [OutputUnit]s for both
-/// KImportEntities and ImportElements.
-String outputUnitString(OutputUnit unit) {
- if (unit == null) return 'none';
- String sb = importPrefixString(unit);
- return '${unit.name}{$sb}';
-}
-
-Map<String, List<PreFragment>> buildPreFragmentMap(
- Map<String, List<FinalizedFragment>> fragmentsToLoad,
- List<PreFragment> preDeferredFragments) {
- Map<FinalizedFragment, PreFragment> fragmentMap = {};
- for (var preFragment in preDeferredFragments) {
- fragmentMap[preFragment.finalizedFragment] = preFragment;
- }
- Map<String, List<PreFragment>> preFragmentMap = {};
- fragmentsToLoad.forEach((loadId, fragments) {
- List<PreFragment> preFragments = [];
- for (var fragment in fragments) {
- preFragments.add(fragmentMap[fragment]);
- }
- preFragmentMap[loadId] = preFragments.toList();
- });
- return preFragmentMap;
-}
-
-class Tags {
- static const String cls = 'class_unit';
- static const String member = 'member_unit';
- static const String closure = 'closure_unit';
- static const String constants = 'constants';
- static const String type = 'type_unit';
- // The below tags appear in a single block comment in the main file.
- // To keep them appearing in sequential order we prefix characters.
- static const String preFragments = 'a_pre_fragments';
- static const String finalizedFragments = 'b_finalized_fragments';
- static const String steps = 'c_steps';
-}
-
-class OutputUnitDataComputer extends DataComputer<Features> {
- const OutputUnitDataComputer();
-
- /// OutputData for [member] as a kernel based element.
- ///
- /// At this point the compiler has already been run, so it is holding the
- /// relevant OutputUnits, we just need to extract that information from it. We
- /// fill [actualMap] with the data computed about what the resulting OutputUnit
- /// is.
- @override
- void computeMemberData(Compiler compiler, MemberEntity member,
- Map<Id, ActualData<Features>> actualMap,
- {bool verbose: false}) {
- JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
- JsToElementMap elementMap = closedWorld.elementMap;
- MemberDefinition definition = elementMap.getMemberDefinition(member);
- OutputUnitIrComputer(compiler.reporter, actualMap, elementMap,
- closedWorld.outputUnitData, closedWorld.closureDataLookup)
- .run(definition.node);
- }
-
- @override
- void computeClassData(Compiler compiler, ClassEntity cls,
- Map<Id, ActualData<Features>> actualMap,
- {bool verbose: false}) {
- JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
- JsToElementMap elementMap = closedWorld.elementMap;
- ClassDefinition definition = elementMap.getClassDefinition(cls);
- OutputUnitIrComputer(compiler.reporter, actualMap, elementMap,
- closedWorld.outputUnitData, closedWorld.closureDataLookup)
- .computeForClass(definition.node);
- }
-
- @override
- void computeLibraryData(Compiler compiler, LibraryEntity library,
- Map<Id, ActualData<Features>> actualMap,
- {bool verbose}) {
- KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
- ir.Library node = frontendStrategy.elementMap.getLibraryNode(library);
- List<PreFragment> preDeferredFragments = compiler
- .backendStrategy.emitterTask.emitter.preDeferredFragmentsForTesting;
- Map<String, List<FinalizedFragment>> fragmentsToLoad =
- compiler.backendStrategy.emitterTask.emitter.finalizedFragmentsToLoad;
- Set<OutputUnit> omittedOutputUnits =
- compiler.backendStrategy.emitterTask.emitter.omittedOutputUnits;
- PreFragmentsIrComputer(compiler.reporter, actualMap, preDeferredFragments,
- fragmentsToLoad, omittedOutputUnits)
- .computeForLibrary(node);
- }
-
- @override
- DataInterpreter<Features> get dataValidator =>
- const FeaturesDataInterpreter();
-}
-
-class PreFragmentsIrComputer extends IrDataExtractor<Features> {
- final List<PreFragment> _preDeferredFragments;
- final Map<String, List<FinalizedFragment>> _fragmentsToLoad;
- final Set<OutputUnit> _omittedOutputUnits;
-
- PreFragmentsIrComputer(
- DiagnosticReporter reporter,
- Map<Id, ActualData<Features>> actualMap,
- this._preDeferredFragments,
- this._fragmentsToLoad,
- this._omittedOutputUnits)
- : super(reporter, actualMap);
-
- @override
- Features computeLibraryValue(Id id, ir.Library library) {
- var name = '${library.importUri.pathSegments.last}';
- Features features = new Features();
- if (!name.startsWith('main')) return features;
-
- // First build a list of pre fragments and their dependencies.
- int index = 1;
- Map<FinalizedFragment, int> finalizedFragmentIndices = {};
- Map<PreFragment, int> preFragmentIndices = {};
- Map<int, PreFragment> reversePreFragmentIndices = {};
- Map<int, FinalizedFragment> reverseFinalizedFragmentIndices = {};
- for (var preFragment in _preDeferredFragments) {
- if (!preFragmentIndices.containsKey(preFragment)) {
- var finalizedFragment = preFragment.finalizedFragment;
- preFragmentIndices[preFragment] = index;
- finalizedFragmentIndices[finalizedFragment] = index;
- reversePreFragmentIndices[index] = preFragment;
- reverseFinalizedFragmentIndices[index] = finalizedFragment;
- index++;
- }
- }
-
- for (int i = 1; i < index; i++) {
- var preFragment = reversePreFragmentIndices[i];
- List<String> needs = [];
- List<OutputUnit> supplied = [];
- List<String> usedBy = [];
- for (var dependent in preFragment.successors) {
- if (preFragmentIndices.containsKey(dependent)) {
- usedBy.add('p${preFragmentIndices[dependent]}');
- }
- }
-
- for (var dependency in preFragment.predecessors) {
- if (preFragmentIndices.containsKey(dependency)) {
- needs.add('p${preFragmentIndices[dependency]}');
- }
- }
-
- for (var emittedOutputUnit in preFragment.emittedOutputUnits) {
- supplied.add(emittedOutputUnit.outputUnit);
- }
-
- var suppliedString = '[${supplied.map(outputUnitString).join(', ')}]';
- features.addElement(Tags.preFragments,
- 'p$i: {units: $suppliedString, usedBy: $usedBy, needs: $needs}');
- }
-
- // Now dump finalized fragments and load ids.
- for (int i = 1; i < index; i++) {
- var finalizedFragment = reverseFinalizedFragmentIndices[i];
- List<String> supplied = [];
-
- for (var codeFragment in finalizedFragment.codeFragments) {
- List<String> outputUnitStrings = [];
- for (var outputUnit in codeFragment.outputUnits) {
- if (!_omittedOutputUnits.contains(outputUnit)) {
- outputUnitStrings.add(outputUnitString(outputUnit));
- }
- }
- if (outputUnitStrings.isNotEmpty) {
- supplied.add(outputUnitStrings.join('+'));
- }
- }
-
- if (supplied.isNotEmpty) {
- var suppliedString = '[${supplied.join(', ')}]';
- features.addElement(Tags.finalizedFragments, 'f$i: $suppliedString');
- }
- }
-
- _fragmentsToLoad.forEach((loadId, finalizedFragments) {
- List<String> finalizedFragmentNeeds = [];
- for (var finalizedFragment in finalizedFragments) {
- assert(finalizedFragmentIndices.containsKey(finalizedFragment));
- finalizedFragmentNeeds
- .add('f${finalizedFragmentIndices[finalizedFragment]}');
- }
- features.addElement(
- Tags.steps, '$loadId=(${finalizedFragmentNeeds.join(', ')})');
- });
-
- return features;
- }
-}
-
-class OutputUnitIrComputer extends IrDataExtractor<Features> {
- final JsToElementMap _elementMap;
- final OutputUnitData _data;
- final ClosureData _closureDataLookup;
-
- Set<String> _constants = {};
-
- OutputUnitIrComputer(
- DiagnosticReporter reporter,
- Map<Id, ActualData<Features>> actualMap,
- this._elementMap,
- this._data,
- this._closureDataLookup)
- : super(reporter, actualMap);
-
- Features getMemberValue(
- String tag, MemberEntity member, Set<String> constants) {
- Features features = Features();
- features.add(tag,
- value: outputUnitString(_data.outputUnitForMemberForTesting(member)));
- for (var constant in constants) {
- features.addElement(Tags.constants, constant);
- }
- return features;
- }
-
- @override
- Features computeClassValue(Id id, ir.Class node) {
- var cls = _elementMap.getClass(node);
- Features features = Features();
- features.add(Tags.cls,
- value: outputUnitString(_data.outputUnitForClassForTesting(cls)));
- features.add(Tags.type,
- value: outputUnitString(_data.outputUnitForClassTypeForTesting(cls)));
- return features;
- }
-
- @override
- Features computeMemberValue(Id id, ir.Member node) {
- if (node is ir.Field && node.isConst) {
- ir.Expression initializer = node.initializer;
- ConstantValue constant = _elementMap.getConstantValue(node, initializer);
- if (!constant.isPrimitive) {
- SourceSpan span = computeSourceSpanFromTreeNode(initializer);
- if (initializer is ir.ConstructorInvocation) {
- // Adjust the source-span to match the AST-based location. The kernel FE
- // skips the "const" keyword for the expression offset and any prefix in
- // front of the constructor. The "-6" is an approximation assuming that
- // there is just a single space after "const" and no prefix.
- // TODO(sigmund): offsets should be fixed in the FE instead.
- span = SourceSpan(span.uri, span.begin - 6, span.end - 6);
- }
- _registerValue(
- NodeId(span.begin, IdKind.node),
- Features.fromMap({
- Tags.member: outputUnitString(
- _data.outputUnitForConstantForTesting(constant))
- }),
- node,
- span,
- actualMap,
- reporter);
- }
- }
-
- Features features =
- getMemberValue(Tags.member, _elementMap.getMember(node), _constants);
- _constants = {};
- return features;
- }
-
- @override
- visitConstantExpression(ir.ConstantExpression node) {
- ConstantValue constant = _elementMap.getConstantValue(null, node);
- if (!constant.isPrimitive) {
- _constants.add('${constant.toStructuredText(_elementMap.types)}='
- '${outputUnitString(_data.outputUnitForConstant(constant))}');
- }
- return super.visitConstantExpression(node);
- }
-
- @override
- Features computeNodeValue(Id id, ir.TreeNode node) {
- if (node is ir.FunctionExpression || node is ir.FunctionDeclaration) {
- ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo(node);
- return getMemberValue(Tags.closure, info.callMethod, const {});
- }
- return null;
- }
-}
-
-/// Set [actualMap] to hold a key of [id] with the computed data [value]
-/// corresponding to [object] at location [sourceSpan]. We also perform error
-/// checking to ensure that the same [id] isn't added twice.
-void _registerValue<T>(Id id, T value, Object object, SourceSpan sourceSpan,
- Map<Id, ActualData<T>> actualMap, CompilerDiagnosticReporter reporter) {
- if (actualMap.containsKey(id)) {
- ActualData<T> existingData = actualMap[id];
- reportHere(reporter, sourceSpan,
- "Duplicate id ${id}, value=$value, object=$object");
- reportHere(
- reporter,
- sourceSpan,
- "Duplicate id ${id}, value=${existingData.value}, "
- "object=${existingData.object}");
- Expect.fail("Duplicate id $id.");
- }
- if (value != null) {
- actualMap[id] =
- ActualData<T>(id, value, sourceSpan.uri, sourceSpan.begin, object);
- }
-}
diff --git a/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart b/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart
new file mode 100644
index 0000000..8028c05
--- /dev/null
+++ b/pkg/compiler/test/deferred_loading/deferred_loading_test_helper.dart
@@ -0,0 +1,360 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.7
+
+import 'package:_fe_analyzer_shared/src/testing/features.dart';
+import 'package:compiler/src/closure.dart';
+import 'package:compiler/src/common.dart';
+import 'package:compiler/src/compiler.dart';
+import 'package:compiler/src/deferred_load.dart';
+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/startup_emitter/fragment_merger.dart';
+import 'package:compiler/src/kernel/kernel_strategy.dart';
+import 'package:expect/expect.dart';
+import '../equivalence/id_equivalence.dart';
+import '../equivalence/id_equivalence_helper.dart';
+import 'package:compiler/src/constants/values.dart';
+
+import 'package:kernel/ast.dart' as ir;
+
+// For ease of testing and making our tests easier to read, we impose an
+// artificial constraint of requiring every deferred import use a different
+// named prefix per test. We enforce this constraint here by checking that no
+// prefix name responds to two different libraries.
+Map<String, Uri> importPrefixes = {};
+
+String importPrefixString(OutputUnit unit) {
+ List<String> importNames = [];
+ for (ImportEntity import in unit.imports) {
+ importNames.add(import.name);
+ Expect.isTrue(import.isDeferred);
+
+ if (importPrefixes.containsKey(import.name)) {
+ var existing = importPrefixes[import.name];
+ var current = import.enclosingLibraryUri;
+ Expect.equals(
+ existing,
+ current,
+ '\n Duplicate prefix \'${import.name}\' used in both:\n'
+ ' - $existing and\n'
+ ' - $current.\n'
+ ' We require using unique prefixes on these tests to make '
+ 'the expectations more readable.');
+ }
+ importPrefixes[import.name] = import.enclosingLibraryUri;
+ }
+ importNames.sort();
+ return importNames.join(', ');
+}
+
+/// Create a consistent string representation of [OutputUnit]s for both
+/// KImportEntities and ImportElements.
+String outputUnitString(OutputUnit unit) {
+ if (unit == null) return 'none';
+ String sb = importPrefixString(unit);
+ return '${unit.name}{$sb}';
+}
+
+Map<String, List<PreFragment>> buildPreFragmentMap(
+ Map<String, List<FinalizedFragment>> fragmentsToLoad,
+ List<PreFragment> preDeferredFragments) {
+ Map<FinalizedFragment, PreFragment> fragmentMap = {};
+ for (var preFragment in preDeferredFragments) {
+ fragmentMap[preFragment.finalizedFragment] = preFragment;
+ }
+ Map<String, List<PreFragment>> preFragmentMap = {};
+ fragmentsToLoad.forEach((loadId, fragments) {
+ List<PreFragment> preFragments = [];
+ for (var fragment in fragments) {
+ preFragments.add(fragmentMap[fragment]);
+ }
+ preFragmentMap[loadId] = preFragments.toList();
+ });
+ return preFragmentMap;
+}
+
+class Tags {
+ static const String cls = 'class_unit';
+ static const String member = 'member_unit';
+ static const String closure = 'closure_unit';
+ static const String constants = 'constants';
+ static const String type = 'type_unit';
+ // The below tags appear in a single block comment in the main file.
+ // To keep them appearing in sequential order we prefix characters.
+ static const String preFragments = 'a_pre_fragments';
+ static const String finalizedFragments = 'b_finalized_fragments';
+ static const String steps = 'c_steps';
+}
+
+class OutputUnitDataComputer extends DataComputer<Features> {
+ const OutputUnitDataComputer();
+
+ /// OutputData for [member] as a kernel based element.
+ ///
+ /// At this point the compiler has already been run, so it is holding the
+ /// relevant OutputUnits, we just need to extract that information from it. We
+ /// fill [actualMap] with the data computed about what the resulting OutputUnit
+ /// is.
+ @override
+ void computeMemberData(Compiler compiler, MemberEntity member,
+ Map<Id, ActualData<Features>> actualMap,
+ {bool verbose: false}) {
+ JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
+ JsToElementMap elementMap = closedWorld.elementMap;
+ MemberDefinition definition = elementMap.getMemberDefinition(member);
+ OutputUnitIrComputer(compiler.reporter, actualMap, elementMap,
+ closedWorld.outputUnitData, closedWorld.closureDataLookup)
+ .run(definition.node);
+ }
+
+ @override
+ void computeClassData(Compiler compiler, ClassEntity cls,
+ Map<Id, ActualData<Features>> actualMap,
+ {bool verbose: false}) {
+ JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
+ JsToElementMap elementMap = closedWorld.elementMap;
+ ClassDefinition definition = elementMap.getClassDefinition(cls);
+ OutputUnitIrComputer(compiler.reporter, actualMap, elementMap,
+ closedWorld.outputUnitData, closedWorld.closureDataLookup)
+ .computeForClass(definition.node);
+ }
+
+ @override
+ void computeLibraryData(Compiler compiler, LibraryEntity library,
+ Map<Id, ActualData<Features>> actualMap,
+ {bool verbose}) {
+ KernelFrontendStrategy frontendStrategy = compiler.frontendStrategy;
+ ir.Library node = frontendStrategy.elementMap.getLibraryNode(library);
+ List<PreFragment> preDeferredFragments = compiler
+ .backendStrategy.emitterTask.emitter.preDeferredFragmentsForTesting;
+ Map<String, List<FinalizedFragment>> fragmentsToLoad =
+ compiler.backendStrategy.emitterTask.emitter.finalizedFragmentsToLoad;
+ Set<OutputUnit> omittedOutputUnits =
+ compiler.backendStrategy.emitterTask.emitter.omittedOutputUnits;
+ PreFragmentsIrComputer(compiler.reporter, actualMap, preDeferredFragments,
+ fragmentsToLoad, omittedOutputUnits)
+ .computeForLibrary(node);
+ }
+
+ @override
+ DataInterpreter<Features> get dataValidator =>
+ const FeaturesDataInterpreter();
+}
+
+class PreFragmentsIrComputer extends IrDataExtractor<Features> {
+ final List<PreFragment> _preDeferredFragments;
+ final Map<String, List<FinalizedFragment>> _fragmentsToLoad;
+ final Set<OutputUnit> _omittedOutputUnits;
+
+ PreFragmentsIrComputer(
+ DiagnosticReporter reporter,
+ Map<Id, ActualData<Features>> actualMap,
+ this._preDeferredFragments,
+ this._fragmentsToLoad,
+ this._omittedOutputUnits)
+ : super(reporter, actualMap);
+
+ @override
+ Features computeLibraryValue(Id id, ir.Library library) {
+ var name = '${library.importUri.pathSegments.last}';
+ Features features = new Features();
+ if (!name.startsWith('main')) return features;
+
+ // First build a list of pre fragments and their dependencies.
+ int index = 1;
+ Map<FinalizedFragment, int> finalizedFragmentIndices = {};
+ Map<PreFragment, int> preFragmentIndices = {};
+ Map<int, PreFragment> reversePreFragmentIndices = {};
+ Map<int, FinalizedFragment> reverseFinalizedFragmentIndices = {};
+ for (var preFragment in _preDeferredFragments) {
+ if (!preFragmentIndices.containsKey(preFragment)) {
+ var finalizedFragment = preFragment.finalizedFragment;
+ preFragmentIndices[preFragment] = index;
+ finalizedFragmentIndices[finalizedFragment] = index;
+ reversePreFragmentIndices[index] = preFragment;
+ reverseFinalizedFragmentIndices[index] = finalizedFragment;
+ index++;
+ }
+ }
+
+ for (int i = 1; i < index; i++) {
+ var preFragment = reversePreFragmentIndices[i];
+ List<String> needs = [];
+ List<OutputUnit> supplied = [];
+ List<String> usedBy = [];
+ for (var dependent in preFragment.successors) {
+ if (preFragmentIndices.containsKey(dependent)) {
+ usedBy.add('p${preFragmentIndices[dependent]}');
+ }
+ }
+
+ for (var dependency in preFragment.predecessors) {
+ if (preFragmentIndices.containsKey(dependency)) {
+ needs.add('p${preFragmentIndices[dependency]}');
+ }
+ }
+
+ for (var emittedOutputUnit in preFragment.emittedOutputUnits) {
+ supplied.add(emittedOutputUnit.outputUnit);
+ }
+
+ var suppliedString = '[${supplied.map(outputUnitString).join(', ')}]';
+ features.addElement(Tags.preFragments,
+ 'p$i: {units: $suppliedString, usedBy: $usedBy, needs: $needs}');
+ }
+
+ // Now dump finalized fragments and load ids.
+ for (int i = 1; i < index; i++) {
+ var finalizedFragment = reverseFinalizedFragmentIndices[i];
+ List<String> supplied = [];
+
+ for (var codeFragment in finalizedFragment.codeFragments) {
+ List<String> outputUnitStrings = [];
+ for (var outputUnit in codeFragment.outputUnits) {
+ if (!_omittedOutputUnits.contains(outputUnit)) {
+ outputUnitStrings.add(outputUnitString(outputUnit));
+ }
+ }
+ if (outputUnitStrings.isNotEmpty) {
+ supplied.add(outputUnitStrings.join('+'));
+ }
+ }
+
+ if (supplied.isNotEmpty) {
+ var suppliedString = '[${supplied.join(', ')}]';
+ features.addElement(Tags.finalizedFragments, 'f$i: $suppliedString');
+ }
+ }
+
+ _fragmentsToLoad.forEach((loadId, finalizedFragments) {
+ List<String> finalizedFragmentNeeds = [];
+ for (var finalizedFragment in finalizedFragments) {
+ assert(finalizedFragmentIndices.containsKey(finalizedFragment));
+ finalizedFragmentNeeds
+ .add('f${finalizedFragmentIndices[finalizedFragment]}');
+ }
+ features.addElement(
+ Tags.steps, '$loadId=(${finalizedFragmentNeeds.join(', ')})');
+ });
+
+ return features;
+ }
+}
+
+class OutputUnitIrComputer extends IrDataExtractor<Features> {
+ final JsToElementMap _elementMap;
+ final OutputUnitData _data;
+ final ClosureData _closureDataLookup;
+
+ Set<String> _constants = {};
+
+ OutputUnitIrComputer(
+ DiagnosticReporter reporter,
+ Map<Id, ActualData<Features>> actualMap,
+ this._elementMap,
+ this._data,
+ this._closureDataLookup)
+ : super(reporter, actualMap);
+
+ Features getMemberValue(
+ String tag, MemberEntity member, Set<String> constants) {
+ Features features = Features();
+ features.add(tag,
+ value: outputUnitString(_data.outputUnitForMemberForTesting(member)));
+ for (var constant in constants) {
+ features.addElement(Tags.constants, constant);
+ }
+ return features;
+ }
+
+ @override
+ Features computeClassValue(Id id, ir.Class node) {
+ var cls = _elementMap.getClass(node);
+ Features features = Features();
+ features.add(Tags.cls,
+ value: outputUnitString(_data.outputUnitForClassForTesting(cls)));
+ features.add(Tags.type,
+ value: outputUnitString(_data.outputUnitForClassTypeForTesting(cls)));
+ return features;
+ }
+
+ @override
+ Features computeMemberValue(Id id, ir.Member node) {
+ if (node is ir.Field && node.isConst) {
+ ir.Expression initializer = node.initializer;
+ ConstantValue constant = _elementMap.getConstantValue(node, initializer);
+ if (!constant.isPrimitive) {
+ SourceSpan span = computeSourceSpanFromTreeNode(initializer);
+ if (initializer is ir.ConstructorInvocation) {
+ // Adjust the source-span to match the AST-based location. The kernel FE
+ // skips the "const" keyword for the expression offset and any prefix in
+ // front of the constructor. The "-6" is an approximation assuming that
+ // there is just a single space after "const" and no prefix.
+ // TODO(sigmund): offsets should be fixed in the FE instead.
+ span = SourceSpan(span.uri, span.begin - 6, span.end - 6);
+ }
+ _registerValue(
+ NodeId(span.begin, IdKind.node),
+ Features.fromMap({
+ Tags.member: outputUnitString(
+ _data.outputUnitForConstantForTesting(constant))
+ }),
+ node,
+ span,
+ actualMap,
+ reporter);
+ }
+ }
+
+ Features features =
+ getMemberValue(Tags.member, _elementMap.getMember(node), _constants);
+ _constants = {};
+ return features;
+ }
+
+ @override
+ visitConstantExpression(ir.ConstantExpression node) {
+ ConstantValue constant = _elementMap.getConstantValue(null, node);
+ if (!constant.isPrimitive) {
+ _constants.add('${constant.toStructuredText(_elementMap.types)}='
+ '${outputUnitString(_data.outputUnitForConstant(constant))}');
+ }
+ return super.visitConstantExpression(node);
+ }
+
+ @override
+ Features computeNodeValue(Id id, ir.TreeNode node) {
+ if (node is ir.FunctionExpression || node is ir.FunctionDeclaration) {
+ ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo(node);
+ return getMemberValue(Tags.closure, info.callMethod, const {});
+ }
+ return null;
+ }
+}
+
+/// Set [actualMap] to hold a key of [id] with the computed data [value]
+/// corresponding to [object] at location [sourceSpan]. We also perform error
+/// checking to ensure that the same [id] isn't added twice.
+void _registerValue<T>(Id id, T value, Object object, SourceSpan sourceSpan,
+ Map<Id, ActualData<T>> actualMap, CompilerDiagnosticReporter reporter) {
+ if (actualMap.containsKey(id)) {
+ ActualData<T> existingData = actualMap[id];
+ reportHere(reporter, sourceSpan,
+ "Duplicate id ${id}, value=$value, object=$object");
+ reportHere(
+ reporter,
+ sourceSpan,
+ "Duplicate id ${id}, value=${existingData.value}, "
+ "object=${existingData.object}");
+ Expect.fail("Duplicate id $id.");
+ }
+ if (value != null) {
+ actualMap[id] =
+ ActualData<T>(id, value, sourceSpan.uri, sourceSpan.begin, object);
+ }
+}