Skip to content

Commit 10abacb

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
Use hasOnlyNonDeferredImportPathsToConstant to add deferred constants
Change-Id: Ifd8b733c526fe4e7ae837a7d17c682e91717feb5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/98013 Commit-Queue: Johnni Winther <[email protected]> Reviewed-by: Sigmund Cherem <[email protected]>
1 parent 32a5fa0 commit 10abacb

10 files changed

+328
-160
lines changed

pkg/compiler/lib/src/deferred_load.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,21 @@ class OutputUnitData {
14391439
return outputUnitTo._imports.containsAll(outputUnitFrom._imports);
14401440
}
14411441

1442+
/// Returns `true` if constant [to] is reachable from element [from] without
1443+
/// crossing a deferred import.
1444+
///
1445+
/// For example, if we have two deferred libraries `A` and `B` that both
1446+
/// import a library `C`, then even though elements from `A` and `C` end up in
1447+
/// different output units, there is a non-deferred path between `A` and `C`.
1448+
bool hasOnlyNonDeferredImportPathsToConstant(
1449+
MemberEntity from, ConstantValue to) {
1450+
OutputUnit outputUnitFrom = outputUnitForMember(from);
1451+
OutputUnit outputUnitTo = outputUnitForConstant(to);
1452+
if (outputUnitTo == mainOutputUnit) return true;
1453+
if (outputUnitFrom == mainOutputUnit) return false;
1454+
return outputUnitTo._imports.containsAll(outputUnitFrom._imports);
1455+
}
1456+
14421457
/// Registers that a constant is used in the same deferred output unit as
14431458
/// [field].
14441459
void registerConstantDeferredUse(

pkg/compiler/lib/src/ssa/builder_kernel.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,8 +1527,8 @@ class KernelSsaGraphBuilder extends ir.Visitor
15271527
@override
15281528
void visitConstantExpression(ir.ConstantExpression node) {
15291529
ConstantValue value = _elementMap.getConstantValue(node);
1530-
ir.LibraryDependency import = getDeferredImport(node);
1531-
if (import != null) {
1530+
if (!closedWorld.outputUnitData
1531+
.hasOnlyNonDeferredImportPathsToConstant(targetElement, value)) {
15321532
stack.add(graph.addDeferredConstant(
15331533
value,
15341534
closedWorld.outputUnitData.outputUnitForConstant(value),
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Test that the additional runtime type support is output to the right
6+
// Files when using deferred loading.
7+
8+
import 'package:compiler/compiler_new.dart';
9+
import 'package:compiler/src/commandline_options.dart';
10+
import 'package:compiler/src/compiler.dart';
11+
import 'package:compiler/src/constants/values.dart';
12+
import 'package:compiler/src/deferred_load.dart';
13+
import 'package:compiler/src/elements/entities.dart';
14+
import 'package:compiler/src/js_emitter/model.dart';
15+
import 'package:compiler/src/util/util.dart';
16+
import 'package:expect/expect.dart';
17+
import '../helpers/memory_compiler.dart';
18+
import '../helpers/output_collector.dart';
19+
import '../helpers/program_lookup.dart';
20+
21+
class OutputUnitDescriptor {
22+
final String uri;
23+
final String member;
24+
final String name;
25+
26+
const OutputUnitDescriptor(this.uri, this.member, this.name);
27+
}
28+
29+
run(Map<String, String> sourceFiles, List<OutputUnitDescriptor> outputUnits,
30+
Map<String, Set<String>> expectedOutputUnits,
31+
{bool useCFEConstants: false}) async {
32+
OutputCollector collector = new OutputCollector();
33+
CompilationResult result = await runCompiler(
34+
memorySourceFiles: sourceFiles,
35+
outputProvider: collector,
36+
options: useCFEConstants
37+
? ['${Flags.enableLanguageExperiments}=constant-update-2018']
38+
: ['${Flags.enableLanguageExperiments}=no-constant-update-2018']);
39+
Compiler compiler = result.compiler;
40+
ProgramLookup lookup = new ProgramLookup(compiler);
41+
var closedWorld = compiler.backendClosedWorldForTesting;
42+
var elementEnvironment = closedWorld.elementEnvironment;
43+
44+
LibraryEntity lookupLibrary(name) {
45+
return elementEnvironment.lookupLibrary(Uri.parse(name));
46+
}
47+
48+
OutputUnit Function(MemberEntity) outputUnitForMember =
49+
closedWorld.outputUnitData.outputUnitForMember;
50+
51+
Map<String, Fragment> fragments = {};
52+
fragments['main'] = lookup.program.mainFragment;
53+
54+
for (OutputUnitDescriptor descriptor in outputUnits) {
55+
LibraryEntity library = lookupLibrary(descriptor.uri);
56+
MemberEntity member =
57+
elementEnvironment.lookupLibraryMember(library, descriptor.member);
58+
OutputUnit outputUnit = outputUnitForMember(member);
59+
fragments[descriptor.name] = lookup.getFragment(outputUnit);
60+
}
61+
62+
Map<String, Set<String>> actualOutputUnits = {};
63+
64+
bool errorsFound = false;
65+
66+
void processFragment(String fragmentName, Fragment fragment) {
67+
for (Constant constant in fragment.constants) {
68+
String text = constant.value.toStructuredText();
69+
Set<String> expectedConstantUnit = expectedOutputUnits[text];
70+
if (expectedConstantUnit == null) {
71+
if (constant.value is DeferredGlobalConstantValue) {
72+
print('ERROR: No expectancy for $constant found in $fragmentName');
73+
errorsFound = true;
74+
}
75+
} else {
76+
(actualOutputUnits[text] ??= <String>{}).add(fragmentName);
77+
}
78+
}
79+
}
80+
81+
fragments.forEach(processFragment);
82+
83+
expectedOutputUnits.forEach((String constant, Set<String> expectedSet) {
84+
Set<String> actualSet = actualOutputUnits[constant] ?? const <String>{};
85+
if (!equalSets(expectedSet, actualSet)) {
86+
print("ERROR: Constant $constant found in $actualSet, expected "
87+
"$expectedSet");
88+
errorsFound = true;
89+
}
90+
});
91+
92+
Expect.isFalse(errorsFound, "Errors found.");
93+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Test that the additional runtime type support is output to the right
6+
// Files when using deferred loading.
7+
8+
import 'package:async_helper/async_helper.dart';
9+
import 'constant_emission_test_helper.dart';
10+
11+
void main() {
12+
runTest({bool useCFEConstants: false}) async {
13+
Map<String, Set<String>> expectedOutputUnits = {
14+
'ConstructedConstant(C(x=IntConstant(1)))': {'main'},
15+
'DeferredGlobalConstant(ConstructedConstant(C(x=IntConstant(1))))':
16+
// With CFE constants, the references are inlined, so the constant
17+
// only occurs in main.
18+
useCFEConstants ? {} : {'lib2'},
19+
'ConstructedConstant(C(x=IntConstant(2)))': {'lib1'},
20+
'DeferredGlobalConstant(ConstructedConstant(C(x=IntConstant(2))))': {
21+
'lib1'
22+
},
23+
'ConstructedConstant(C(x=IntConstant(3)))': {'lib1'},
24+
'ConstructedConstant(C(x=IntConstant(4)))': {'lib2'},
25+
'DeferredGlobalConstant(ConstructedConstant(C(x=IntConstant(4))))': {
26+
'lib2'
27+
},
28+
'ConstructedConstant(C(x=IntConstant(5)))': {'lib2'},
29+
};
30+
await run(
31+
MEMORY_SOURCE_FILES,
32+
const [
33+
OutputUnitDescriptor('memory:lib1.dart', 'm1', 'lib1'),
34+
OutputUnitDescriptor('memory:lib2.dart', 'm2', 'lib2'),
35+
],
36+
expectedOutputUnits,
37+
useCFEConstants: useCFEConstants);
38+
}
39+
40+
asyncTest(() async {
41+
print('--test from kernel------------------------------------------------');
42+
await runTest();
43+
print('--test from kernel with CFE constants-----------------------------');
44+
await runTest(useCFEConstants: true);
45+
});
46+
}
47+
48+
// Make sure that deferred constants are not inlined into the main hunk.
49+
const Map<String, String> MEMORY_SOURCE_FILES = const {
50+
"main.dart": r"""
51+
import 'c.dart';
52+
import 'lib1.dart' deferred as l1;
53+
54+
const c1 = const C(1);
55+
56+
main() async {
57+
print(c1.x);
58+
await l1.loadLibrary();
59+
l1.m1();
60+
print(l1.c2);
61+
}
62+
""",
63+
"lib1.dart": """
64+
import 'c.dart';
65+
import 'lib2.dart' deferred as l2;
66+
67+
const c2 = const C(2);
68+
const c3 = const C(3);
69+
70+
m1() async {
71+
print(c2);
72+
print(c3);
73+
await l2.loadLibrary();
74+
l2.m2();
75+
print(l2.c3);
76+
print(l2.c4);
77+
}
78+
""",
79+
"lib2.dart": """
80+
import 'c.dart';
81+
82+
const c3 = const C(1);
83+
const c4 = const C(4);
84+
const c5 = const C(5);
85+
86+
m2() async {
87+
print(c3);
88+
print(c4);
89+
print(c5);
90+
}
91+
""",
92+
"c.dart": """
93+
class C { const C(this.x); final x; }
94+
""",
95+
};

tests/compiler/dart2js/deferred/dont_inline_deferred_constants_test.dart

Lines changed: 22 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,113 +6,46 @@
66
// Files when using deferred loading.
77

88
import 'package:async_helper/async_helper.dart';
9-
import 'package:compiler/compiler_new.dart';
10-
import 'package:compiler/src/commandline_options.dart';
11-
import 'package:compiler/src/compiler.dart';
12-
import 'package:compiler/src/constants/values.dart';
13-
import 'package:compiler/src/deferred_load.dart';
14-
import 'package:compiler/src/elements/entities.dart';
15-
import 'package:compiler/src/js_emitter/model.dart';
16-
import 'package:expect/expect.dart';
17-
import '../helpers/memory_compiler.dart';
18-
import '../helpers/output_collector.dart';
19-
import '../helpers/program_lookup.dart';
9+
import 'constant_emission_test_helper.dart';
2010

2111
void main() {
2212
runTest({bool useCFEConstants: false}) async {
23-
OutputCollector collector = new OutputCollector();
24-
CompilationResult result = await runCompiler(
25-
memorySourceFiles: MEMORY_SOURCE_FILES,
26-
outputProvider: collector,
27-
options: useCFEConstants
28-
? ['${Flags.enableLanguageExperiments}=constant-update-2018']
29-
: ['${Flags.enableLanguageExperiments}=no-constant-update-2018']);
30-
Compiler compiler = result.compiler;
31-
ProgramLookup lookup = new ProgramLookup(compiler);
32-
var closedWorld = compiler.backendClosedWorldForTesting;
33-
var elementEnvironment = closedWorld.elementEnvironment;
34-
35-
LibraryEntity lookupLibrary(name) {
36-
return elementEnvironment.lookupLibrary(Uri.parse(name));
37-
}
38-
39-
OutputUnit Function(MemberEntity) outputUnitForMember =
40-
closedWorld.outputUnitData.outputUnitForMember;
41-
42-
LibraryEntity lib1 = lookupLibrary("memory:lib1.dart");
43-
MemberEntity foo1 = elementEnvironment.lookupLibraryMember(lib1, "foo");
44-
OutputUnit ou_lib1 = outputUnitForMember(foo1);
45-
46-
LibraryEntity lib2 = lookupLibrary("memory:lib2.dart");
47-
MemberEntity foo2 = elementEnvironment.lookupLibraryMember(lib2, "foo");
48-
OutputUnit ou_lib2 = outputUnitForMember(foo2);
49-
50-
LibraryEntity mainApp = elementEnvironment.mainLibrary;
51-
MemberEntity fooMain =
52-
elementEnvironment.lookupLibraryMember(mainApp, "foo");
53-
OutputUnit ou_lib1_lib2 = outputUnitForMember(fooMain);
54-
5513
Map<String, Set<String>> expectedOutputUnits = {
5614
// Test that the deferred constants are not inlined into the main file.
57-
'IntConstant(1010)': {'lib1'},
58-
'StringConstant("string1")': {'lib1'},
59-
'StringConstant("string2")': {'lib1'},
15+
'DeferredGlobalConstant(IntConstant(1010))': {'lib1'},
16+
'DeferredGlobalConstant(StringConstant("string1"))': {'lib1'},
17+
'DeferredGlobalConstant(StringConstant("string2"))': {'lib1'},
6018
// "string4" is shared between lib1 and lib2, but it can be inlined.
61-
'StringConstant("string4")':
19+
'DeferredGlobalConstant(StringConstant("string4"))':
6220
// TODO(johnniwinther): Should we inline CFE constants within deferred
6321
// library boundaries?
6422
useCFEConstants ? {'lib12'} : {'lib1', 'lib2'},
6523
// C(1) is shared between main, lib1 and lib2. Test that lib1 and lib2
6624
// each has a reference to it. It is defined in the main output file.
67-
'ConstructedConstant(C(p=IntConstant(1)))':
68-
// With CFE constants, the references are inlined, so the constant only
69-
// occurs in main.
70-
useCFEConstants ? {'main'} : {'main', 'lib1', 'lib2'},
25+
'ConstructedConstant(C(p=IntConstant(1)))': {'main'},
26+
'DeferredGlobalConstant(ConstructedConstant(C(p=IntConstant(1))))':
27+
// With CFE constants, the references are inlined, so the constant
28+
// only occurs in main.
29+
useCFEConstants ? {} : {'lib1', 'lib2'},
7130
// C(2) is shared between lib1 and lib2, each of them has their own
7231
// reference to it.
73-
'ConstructedConstant(C(p=IntConstant(2)))':
32+
'ConstructedConstant(C(p=IntConstant(2)))': {'lib12'},
33+
'DeferredGlobalConstant(ConstructedConstant(C(p=IntConstant(2))))':
7434
// With CFE constants, the references are inlined, so the constant
7535
// occurs in lib12.
76-
useCFEConstants ? {'lib12'} : {'lib1', 'lib2', 'lib12'},
36+
useCFEConstants ? {'lib12'} : {'lib1', 'lib2'},
7737
// Test that the non-deferred constant is inlined.
7838
'ConstructedConstant(C(p=IntConstant(5)))': {'main'},
7939
};
80-
81-
Map<String, Set<String>> actualOutputUnits = {};
82-
83-
void processFragment(Fragment fragment, String fragmentName) {
84-
for (Constant constant in fragment.constants) {
85-
String text;
86-
if (constant.value is DeferredGlobalConstantValue) {
87-
DeferredGlobalConstantValue deferred = constant.value;
88-
text = deferred.referenced.toStructuredText();
89-
} else {
90-
text = constant.value.toStructuredText();
91-
}
92-
Set<String> expectedConstantUnit = expectedOutputUnits[text];
93-
if (expectedConstantUnit == null) {
94-
if (constant.value is DeferredGlobalConstantValue) {
95-
print('No expectancy for $constant found in $fragmentName');
96-
}
97-
} else {
98-
(actualOutputUnits[text] ??= <String>{}).add(fragmentName);
99-
}
100-
}
101-
}
102-
103-
processFragment(lookup.program.mainFragment, 'main');
104-
processFragment(lookup.getFragment(ou_lib1), 'lib1');
105-
processFragment(lookup.getFragment(ou_lib2), 'lib2');
106-
processFragment(lookup.getFragment(ou_lib1_lib2), 'lib12');
107-
108-
expectedOutputUnits.forEach((String constant, Set<String> expectedSet) {
109-
Set<String> actualSet = actualOutputUnits[constant] ?? const <String>{};
110-
Expect.setEquals(
111-
expectedSet,
112-
actualSet,
113-
"Constant $constant found in $actualSet, expected "
114-
"$expectedSet");
115-
});
40+
await run(
41+
MEMORY_SOURCE_FILES,
42+
const [
43+
OutputUnitDescriptor('memory:lib1.dart', 'foo', 'lib1'),
44+
OutputUnitDescriptor('memory:lib2.dart', 'foo', 'lib2'),
45+
OutputUnitDescriptor('memory:main.dart', 'foo', 'lib12')
46+
],
47+
expectedOutputUnits,
48+
useCFEConstants: useCFEConstants);
11649
}
11750

11851
asyncTest(() async {

0 commit comments

Comments
 (0)