Skip to content

Commit bae176b

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
[cfe+dart2js] Add shared constant evaluation test.
Change-Id: I11fa0f576c5d177d85d3c17cfa075b7dd91326b8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108604 Commit-Queue: Johnni Winther <[email protected]> Reviewed-by: Jens Johansen <[email protected]>
1 parent fdd3822 commit bae176b

File tree

14 files changed

+1195
-22
lines changed

14 files changed

+1195
-22
lines changed

pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,11 +444,12 @@ class ConstantsTransformer extends Transformer {
444444
}
445445
}
446446

447-
evaluateAndTransformWithContext(TreeNode treeContext, Expression node) {
447+
Expression evaluateAndTransformWithContext(
448+
TreeNode treeContext, Expression node) {
448449
return makeConstantExpression(evaluateWithContext(treeContext, node), node);
449450
}
450451

451-
evaluateWithContext(TreeNode treeContext, Expression node) {
452+
Constant evaluateWithContext(TreeNode treeContext, Expression node) {
452453
if (treeContext == node) {
453454
return constantEvaluator.evaluate(node);
454455
}
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
// Copyright (c) 2019, 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+
import 'package:front_end/src/api_prototype/compiler_options.dart'
6+
show CompilerOptions;
7+
import 'package:front_end/src/api_prototype/experimental_flags.dart'
8+
show ExperimentalFlag;
9+
import 'package:front_end/src/testing/id_extractor.dart' show DataExtractor;
10+
import 'package:kernel/ast.dart';
11+
import '../kernel_generator_impl.dart' show CompilerResult;
12+
import 'compiler_common.dart' show compileScript, toTestUri;
13+
import 'id.dart' show ActualData, ClassId, Id, IdValue, MemberId, NodeId;
14+
import 'id_testing.dart'
15+
show
16+
CompiledData,
17+
DataInterpreter,
18+
MemberAnnotations,
19+
RunTestFunction,
20+
TestData,
21+
cfeMarker,
22+
checkCode;
23+
import 'id_testing_utils.dart';
24+
25+
export '../fasta/compiler_context.dart' show CompilerContext;
26+
export '../kernel_generator_impl.dart' show CompilerResult;
27+
28+
/// Test configuration used for testing CFE in its default state.
29+
const TestConfig defaultCfeConfig = const TestConfig(cfeMarker, 'cfe');
30+
31+
/// Test configuration used for testing CFE in with constant evaluation.
32+
const TestConfig cfeConstantUpdate2018Config = const TestConfig(
33+
cfeMarker, 'cfe with constant-update-2018',
34+
experimentalFlags: const {ExperimentalFlag.constantUpdate2018: true});
35+
36+
class TestConfig {
37+
final String marker;
38+
final String name;
39+
final Map<ExperimentalFlag, bool> experimentalFlags;
40+
41+
const TestConfig(this.marker, this.name, {this.experimentalFlags = const {}});
42+
}
43+
44+
abstract class DataComputer<T> {
45+
const DataComputer();
46+
47+
/// Called before testing to setup flags needed for data collection.
48+
void setup() {}
49+
50+
/// Function that computes a data mapping for [member].
51+
///
52+
/// Fills [actualMap] with the data.
53+
void computeMemberData(CompilerResult compilerResult, Member member,
54+
Map<Id, ActualData<T>> actualMap,
55+
{bool verbose});
56+
57+
/// Function that computes a data mapping for [cls].
58+
///
59+
/// Fills [actualMap] with the data.
60+
void computeClassData(CompilerResult compilerResult, Class cls,
61+
Map<Id, ActualData<T>> actualMap,
62+
{bool verbose}) {}
63+
64+
/// Returns the [DataInterpreter] used to check the actual data with the
65+
/// expected data.
66+
DataInterpreter<T> get dataValidator;
67+
}
68+
69+
class CfeCompiledData<T> extends CompiledData<T> {
70+
final CompilerResult compilerResult;
71+
72+
CfeCompiledData(
73+
this.compilerResult,
74+
Uri mainUri,
75+
Map<Uri, Map<Id, ActualData<T>>> actualMaps,
76+
Map<Id, ActualData<T>> globalData)
77+
: super(mainUri, actualMaps, globalData);
78+
79+
@override
80+
int getOffsetFromId(Id id, Uri uri) {
81+
if (id is NodeId) return id.value;
82+
if (id is MemberId) {
83+
Library library = lookupLibrary(compilerResult.component, uri);
84+
Member member;
85+
if (id.className != null) {
86+
Class cls = lookupClass(library, id.className);
87+
member = lookupClassMember(cls, id.memberName);
88+
} else {
89+
member = lookupLibraryMember(library, id.memberName);
90+
}
91+
return member.fileOffset;
92+
} else if (id is ClassId) {
93+
Library library = lookupLibrary(compilerResult.component, uri);
94+
Class cls = lookupClass(library, id.className);
95+
return cls.fileOffset;
96+
}
97+
return null;
98+
}
99+
100+
@override
101+
void reportError(Uri uri, int offset, String message) {
102+
printMessageInLocation(
103+
compilerResult.component.uriToSource, uri, offset, message);
104+
}
105+
}
106+
107+
abstract class CfeDataExtractor<T> extends DataExtractor<T> {
108+
final CompilerResult compilerResult;
109+
110+
CfeDataExtractor(this.compilerResult, Map<Id, ActualData<T>> actualMap)
111+
: super(actualMap);
112+
113+
@override
114+
void report(Uri uri, int offset, String message) {
115+
printMessageInLocation(
116+
compilerResult.component.uriToSource, uri, offset, message);
117+
}
118+
119+
@override
120+
void fail(String message) {
121+
onFailure(message);
122+
}
123+
}
124+
125+
/// Create the testing URI used for [fileName] in annotated tests.
126+
Uri createUriForFileName(String fileName, {bool isLib}) => toTestUri(fileName);
127+
128+
void onFailure(String message) => throw new StateError(message);
129+
130+
/// Creates a test runner for [dataComputer] on [testedConfigs].
131+
RunTestFunction runTestFor<T>(
132+
DataComputer<T> dataComputer, List<TestConfig> testedConfigs) {
133+
return (TestData testData,
134+
{bool testAfterFailures, bool verbose, bool printCode}) {
135+
return runTest(testData, dataComputer, testedConfigs,
136+
testAfterFailures: testAfterFailures,
137+
verbose: verbose,
138+
printCode: printCode,
139+
onFailure: onFailure);
140+
};
141+
}
142+
143+
/// Runs [dataComputer] on [testData] for all [testedConfigs].
144+
///
145+
/// Returns `true` if an error was encountered.
146+
Future<bool> runTest<T>(TestData testData, DataComputer<T> dataComputer,
147+
List<TestConfig> testedConfigs,
148+
{bool testAfterFailures,
149+
bool verbose,
150+
bool printCode,
151+
bool forUserLibrariesOnly: true,
152+
Iterable<Id> globalIds: const <Id>[],
153+
void onFailure(String message)}) async {
154+
bool hasFailures = false;
155+
for (TestConfig config in testedConfigs) {
156+
if (await runTestForConfig(testData, dataComputer, config,
157+
fatalErrors: !testAfterFailures,
158+
onFailure: onFailure,
159+
verbose: verbose,
160+
printCode: printCode)) {
161+
hasFailures = true;
162+
}
163+
}
164+
return hasFailures;
165+
}
166+
167+
/// Runs [dataComputer] on [testData] for [config].
168+
///
169+
/// Returns `true` if an error was encountered.
170+
Future<bool> runTestForConfig<T>(
171+
TestData testData, DataComputer<T> dataComputer, TestConfig config,
172+
{bool fatalErrors,
173+
bool verbose,
174+
bool printCode,
175+
bool forUserLibrariesOnly: true,
176+
Iterable<Id> globalIds: const <Id>[],
177+
void onFailure(String message)}) async {
178+
MemberAnnotations<IdValue> memberAnnotations =
179+
testData.expectedMaps[config.marker];
180+
Iterable<Id> globalIds = memberAnnotations.globalData.keys;
181+
CompilerOptions options = new CompilerOptions();
182+
options.debugDump = printCode;
183+
options.experimentalFlags.addAll(config.experimentalFlags);
184+
CompilerResult compilerResult = await compileScript(
185+
testData.memorySourceFiles,
186+
options: options,
187+
retainDataForTesting: true);
188+
Component component = compilerResult.component;
189+
Map<Uri, Map<Id, ActualData<T>>> actualMaps = <Uri, Map<Id, ActualData<T>>>{};
190+
Map<Id, ActualData<T>> globalData = <Id, ActualData<T>>{};
191+
192+
Map<Id, ActualData<T>> actualMapFor(TreeNode node) {
193+
Uri uri = node.location.file;
194+
return actualMaps.putIfAbsent(uri, () => <Id, ActualData<T>>{});
195+
}
196+
197+
void processMember(Member member, Map<Id, ActualData<T>> actualMap) {
198+
if (member.enclosingClass != null) {
199+
if (member.enclosingClass.isEnum) {
200+
if (member is Constructor ||
201+
member.isInstanceMember ||
202+
member.name == 'values') {
203+
return;
204+
}
205+
}
206+
if (member is Constructor && member.enclosingClass.isMixinApplication) {
207+
return;
208+
}
209+
}
210+
dataComputer.computeMemberData(compilerResult, member, actualMap,
211+
verbose: verbose);
212+
}
213+
214+
void processClass(Class cls, Map<Id, ActualData<T>> actualMap) {
215+
dataComputer.computeClassData(compilerResult, cls, actualMap,
216+
verbose: verbose);
217+
}
218+
219+
bool excludeLibrary(Library library) {
220+
return forUserLibrariesOnly &&
221+
(library.importUri.scheme == 'dart' ||
222+
library.importUri.scheme == 'package');
223+
}
224+
225+
for (Library library in component.libraries) {
226+
if (excludeLibrary(library)) continue;
227+
for (Class cls in library.classes) {
228+
processClass(cls, actualMapFor(cls));
229+
for (Member member in cls.members) {
230+
processMember(member, actualMapFor(member));
231+
}
232+
}
233+
for (Member member in library.members) {
234+
processMember(member, actualMapFor(member));
235+
}
236+
}
237+
238+
List<Uri> globalLibraries = <Uri>[
239+
Uri.parse('dart:core'),
240+
Uri.parse('dart:collection'),
241+
Uri.parse('dart:async'),
242+
];
243+
244+
Class getGlobalClass(String className) {
245+
Class cls;
246+
for (Uri uri in globalLibraries) {
247+
Library library = lookupLibrary(component, uri);
248+
if (library != null) {
249+
cls ??= lookupClass(library, className);
250+
}
251+
}
252+
if (cls == null) {
253+
throw "Global class '$className' not found in the global "
254+
"libraries: ${globalLibraries.join(', ')}";
255+
}
256+
return cls;
257+
}
258+
259+
Member getGlobalMember(String memberName) {
260+
Member member;
261+
for (Uri uri in globalLibraries) {
262+
Library library = lookupLibrary(component, uri);
263+
if (library != null) {
264+
member ??= lookupLibraryMember(library, memberName);
265+
}
266+
}
267+
if (member == null) {
268+
throw "Global member '$memberName' not found in the global "
269+
"libraries: ${globalLibraries.join(', ')}";
270+
}
271+
return member;
272+
}
273+
274+
for (Id id in globalIds) {
275+
if (id is MemberId) {
276+
Member member;
277+
if (id.className != null) {
278+
Class cls = getGlobalClass(id.className);
279+
member = lookupClassMember(cls, id.memberName);
280+
if (member == null) {
281+
throw "Global member '${id.memberName}' not found in class $cls.";
282+
}
283+
} else {
284+
member = getGlobalMember(id.memberName);
285+
}
286+
processMember(member, globalData);
287+
} else if (id is ClassId) {
288+
Class cls = getGlobalClass(id.className);
289+
processClass(cls, globalData);
290+
} else {
291+
throw new UnsupportedError("Unexpected global id: $id");
292+
}
293+
}
294+
295+
CfeCompiledData compiledData = new CfeCompiledData<T>(
296+
compilerResult, testData.testFileUri, actualMaps, globalData);
297+
298+
return checkCode(config.name, testData.testFileUri, testData.code,
299+
memberAnnotations, compiledData, dataComputer.dataValidator,
300+
fatalErrors: fatalErrors, onFailure: onFailure);
301+
}
302+
303+
void printMessageInLocation(
304+
Map<Uri, Source> uriToSource, Uri uri, int offset, String message) {
305+
Source source = uriToSource[uri];
306+
if (source == null) {
307+
print('$uri@$offset: $message');
308+
}
309+
Location location = source.getLocation(uri, offset);
310+
print('$location: $message');
311+
print(source.getTextLine(location.line));
312+
print(' ' * (location.column - 1) + '^');
313+
}

0 commit comments

Comments
 (0)