[ddc] Clean up expression compiler tests

- Allow running the tests with asserts enabled

- Allow running the tests in canary mode

- Unify common test code and remove duplication
   - merge expression_compiler/setup_compiler_options.dart
     into share_compiler_options.dart

   - Merge TestCompiler code from expression evaluation and
     expression compilation tests and move into
     expression_compiler/test_compiler.dart

   - Rename various TestDrivers so they have more descriptive names

- Remove 'golden' JS comparison tests from expression_compiler_test.dart
   - replace by evaluation tests where needed

Closes: https://github.com/dart-lang/sdk/issues/53145
Change-Id: Ic797fa4ee9bfa6b858b924be9f9a53fd10ae1448
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/318080
Commit-Queue: Anna Gringauze <annagrin@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Anna Gringauze 2023-08-08 16:21:31 +00:00 committed by Commit Queue
parent 286e5761e3
commit 67d752aa12
32 changed files with 584 additions and 1270 deletions

View file

@ -35,5 +35,4 @@ dev_dependencies:
stack_trace: any
test: any
testing: any
vm: any
webkit_inspection_protocol: any

View file

@ -4,13 +4,14 @@
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
var setup = SetupCompilerOptions(args: args);
group('Assertions |', () {
group('Asserts', () {
const source = r'''
void main() {
var b = const bool.fromEnvironment('dart.web.assertions_enabled');
@ -24,60 +25,66 @@ void main(List<String> args) async {
}
''';
tearDown(() async {
await driver.cleanupTest();
});
setUpAll(() => driver.initSource(setup, source));
tearDownAll(() async {
await driver.cleanupTest();
await driver.finish();
});
test('dart.web.assertions_enabled is set when asserts are enabled',
() async {
var setup = SetupCompilerOptions(args: args);
await driver.initSource(setup, source);
if (setup.enableAsserts) {
group('enabled |', () {
test('dart.web.assertions_enabled is set', () async {
await driver.check(
breakpointId: 'bp', expression: 'b', expectedResult: 'true');
});
await driver.check(
breakpointId: 'bp',
expression: 'b',
expectedResult: '${setup.enableAsserts}');
});
test('assert errors in the source code when asserts are enabled', () async {
var setup = SetupCompilerOptions(args: args);
await driver.initSource(setup, source);
await driver.check(
breakpointId: 'bp',
expression: 'myAssert()',
expectedResult: setup.enableAsserts
? allOf(
test('assert errors in the source code', () async {
await driver.check(
breakpointId: 'bp',
expression: 'myAssert()',
expectedResult: allOf(
contains('Error: Assertion failed:'),
contains('test.dart:8:16'),
contains('false'),
contains('is not true'),
)
: '0',
);
});
));
});
test('assert errors in evaluated expression when asserts are enabled',
() async {
var setup = SetupCompilerOptions(args: args);
await driver.initSource(setup, source);
await driver.check(
breakpointId: 'bp',
expression: '() { assert(false); return 0; } ()',
expectedResult: setup.enableAsserts
? allOf(
test('assert errors in evaluated expression', () async {
await driver.check(
breakpointId: 'bp',
expression: '() { assert(false); return 0; } ()',
expectedResult: allOf(
contains('Error: Assertion failed:'),
contains('<unknown source>:-1:-1'),
contains('BoolLiteral(false)'),
contains('is not true'),
)
: '0',
);
});
));
});
});
}
if (!setup.enableAsserts) {
group('disabled |', () {
test('dart.web.assertions_enabled is not set', () async {
await driver.check(
breakpointId: 'bp', expression: 'b', expectedResult: 'false');
});
test('no assert errors in the source code', () async {
await driver.check(
breakpointId: 'bp',
expression: 'myAssert()',
expectedResult: '0');
});
test('no assert errors in evaluated expression', () async {
await driver.check(
breakpointId: 'bp',
expression: '() { assert(false); return 0; } ()',
expectedResult: '0');
});
});
}
});
}

View file

@ -6,13 +6,15 @@ import 'dart:io';
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
var setup = SetupCompilerOptions(args: args);
var mode = setup.canaryFeatures ? 'canary' : 'stable';
group('canary', () {
group('$mode mode', () {
const source = r'''
void main() {
print('hello world');
@ -28,7 +30,6 @@ void main(List<String> args) async {
});
test('is automatically set to the configuration value', () async {
var setup = SetupCompilerOptions(args: args);
await driver.initSource(setup, source);
expect(

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Sound null safety) (Agnostic code shard 1)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Sound null safety) (Agnostic code shard 2)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Unsound null safety) (Agnostic code shard 1)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Unsound null safety) (Agnostic code shard 2)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Legacy code shard 1)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Legacy code shard 2)', () {
tearDownAll(() async {

View file

@ -6,14 +6,14 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
// Set to `true` for debug output.
final debug = false;
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Sound null safety)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Unsound null safety)', () {
tearDownAll(() async {

View file

@ -6,11 +6,11 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('Dart 2.17 language features', () {
tearDownAll(() async {
@ -64,7 +64,8 @@ void main(List<String> args) async {
}
/// Shared tests for language features introduced in version 2.17.0.
void runSharedTests(SetupCompilerOptions setup, TestDriver driver) {
void runSharedTests(
SetupCompilerOptions setup, ExpressionEvaluationTestDriver driver) {
group('Named arguments anywhere', () {
var source = r'''
String topLevelMethod(int param1, String param2,

View file

@ -6,11 +6,11 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('Dart 3.0 language features', () {
tearDownAll(() async {
@ -64,7 +64,8 @@ void main(List<String> args) async {
}
/// Shared tests for language features introduced in version 3.0.0.
void runSharedTests(SetupCompilerOptions setup, TestDriver driver) {
void runSharedTests(
SetupCompilerOptions setup, ExpressionEvaluationTestDriver driver) {
group('Records', () {
const recordsSource = '''
void main() {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Sound null safety) (Agnostic code shard 1)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Sound null safety) (Agnostic code shard 2)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Unsound null safety) (Agnostic code shard 1)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Unsound null safety) (Agnostic code shard 2)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Legacy code shard 1)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Legacy code shard 2)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Sound null safety)', () {
tearDownAll(() async {

View file

@ -6,12 +6,12 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_shared.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
group('(Unsound null safety)', () {
tearDownAll(() async {

View file

@ -4,8 +4,8 @@
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
const simpleClassSource = '''
extension NumberParsing on String {
@ -64,7 +64,8 @@ main() {
/// Shared tests that require a language version >=2.12.0 <2.17.0.
// TODO(nshahan) Merge with [runAgnosticSharedTests] after we no longer need to
// test support for evaluation in legacy (pre-null safety) code.
void runNullSafeSharedTests(SetupCompilerOptions setup, TestDriver driver) {
void runNullSafeSharedTests(
SetupCompilerOptions setup, ExpressionEvaluationTestDriver driver) {
group('Exceptions', () {
const exceptionSource = r'''
void main() {
@ -207,6 +208,17 @@ void runNullSafeSharedTests(SetupCompilerOptions setup, TestDriver driver) {
expectedResult: 'test.C.new { Symbol(_unusedField): 4, '
'Symbol(C.field): 42, Symbol(_field): 0}');
});
test('map access', () async {
await driver.check(
breakpointId: 'methodBP',
expression: '''
(Map<String, String> params) {
return params["a"];
}({"a":"b"})
''',
expectedResult: 'b');
});
});
group('Expression compiler tests in global function:', () {
@ -470,7 +482,7 @@ void runNullSafeSharedTests(SetupCompilerOptions setup, TestDriver driver) {
/// This group of tests has been sharded manually. The others are in
/// [runAgnosticSharedTestsShard2].
void runAgnosticSharedTestsShard1(
SetupCompilerOptions setup, TestDriver driver) {
SetupCompilerOptions setup, ExpressionEvaluationTestDriver driver) {
group('Correct null safety mode used', () {
var source = '''
const soundNullSafety = !(<Null>[] is List<int>);
@ -956,7 +968,7 @@ void runAgnosticSharedTestsShard1(
/// This group of tests has been sharded manually. The others are in
/// [runAgnosticSharedTestsShard1].
void runAgnosticSharedTestsShard2(
SetupCompilerOptions setup, TestDriver driver) {
SetupCompilerOptions setup, ExpressionEvaluationTestDriver driver) {
group('Expression compiler tests in constructor:', () {
var source = simpleClassSource;

View file

@ -10,182 +10,20 @@ import 'dart:math';
import 'package:async/async.dart';
import 'package:browser_launcher/browser_launcher.dart' as browser;
import 'package:dev_compiler/src/compiler/module_builder.dart';
import 'package:dev_compiler/src/compiler/shared_command.dart'
show SharedCompilerOptions;
import 'package:dev_compiler/src/kernel/command.dart';
import 'package:dev_compiler/src/kernel/compiler.dart' show ProgramCompiler;
import 'package:dev_compiler/src/kernel/expression_compiler.dart'
show ExpressionCompiler;
import 'package:dev_compiler/src/kernel/module_metadata.dart';
import 'package:front_end/src/api_unstable/ddc.dart' as fe;
import 'package:front_end/src/fasta/incremental_serializer.dart' as fe;
import 'package:kernel/ast.dart' show Component, Library;
import 'package:path/path.dart' as p;
import 'package:source_maps/source_maps.dart' as source_maps;
import 'package:test/test.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'
as wip;
import 'setup_compiler_options.dart';
import '../shared_test_options.dart';
import 'test_compiler.dart';
class DevelopmentIncrementalCompiler extends fe.IncrementalCompiler {
Uri entryPoint;
DevelopmentIncrementalCompiler(fe.CompilerOptions options, this.entryPoint,
[Uri? initializeFrom,
bool? outlineOnly,
fe.IncrementalSerializer? incrementalSerializer])
: super(
fe.CompilerContext(
fe.ProcessedOptions(options: options, inputs: [entryPoint])),
initializeFrom,
outlineOnly,
incrementalSerializer);
DevelopmentIncrementalCompiler.fromComponent(fe.CompilerOptions options,
this.entryPoint, Component componentToInitializeFrom,
[bool? outlineOnly, fe.IncrementalSerializer? incrementalSerializer])
: super.fromComponent(
fe.CompilerContext(
fe.ProcessedOptions(options: options, inputs: [entryPoint])),
componentToInitializeFrom,
outlineOnly,
incrementalSerializer);
}
class TestCompilationResult {
final String? result;
final bool isSuccess;
TestCompilationResult(this.result, this.isSuccess);
}
class TestCompiler {
final SetupCompilerOptions setup;
final Component component;
final ExpressionCompiler evaluator;
final ModuleMetadata? metadata;
final source_maps.SingleMapping sourceMap;
TestCompiler._(this.setup, this.component, this.evaluator, this.metadata,
this.sourceMap);
static Future<TestCompiler> init(SetupCompilerOptions setup,
{required Uri input,
required Uri output,
Uri? packages,
Map<String, bool> experiments = const {}}) async {
// Initialize the incremental compiler and module component.
// TODO: extend this for multi-module compilations by storing separate
// compilers/components/names per module.
setup.options.packagesFileUri = packages;
setup.options.explicitExperimentalFlags.addAll(fe.parseExperimentalFlags(
experiments,
onError: (message) => throw Exception(message)));
var compiler = DevelopmentIncrementalCompiler(setup.options, input);
var compilerResult = await compiler.computeDelta();
var component = compilerResult.component;
component.computeCanonicalNames();
// Initialize DDC.
var moduleName = p.basenameWithoutExtension(output.toFilePath());
var classHierarchy = compilerResult.classHierarchy!;
var compilerOptions = SharedCompilerOptions(
replCompile: true,
moduleName: moduleName,
experiments: experiments,
soundNullSafety: setup.soundNullSafety,
emitDebugMetadata: true,
canaryFeatures: setup.canaryFeatures,
enableAsserts: setup.enableAsserts,
);
var coreTypes = compilerResult.coreTypes;
final importToSummary = Map<Library, Component>.identity();
final summaryToModule = Map<Component, String>.identity();
for (var lib in component.libraries) {
importToSummary[lib] = component;
}
summaryToModule[component] = moduleName;
var kernel2jsCompiler = ProgramCompiler(component, classHierarchy,
compilerOptions, importToSummary, summaryToModule,
coreTypes: coreTypes);
var module = kernel2jsCompiler.emitModule(component);
// Perform a full compile, writing the compiled JS + sourcemap.
var code = jsProgramToCode(
module,
setup.moduleFormat,
inlineSourceMap: compilerOptions.inlineSourceMap,
buildSourceMap: compilerOptions.sourceMap,
emitDebugMetadata: compilerOptions.emitDebugMetadata,
emitDebugSymbols: compilerOptions.emitDebugSymbols,
jsUrl: '$output',
mapUrl: '$output.map',
compiler: kernel2jsCompiler,
component: component,
);
var codeBytes = utf8.encode(code.code);
var sourceMapBytes = utf8.encode(json.encode(code.sourceMap));
File(output.toFilePath()).writeAsBytesSync(codeBytes);
File('${output.toFilePath()}.map').writeAsBytesSync(sourceMapBytes);
// Save the expression evaluator for future evaluations.
var evaluator = ExpressionCompiler(
setup.options,
setup.moduleFormat,
setup.errors,
compiler,
kernel2jsCompiler,
component,
);
if (setup.errors.isNotEmpty) {
throw Exception('Compilation failed with: ${setup.errors}');
}
setup.diagnosticMessages.clear();
var sourceMap = source_maps.SingleMapping.fromJson(
code.sourceMap!.cast<String, dynamic>());
return TestCompiler._(
setup, component, evaluator, code.metadata, sourceMap);
}
Future<TestCompilationResult> compileExpression(
{required Uri input,
required int line,
required int column,
required Map<String, String> scope,
required String expression}) async {
var libraryUri = metadataForLibraryUri(input);
var jsExpression = await evaluator.compileExpressionToJs(
libraryUri.importUri, line, column, scope, expression);
if (setup.errors.isNotEmpty) {
jsExpression = setup.errors.toString().replaceAll(
RegExp(
r'org-dartlang-debug:synthetic_debug_expression:[0-9]*:[0-9]*:'),
'');
return TestCompilationResult(jsExpression, false);
}
return TestCompilationResult(jsExpression, true);
}
LibraryMetadata metadataForLibraryUri(Uri libraryUri) =>
metadata!.libraries.entries
.firstWhere((entry) => entry.value.fileUri == '$libraryUri')
.value;
}
class TestDriver {
class ExpressionEvaluationTestDriver {
final browser.Chrome chrome;
final Directory chromeDir;
final wip.WipConnection connection;
final wip.WipDebugger debugger;
late TestCompiler compiler;
late TestExpressionCompiler compiler;
late Uri htmlBootstrapper;
late Uri input;
late Uri output;
@ -196,10 +34,11 @@ class TestDriver {
late Directory testDir;
late String dartSdkPath;
TestDriver._(this.chrome, this.chromeDir, this.connection, this.debugger);
ExpressionEvaluationTestDriver._(
this.chrome, this.chromeDir, this.connection, this.debugger);
/// Initializes a Chrome browser instance, tab connection, and debugger.
static Future<TestDriver> init() async {
static Future<ExpressionEvaluationTestDriver> init() async {
// Create a temporary directory for holding Chrome tests.
var chromeDir = Directory.systemTemp.createTempSync('ddc_eval_test_anchor');
@ -243,7 +82,8 @@ class TestDriver {
var debugger = connection.debugger;
await debugger.enable().timeout(Duration(seconds: 5),
onTimeout: (() => throw Exception('Unable to enable WIP debugger')));
return TestDriver._(chrome, chromeDir, connection, debugger);
return ExpressionEvaluationTestDriver._(
chrome, chromeDir, connection, debugger);
}
/// Must be called when testing a new Dart program.
@ -288,7 +128,7 @@ class TestDriver {
''');
// Initialize DDC and the incremental compiler, then perform a full compile.
compiler = await TestCompiler.init(setup,
compiler = await TestExpressionCompiler.init(setup,
input: input,
output: output,
packages: packagesFile,
@ -660,14 +500,10 @@ class TestDriver {
}) async {
var frame = event.getCallFrames().first;
var loadModule = setup.moduleFormat == ModuleFormat.amd
? 'require'
: 'dart_library.import';
var jsExpression = '''
(function () {
try {
var sdk = $loadModule('dart_sdk');
var sdk = ${setup.loadModule}('dart_sdk');
var dart = sdk.dart;
var interceptors = sdk._interceptors;
return $expression;
@ -685,23 +521,28 @@ class TestDriver {
);
}
Future<TestCompilationResult> _compileDartExpression(
wip.WipCallFrame frame, String expression, int dartLine) async {
// Retrieve the call frame and its scope variables.
var scope = await _collectScopeVariables(frame);
// Perform an incremental compile.
return await compiler.compileExpression(
input: input,
line: dartLine,
column: 1,
scope: scope,
expression: expression);
}
Future<wip.RemoteObject> _evaluateDartExpression(
wip.DebuggerPausedEvent event,
String expression,
int dartLine, {
bool returnByValue = false,
}) async {
// Retrieve the call frame and its scope variables.
var frame = event.getCallFrames().first;
var scope = await _collectScopeVariables(frame);
// Perform an incremental compile.
var result = await compiler.compileExpression(
input: input,
line: dartLine,
column: 1,
scope: scope,
expression: expression);
var result = await _compileDartExpression(frame, expression, dartLine);
if (!result.isSuccess) {
setup.diagnosticMessages.clear();

View file

@ -0,0 +1,121 @@
// Copyright (c) 2023, 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.
import 'dart:io' show Directory, File;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'test_compiler.dart';
class ExpressionCompilerTestDriver {
final SetupCompilerOptions setup;
late Directory testDir;
String source;
late Uri input;
late Uri output;
late Uri packages;
late int line;
ExpressionCompilerTestDriver(this.setup, this.source) {
source = '${setup.dartLangComment}\n\n$source';
line = _getEvaluationLine(source);
var systemTempDir = Directory.systemTemp;
testDir = systemTempDir.createTempSync('foo bar');
output = testDir.uri.resolve('test.js');
input = testDir.uri.resolve('test.dart');
File.fromUri(input)
..createSync()
..writeAsStringSync(source);
packages = testDir.uri.resolve('package_config.json');
File.fromUri(packages)
..createSync()
..writeAsStringSync('''
{
"configVersion": 2,
"packages": [
{
"name": "foo",
"rootUri": "./",
"packageUri": "./"
}
]
}
''');
}
void delete() {
testDir.deleteSync(recursive: true);
}
Future<TestExpressionCompiler> createCompiler() =>
TestExpressionCompiler.init(setup,
input: input, output: output, packages: packages);
Future<TestCompilationResult> compile({
required TestExpressionCompiler compiler,
required Map<String, String> scope,
required String expression,
}) async {
return compiler.compileExpression(
input: input,
line: line,
column: 1,
scope: scope,
expression: expression);
}
void checkResult(
TestCompilationResult result, {
String? expectedError,
dynamic expectedResult,
}) {
var success = expectedError == null;
var message = success ? expectedResult! : expectedError;
expect(
result,
const TypeMatcher<TestCompilationResult>()
.having((r) => r.result!, 'result', _matches(message))
.having((r) => r.isSuccess, 'isSuccess', success));
}
Future<void> check({
TestExpressionCompiler? compiler,
required Map<String, String> scope,
required String expression,
String? expectedError,
dynamic expectedResult,
}) async {
compiler ??= await createCompiler();
var result =
await compile(compiler: compiler, scope: scope, expression: expression);
checkResult(result,
expectedError: expectedError, expectedResult: expectedResult);
}
Matcher _matches(dynamic matcher) {
if (matcher is Matcher) return matcher;
if (matcher is! String) throw StateError('Unsupported matcher $matcher');
var unIndented = RegExp.escape(matcher).replaceAll(RegExp('[ ]+'), '[ ]*');
return matches(RegExp(unIndented, multiLine: true));
}
static int _getEvaluationLine(String source) {
var placeholderRegExp = RegExp(r'// Breakpoint');
var lines = source.split('\n');
for (var line = 0; line < lines.length; line++) {
var content = lines[line];
if (placeholderRegExp.firstMatch(content) != null) {
return line + 1;
}
}
return -1;
}
}

View file

@ -2,284 +2,21 @@
// 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.
import 'dart:io' show Directory, File;
import 'package:dev_compiler/src/compiler/js_names.dart';
import 'package:dev_compiler/src/compiler/module_builder.dart';
import 'package:dev_compiler/src/compiler/shared_command.dart'
show SharedCompilerOptions;
import 'package:dev_compiler/src/js_ast/js_ast.dart';
import 'package:dev_compiler/src/kernel/compiler.dart' show ProgramCompiler;
import 'package:dev_compiler/src/kernel/expression_compiler.dart'
show ExpressionCompiler;
import 'package:kernel/ast.dart' show Component, Library;
import 'package:test/test.dart';
import 'package:vm/transformations/type_flow/utils.dart';
import '../shared_test_options.dart';
import 'expression_compiler_suite.dart';
// TODO(annagrin): Replace javascript matching in tests below with evaluating
// the javascript and checking the result.
// See https://github.com/dart-lang/sdk/issues/41959
/// Convenience class describing JavaScript module
/// to ensure we have normalized module names
class Module {
/// variable name used in JavaScript output to load the module
/// example: file
final String name;
/// JavaScript module name used in trackLibraries
/// example: packages/package/file.dart
final String path;
/// URI where the contents of the library that produces this module
/// can be found
/// example: /Users/../package/file.dart
final Uri fileUri;
/// Import URI for the library that generates this module.
/// example: packages:package/file.dart
final Uri importUri;
Module(this.importUri, this.fileUri)
: name = libraryUriToJsIdentifier(importUri),
path = importUri.isScheme('package')
? 'packages/${importUri.path}'
: importUri.path;
String get package => importUri.toString();
String get file => fileUri.path;
@override
String toString() =>
'Name: \$name, File: \$file, Package: \$package, path: \$path';
}
class TestCompilationResult {
final String result;
final bool isSuccess;
TestCompilationResult(this.result, this.isSuccess);
}
class TestCompiler {
final SetupCompilerOptions setup;
final Map<Uri, Module> _modules = {};
late final ExpressionCompiler compiler;
TestCompiler._(this.setup);
static Future<TestCompiler> create(
SetupCompilerOptions setup, Uri input, Uri packages) async {
final testCompiler = TestCompiler._(setup);
await testCompiler._createCompiler(input, packages);
return testCompiler;
}
Future<TestCompilationResult> compile(
{required Uri input,
required int line,
required int column,
required Map<String, String> scope,
required String expression}) async {
// clear previous errors
setup.errors.clear();
// compile
var jsExpression = await compiler.compileExpressionToJs(
_libraryUriFor(input), line, column, scope, expression);
if (setup.errors.isNotEmpty) {
jsExpression = setup.errors.toString().replaceAll(
RegExp(
r'org-dartlang-debug:synthetic_debug_expression:[0-9]*:[0-9]*:'),
'');
return TestCompilationResult(jsExpression, false);
}
return TestCompilationResult(jsExpression!, true);
}
Future<void> _createCompiler(Uri input, Uri packages) async {
// initialize incremental compiler and create component
setup.options.packagesFileUri = packages;
var frontend = DevelopmentIncrementalCompiler(setup.options, input);
var compilerResult = await frontend.computeDelta();
var component = compilerResult.component;
component.computeCanonicalNames();
// initialize ddc
var moduleName = 'foo.dart';
var classHierarchy = compilerResult.classHierarchy;
var compilerOptions = SharedCompilerOptions(
replCompile: true,
moduleName: moduleName,
soundNullSafety: setup.soundNullSafety,
moduleFormats: [setup.moduleFormat],
canaryFeatures: false,
);
var coreTypes = compilerResult.coreTypes;
final importToSummary = Map<Library, Component>.identity();
final summaryToModule = Map<Component, String>.identity();
for (var lib in component.libraries) {
importToSummary[lib] = component;
}
summaryToModule[component] = moduleName;
var kernel2jsCompiler = ProgramCompiler(component, classHierarchy!,
compilerOptions, importToSummary, summaryToModule,
coreTypes: coreTypes);
var moduleTree = kernel2jsCompiler.emitModule(component);
{
var opts = JavaScriptPrintingOptions(
allowKeywordsInProperties: true, allowSingleLineIfStatements: true);
var printer = SimpleJavaScriptPrintingContext();
var tree = transformModuleFormat(setup.moduleFormat, moduleTree);
tree.accept(Printer(opts, printer, localNamer: TemporaryNamer(tree)));
var printed = printer.getText();
debugPrint(printed);
}
_collectModules(component);
// create expression compiler
compiler = ExpressionCompiler(
setup.options,
setup.moduleFormat,
setup.errors,
frontend,
kernel2jsCompiler,
component,
);
}
void _collectModules(Component component) {
for (var library in component.libraries) {
_modules[library.fileUri] = Module(library.importUri, library.fileUri);
}
}
String _libraryUriFor(Uri input) => _modules[input]!.package;
}
class TestDriver {
final SetupCompilerOptions options;
late Directory tempDir;
final String source;
late Uri input;
late Uri packages;
late File file;
int line;
TestDriver(this.options, this.source) : line = _getEvaluationLine(source) {
var systemTempDir = Directory.systemTemp;
tempDir = systemTempDir.createTempSync('foo bar');
input = tempDir.uri.resolve('foo.dart');
file = File.fromUri(input)..createSync();
file.writeAsStringSync(source);
packages = tempDir.uri.resolve('package_config.json');
file = File.fromUri(packages)..createSync();
file.writeAsStringSync('''
{
"configVersion": 2,
"packages": [
{
"name": "foo",
"rootUri": "./",
"packageUri": "./"
}
]
}
''');
}
void delete() {
tempDir.delete(recursive: true);
}
Future<TestCompiler> createCompiler() =>
TestCompiler.create(options, input, packages);
Future<TestCompilationResult> compile({
required TestCompiler compiler,
required Map<String, String> scope,
required String expression,
}) async {
return compiler.compile(
input: input,
line: line,
column: 1,
scope: scope,
expression: expression);
}
void checkResult(
TestCompilationResult result, {
String? expectedError,
String? expectedResult,
}) {
var success = expectedError == null;
var message = success ? expectedResult! : expectedError;
expect(
result,
const TypeMatcher<TestCompilationResult>()
.having((r) => _normalize(r.result), 'result', _matches(message))
.having((r) => r.isSuccess, 'isSuccess', success));
}
Future<void> check({
TestCompiler? compiler,
required Map<String, String> scope,
required String expression,
String? expectedError,
String? expectedResult,
}) async {
compiler ??= await createCompiler();
var result =
await compile(compiler: compiler, scope: scope, expression: expression);
checkResult(result,
expectedError: expectedError, expectedResult: expectedResult);
}
String _normalize(String text) {
return text
.replaceAll(RegExp('\'.*foo.dart\''), '\'foo.dart\'')
.replaceAll(RegExp('".*foo.dart"'), '\'foo.dart\'');
}
Matcher _matches(String text) {
var unIndented = RegExp.escape(text).replaceAll(RegExp('[ ]+'), '[ ]*');
return matches(RegExp(unIndented, multiLine: true));
}
static int _getEvaluationLine(String source) {
var placeholderRegExp = RegExp(r'/\* evaluation placeholder \*/');
var lines = source.split('\n');
for (var line = 0; line < lines.length; line++) {
var content = lines[line];
if (placeholderRegExp.firstMatch(content) != null) {
return line + 1;
}
}
return -1;
}
}
void main() {
void main(List<String> args) {
for (var moduleFormat in [ModuleFormat.amd, ModuleFormat.ddc]) {
group('Module format: $moduleFormat |', () {
group('Unsound null safety |', () {
runUnsoundTests(moduleFormat);
runTests(SetupCompilerOptions(
soundNullSafety: false,
moduleFormat: moduleFormat,
args: args,
));
});
});
}
@ -287,33 +24,31 @@ void main() {
for (var moduleFormat in [ModuleFormat.amd, ModuleFormat.ddc]) {
group('Module format: $moduleFormat |', () {
group('Sound null safety |', () {
runSoundTests(moduleFormat);
runTests(SetupCompilerOptions(
soundNullSafety: true,
moduleFormat: moduleFormat,
args: args,
));
});
});
}
}
void runUnsoundTests(ModuleFormat moduleFormat) {
var options = SetupCompilerOptions(
soundNullSafety: false,
moduleFormat: moduleFormat,
);
void runTests(SetupCompilerOptions setup) {
group('Expression compilations on the same expression compiler |', () {
var source = '''
${options.dartLangComment}
main() {
}
main() {
}
void foo() {
/* evaluation placeholder */
}
''';
void foo() {
// Breakpoint
}
''';
late TestDriver driver;
late ExpressionCompilerTestDriver driver;
setUp(() {
driver = TestDriver(options, source);
driver = ExpressionCompilerTestDriver(setup, source);
});
tearDown(() {
@ -326,24 +61,12 @@ void runUnsoundTests(ModuleFormat moduleFormat) {
compiler: compiler,
scope: <String, String>{},
expression: 'true',
expectedResult: '''
(function() {
return true;
}(
))
''');
expectedResult: contains('return true;'));
await driver.check(
compiler: compiler,
scope: <String, String>{},
expression: 'false',
expectedResult: '''
(function() {
return false;
}(
))
''');
expectedResult: contains('return false;'));
});
test('some successful expression compilations', () async {
@ -352,13 +75,7 @@ void runUnsoundTests(ModuleFormat moduleFormat) {
compiler: compiler,
scope: <String, String>{},
expression: 'true',
expectedResult: '''
(function() {
return true;
}(
))
''');
expectedResult: contains('return true;'));
await driver.check(
compiler: compiler,
scope: <String, String>{},
@ -369,13 +86,7 @@ void runUnsoundTests(ModuleFormat moduleFormat) {
compiler: compiler,
scope: <String, String>{},
expression: 'false',
expectedResult: '''
(function() {
return false;
}(
))
''');
expectedResult: contains('return false;'));
});
test('failing expression compilations', () async {
@ -397,26 +108,25 @@ void runUnsoundTests(ModuleFormat moduleFormat) {
group('Expression compiler import tests', () {
var source = '''
${options.dartLangComment}
import 'dart:io' show Directory;
import 'dart:io' as p;
import 'dart:convert' as p;
main() {
print(Directory.systemTemp);
print(p.Directory.systemTemp);
print(p.utf.decoder);
}
import 'dart:io' show Directory;
import 'dart:io' as p;
import 'dart:convert' as p;
void foo() {
/* evaluation placeholder */
}
''';
main() {
print(Directory.systemTemp);
print(p.Directory.systemTemp);
print(p.utf8.decoder);
}
late TestDriver driver;
void foo() {
// Breakpoint
}
''';
late ExpressionCompilerTestDriver driver;
setUp(() {
driver = TestDriver(options, source);
driver = ExpressionCompilerTestDriver(setup, source);
});
tearDown(() {
@ -427,30 +137,14 @@ void runUnsoundTests(ModuleFormat moduleFormat) {
await driver.check(
scope: <String, String>{},
expression: 'Directory.systemTemp',
expectedResult: '''
(function() {
const dart_sdk = ${options.loadModule}('dart_sdk');
const io = dart_sdk.io;
return io.Directory.systemTemp;
}(
))
''');
expectedResult: contains('return io.Directory.systemTemp;'));
});
test('expression referencing named import', () async {
await driver.check(
scope: <String, String>{},
expression: 'p.Directory.systemTemp',
expectedResult: '''
(function() {
const dart_sdk = ${options.loadModule}('dart_sdk');
const io = dart_sdk.io;
return io.Directory.systemTemp;
}(
))
''');
expectedResult: contains('return io.Directory.systemTemp;'));
});
test('expression referencing another library with the same named import',
@ -458,412 +152,7 @@ void runUnsoundTests(ModuleFormat moduleFormat) {
await driver.check(
scope: <String, String>{},
expression: 'p.utf8.decoder',
expectedResult: '''
(function() {
const dart_sdk = ${options.loadModule}('dart_sdk');
const convert = dart_sdk.convert;
return convert.utf8.decoder;
}(
))
''');
});
});
group('Expression compiler tests for interactions with module containers:',
() {
var source = '''
${options.dartLangComment}
class A {
const A();
}
class B {
const B();
}
void foo() {
const a = A();
var check = a is int;
/* evaluation placeholder */
return;
}
void main() => foo();
''';
late TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('evaluation that non-destructively appends to the type container',
() async {
await driver.check(
scope: <String, String>{'a': 'null', 'check': 'null'},
expression: 'a is String',
expectedResult: '''
(function(a, check) {
const dart_sdk = ${options.loadModule}('dart_sdk');
const core = dart_sdk.core;
const dart = dart_sdk.dart;
var T = {
StringL: () => (T.StringL = dart.constFn(dart.legacy(core.String)))()
};
return T.StringL().is(a);
}(
null,
null
))
''');
});
test('evaluation that reuses the type container', () async {
await driver.check(
scope: <String, String>{'a': 'null', 'check': 'null'},
expression: 'a is int',
expectedResult: '''
(function(a, check) {
const dart_sdk = ${options.loadModule}('dart_sdk');
const core = dart_sdk.core;
const dart = dart_sdk.dart;
var T = {
intL: () => (T.intL = dart.constFn(dart.legacy(core.int)))()
};
return T.intL().is(a);
}(
null,
null
))
''');
});
test('evaluation that non-destructively appends to the constant container',
() async {
await driver.check(
scope: <String, String>{'a': 'null', 'check': 'null'},
expression: 'const B()',
expectedResult: '''
(function(a, check) {
const dart_sdk = ${options.loadModule}('dart_sdk');
const dart = dart_sdk.dart;
const foo\$46dart = ${options.loadModule}('foo.dart');
const foo = foo\$46dart.foo;
const CT = Object.create(null);
dart.defineLazy(CT, {
get C0() {
return C[0] = dart.const(Object.setPrototypeOf({
}, foo.B.prototype));
}
}, false);
var C = [void 0];
return C[0] || CT.C0;
}(
null,
null
))
''');
});
test(
'evaluation that reuses the constant container and canonicalizes properly',
() async {
await driver.check(
scope: <String, String>{'a': 'null', 'check': 'null'},
expression: 'a == const A()',
expectedResult: '''
(function(a, check) {
const dart_sdk = ${options.loadModule}('dart_sdk');
const dart = dart_sdk.dart;
const foo\$46dart = ${options.loadModule}('foo.dart');
const foo = foo\$46dart.foo;
const CT = Object.create(null);
dart.defineLazy(CT, {
get C0() {
return C[0] = dart.const(Object.setPrototypeOf({
}, foo.A.prototype));
}
}, false);
var C = [void 0];
return dart.equals(a, C[0] || CT.C0);
}(
null,
null
))
''');
});
});
group('Expression compiler tests using extension symbols', () {
var source = '''
${options.dartLangComment}
void bar() {
/* evaluation placeholder */
}
void main() => bar();
''';
late TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('map access', () async {
await driver.check(
scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
expression:
'(Map<String, String> params) { return params["index"]; }({})',
expectedResult: '''
(function() {
const dart_sdk = ${options.loadModule}('dart_sdk');
const core = dart_sdk.core;
const _js_helper = dart_sdk._js_helper;
const dart = dart_sdk.dart;
const dartx = dart_sdk.dartx;
var T = {
StringL: () => (T.StringL = dart.constFn(dart.legacy(core.String)))(),
MapOfStringL\$StringL: () => (T.MapOfStringL\$StringL = dart.constFn(core.Map\$(T.StringL(), T.StringL())))(),
MapLOfStringL\$StringL: () => (T.MapLOfStringL\$StringL = dart.constFn(dart.legacy(T.MapOfStringL\$StringL())))(),
MapLOfStringL\$StringLToStringL: () => (T.MapLOfStringL\$StringLToStringL = dart.constFn(dart.fnType(T.StringL(), [T.MapLOfStringL\$StringL()])))(),
IdentityMapOfStringL\$StringL: () => (T.IdentityMapOfStringL\$StringL = dart.constFn(_js_helper.IdentityMap\$(T.StringL(), T.StringL())))()
};
var S = {\$_get: dartx._get};
return dart.fn(params => params[S.\$_get]("index"), T.MapLOfStringL\$StringLToStringL())(new (T.IdentityMapOfStringL\$StringL()).new());
}(
))
''');
});
});
}
void runSoundTests(ModuleFormat moduleFormat) {
var options = SetupCompilerOptions(
soundNullSafety: true,
moduleFormat: moduleFormat,
);
group('Expression compilations on the same expression compiler |', () {
var source = '''
${options.dartLangComment}
main() {
}
void foo() {
/* evaluation placeholder */
}
''';
late TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('successful expression compilations', () async {
var compiler = await driver.createCompiler();
await driver.check(
compiler: compiler,
scope: <String, String>{},
expression: 'true',
expectedResult: '''
(function() {
return true;
}(
))
''');
await driver.check(
compiler: compiler,
scope: <String, String>{},
expression: 'false',
expectedResult: '''
(function() {
return false;
}(
))
''');
});
test('some successful expression compilations', () async {
var compiler = await driver.createCompiler();
await driver.check(
compiler: compiler,
scope: <String, String>{},
expression: 'true',
expectedResult: '''
(function() {
return true;
}(
))
''');
await driver.check(
compiler: compiler,
scope: <String, String>{},
expression: 'blah',
expectedError: "Undefined name 'blah'",
);
await driver.check(
compiler: compiler,
scope: <String, String>{},
expression: 'false',
expectedResult: '''
(function() {
return false;
}(
))
''');
});
test('failing expression compilations', () async {
var compiler = await driver.createCompiler();
await driver.check(
compiler: compiler,
scope: <String, String>{},
expression: 'blah1',
expectedError: "Undefined name 'blah1'",
);
await driver.check(
compiler: compiler,
scope: <String, String>{},
expression: 'blah2',
expectedError: "Undefined name 'blah2'",
);
});
});
group('Expression compiler import tests', () {
var source = '''
${options.dartLangComment}
import 'dart:io' show Directory;
import 'dart:io' as p;
import 'dart:convert' as p;
main() {
print(Directory.systemTemp);
print(p.Directory.systemTemp);
print(p.utf.decoder);
}
void foo() {
/* evaluation placeholder */
}
''';
late TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('expression referencing unnamed import', () async {
await driver.check(
scope: <String, String>{},
expression: 'Directory.systemTemp',
expectedResult: '''
(function() {
const dart_sdk = ${options.loadModule}('dart_sdk');
const io = dart_sdk.io;
return io.Directory.systemTemp;
}(
))
''');
});
test('expression referencing named import', () async {
await driver.check(
scope: <String, String>{},
expression: 'p.Directory.systemTemp',
expectedResult: '''
(function() {
const dart_sdk = ${options.loadModule}('dart_sdk');
const io = dart_sdk.io;
return io.Directory.systemTemp;
}(
))
''');
});
test('expression referencing another library with the same named import',
() async {
await driver.check(
scope: <String, String>{},
expression: 'p.utf8.decoder',
expectedResult: '''
(function() {
const dart_sdk = ${options.loadModule}('dart_sdk');
const convert = dart_sdk.convert;
return convert.utf8.decoder;
}(
))
''');
});
});
group('Expression compiler expressions that import extension symbols', () {
var source = '''
${options.dartLangComment}
void bar() {
/* evaluation placeholder */
}
void main() => bar();
''';
late TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('map access', () async {
await driver.check(
scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
expression:
'(Map<String, String> params) { return params["index"]; }({})',
expectedResult: '''
(function() {
const dart_sdk = ${options.loadModule}('dart_sdk');
const core = dart_sdk.core;
const _js_helper = dart_sdk._js_helper;
const dart = dart_sdk.dart;
const dartx = dart_sdk.dartx;
var T = {
StringN: () => (T.StringN = dart.constFn(dart.nullable(core.String)))(),
MapOfString\$String: () => (T.MapOfString\$String = dart.constFn(core.Map\$(core.String, core.String)))(),
MapOfString\$StringToStringN: () => (T.MapOfString\$StringToStringN = dart.constFn(dart.fnType(T.StringN(), [T.MapOfString\$String()])))(),
IdentityMapOfString\$String: () => (T.IdentityMapOfString\$String = dart.constFn(_js_helper.IdentityMap\$(core.String, core.String)))()
};
var S = {\$_get: dartx._get};
return dart.fn(params => params[S.\$_get]("index"), T.MapOfString\$StringToStringN())(new (T.IdentityMapOfString\$String()).new());
}(
))
''');
expectedResult: contains('return convert.utf8.decoder;'));
});
});
}

View file

@ -5,8 +5,8 @@
import 'package:dev_compiler/dev_compiler.dart';
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_worker_shared.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
// Set to true to enable debug output

View file

@ -5,8 +5,8 @@
import 'package:dev_compiler/dev_compiler.dart';
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_worker_shared.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
// Set to true to enable debug output

View file

@ -22,7 +22,7 @@ import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart';
import 'package:test/test.dart';
import 'setup_compiler_options.dart';
import '../shared_test_options.dart';
void runTests(SetupCompilerOptions setup, {bool verbose = false}) {
group('expression compiler worker on startup', () {
@ -77,37 +77,20 @@ void runTests(SetupCompilerOptions setup, {bool verbose = false}) {
});
group('reading assets using standard file system - ', () {
runExpressionCompilationTests(StandardFileSystemTestDriver(
setup.soundNullSafety,
setup.moduleFormat,
setup.canaryFeatures,
setup.enableAsserts,
verbose,
));
runExpressionCompilationTests(StandardFileSystemTestDriver(setup, verbose));
});
group('reading assets using multiroot file system - ', () {
runExpressionCompilationTests(MultiRootFileSystemTestDriver(
setup.soundNullSafety,
setup.moduleFormat,
setup.canaryFeatures,
setup.enableAsserts,
verbose,
));
runExpressionCompilationTests(
MultiRootFileSystemTestDriver(setup, verbose));
});
group('reading assets using asset file system -', () {
runExpressionCompilationTests(AssetFileSystemTestDriver(
setup.soundNullSafety,
setup.moduleFormat,
setup.canaryFeatures,
setup.enableAsserts,
verbose,
));
runExpressionCompilationTests(AssetFileSystemTestDriver(setup, verbose));
});
}
void runExpressionCompilationTests(TestDriver driver) {
void runExpressionCompilationTests(ExpressionCompilerWorkerTestDriver driver) {
group('expression compiler worker', () {
setUpAll(() async {
await driver.setUpAll();
@ -963,13 +946,9 @@ class E {
}
}
abstract class TestDriver {
final bool soundNullSafety;
final ModuleFormat moduleFormat;
final bool canaryFeatures;
final bool enableAsserts;
final bool verbose;
abstract class ExpressionCompilerWorkerTestDriver {
SetupCompilerOptions setup;
bool verbose;
late FileSystem assetFileSystem;
late Directory tempDir;
@ -981,13 +960,7 @@ abstract class TestDriver {
ExpressionCompilerWorker? worker;
Future<void>? workerDone;
TestDriver(
this.soundNullSafety,
this.moduleFormat,
this.canaryFeatures,
this.enableAsserts,
this.verbose,
);
ExpressionCompilerWorkerTestDriver(this.setup, this.verbose);
/// Initialize file systems, inputs, and start servers if needed.
Future<void> start();
@ -996,7 +969,8 @@ abstract class TestDriver {
Future<void> setUpAll() async {
tempDir = Directory.systemTemp.createTempSync('foo bar');
config = TestProjectConfiguration(tempDir, soundNullSafety, moduleFormat)
config = TestProjectConfiguration(
tempDir, setup.soundNullSafety, setup.moduleFormat)
..initialize();
await start();
@ -1026,10 +1000,10 @@ abstract class TestDriver {
fileSystem: assetFileSystem,
requestStream: requestController.stream,
sendResponse: responseController.add,
soundNullSafety: soundNullSafety,
moduleFormat: moduleFormat,
canaryFeatures: canaryFeatures,
enableAsserts: enableAsserts,
soundNullSafety: setup.soundNullSafety,
moduleFormat: setup.moduleFormat,
canaryFeatures: setup.canaryFeatures,
enableAsserts: setup.enableAsserts,
verbose: verbose,
);
workerDone = worker?.run();
@ -1043,15 +1017,9 @@ abstract class TestDriver {
}
}
class StandardFileSystemTestDriver extends TestDriver {
StandardFileSystemTestDriver(
bool soundNullSafety,
ModuleFormat moduleFormat,
bool canaryFeatures,
bool enableAsserts,
bool verbose,
) : super(soundNullSafety, moduleFormat, canaryFeatures, enableAsserts,
verbose);
class StandardFileSystemTestDriver extends ExpressionCompilerWorkerTestDriver {
StandardFileSystemTestDriver(SetupCompilerOptions setup, bool verbose)
: super(setup, verbose);
@override
Future<void> start() async {
@ -1060,15 +1028,9 @@ class StandardFileSystemTestDriver extends TestDriver {
}
}
class MultiRootFileSystemTestDriver extends TestDriver {
MultiRootFileSystemTestDriver(
bool soundNullSafety,
ModuleFormat moduleFormat,
bool canaryFeatures,
bool enableAsserts,
bool verbose,
) : super(soundNullSafety, moduleFormat, canaryFeatures, enableAsserts,
verbose);
class MultiRootFileSystemTestDriver extends ExpressionCompilerWorkerTestDriver {
MultiRootFileSystemTestDriver(SetupCompilerOptions setup, bool verbose)
: super(setup, verbose);
@override
Future<void> start() async {
@ -1079,18 +1041,12 @@ class MultiRootFileSystemTestDriver extends TestDriver {
}
}
class AssetFileSystemTestDriver extends TestDriver {
class AssetFileSystemTestDriver extends ExpressionCompilerWorkerTestDriver {
late TestAssetServer server;
late int port;
AssetFileSystemTestDriver(
bool soundNullSafety,
ModuleFormat moduleFormat,
bool canaryFeatures,
bool enableAsserts,
bool verbose,
) : super(soundNullSafety, moduleFormat, canaryFeatures, enableAsserts,
verbose);
AssetFileSystemTestDriver(SetupCompilerOptions setup, bool verbose)
: super(setup, verbose);
@override
Future<void> start() async {

View file

@ -6,11 +6,11 @@ import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:test/test.dart';
import '../shared_test_options.dart';
import 'expression_compiler_e2e_suite.dart';
import 'setup_compiler_options.dart';
void main(List<String> args) async {
var driver = await TestDriver.init();
var driver = await ExpressionEvaluationTestDriver.init();
tearDownAll(() async {
await driver.finish();
@ -116,7 +116,8 @@ main() {
}
''';
void runSharedTests(SetupCompilerOptions setup, TestDriver driver) {
void runSharedTests(
SetupCompilerOptions setup, ExpressionEvaluationTestDriver driver) {
group('Runtime debugging API |', () {
var source = simpleClassSource;

View file

@ -1,111 +0,0 @@
// Copyright (c) 2023, 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.
import 'package:dev_compiler/src/compiler/module_builder.dart';
import 'package:dev_compiler/src/kernel/command.dart';
import 'package:dev_compiler/src/kernel/target.dart' show DevCompilerTarget;
import 'package:front_end/src/api_unstable/ddc.dart' as fe;
import 'package:front_end/src/compute_platform_binaries_location.dart' as fe;
import 'package:kernel/target/targets.dart';
import 'package:path/path.dart' as p;
class SetupCompilerOptions {
static final sdkRoot = fe.computePlatformBinariesLocation();
static final buildRoot =
fe.computePlatformBinariesLocation(forceBuildDir: true);
static final sdkUnsoundSummaryPath =
buildRoot.resolve('ddc_outline_unsound.dill').toFilePath();
static final sdkSoundSummaryPath =
buildRoot.resolve('ddc_outline.dill').toFilePath();
static final librariesSpecificationUri =
buildRoot.resolve('lib/libraries.json').toFilePath();
final bool legacyCode;
final List<String> errors = [];
final List<String> diagnosticMessages = [];
final ModuleFormat moduleFormat;
final fe.CompilerOptions options;
final bool soundNullSafety;
final bool canaryFeatures;
final bool enableAsserts;
static fe.CompilerOptions _getOptions(
{required bool enableAsserts, required bool soundNullSafety}) {
var options = fe.CompilerOptions()
..verbose = false // set to true for debugging
..sdkRoot = sdkRoot
..target =
DevCompilerTarget(TargetFlags(soundNullSafety: soundNullSafety))
..librariesSpecificationUri = p.toUri('sdk/lib/libraries.json')
..omitPlatform = true
..sdkSummary =
p.toUri(soundNullSafety ? sdkSoundSummaryPath : sdkUnsoundSummaryPath)
..environmentDefines =
addGeneratedVariables({}, enableAsserts: enableAsserts)
..nnbdMode = soundNullSafety ? fe.NnbdMode.Strong : fe.NnbdMode.Weak;
return options;
}
SetupCompilerOptions._({
this.enableAsserts = true,
this.soundNullSafety = true,
this.legacyCode = false,
this.moduleFormat = ModuleFormat.amd,
this.canaryFeatures = false,
}) : options = _getOptions(
soundNullSafety: soundNullSafety, enableAsserts: enableAsserts) {
options.onDiagnostic = (fe.DiagnosticMessage m) {
diagnosticMessages.addAll(m.plainTextFormatted);
if (m.severity == fe.Severity.error ||
m.severity == fe.Severity.internalProblem) {
errors.addAll(m.plainTextFormatted);
}
};
}
/// Creates current compiler setup options.
///
/// Reads options determined by the test configuration from the configuration
/// environment variable set by the test runner.
///
/// To run tests locally using test.py, pass a vm option defining required
/// configuration, for example:
///
/// `./tools/test.py -n web-dev-canary-unittest-asserts-mac`
///
/// To run a single test locally, pass the --canary or --enable-asserts flags
/// to the command line to enable corresponding features, for example:
///
/// `dart test/expression_compiler/assertions_enabled_test.dart --canary --enable-asserts`
factory SetupCompilerOptions({
bool soundNullSafety = true,
bool legacyCode = false,
ModuleFormat moduleFormat = ModuleFormat.amd,
List<String> args = const <String>[],
}) {
// Find if the test is run with arguments overriding the configuration
late bool enableAsserts;
late bool canaryFeatures;
// Read configuration settings from matrix.json
var configuration = String.fromEnvironment('test_runner.configuration');
if (configuration.isEmpty) {
// If not running from test runner, read options from the args
enableAsserts = args.contains('--enable-asserts');
canaryFeatures = args.contains('--canary');
} else {
// If running from the test runner, read options from the environment
// (set to configuration settings from matrix.json).
enableAsserts = configuration.contains('-asserts-');
canaryFeatures = configuration.contains('-canary-');
}
return SetupCompilerOptions._(
enableAsserts: enableAsserts,
soundNullSafety: soundNullSafety,
legacyCode: legacyCode,
moduleFormat: moduleFormat,
canaryFeatures: canaryFeatures,
);
}
}

View file

@ -0,0 +1,154 @@
// Copyright (c) 2023, 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.
import 'dart:async';
import 'dart:convert';
import 'dart:io' show File;
import 'package:dev_compiler/src/compiler/shared_command.dart'
show SharedCompilerOptions;
import 'package:dev_compiler/src/kernel/command.dart';
import 'package:dev_compiler/src/kernel/compiler.dart' show ProgramCompiler;
import 'package:dev_compiler/src/kernel/expression_compiler.dart'
show ExpressionCompiler;
import 'package:dev_compiler/src/kernel/module_metadata.dart';
import 'package:front_end/src/api_unstable/ddc.dart' as fe;
import 'package:kernel/ast.dart' show Component, Library;
import 'package:path/path.dart' as p;
import 'package:source_maps/source_maps.dart' as source_maps;
import '../shared_test_options.dart';
class TestCompilationResult {
final String? result;
final bool isSuccess;
TestCompilationResult(this.result, this.isSuccess);
}
class TestExpressionCompiler {
final SetupCompilerOptions setup;
final Component component;
final ExpressionCompiler compiler;
final ModuleMetadata? metadata;
final source_maps.SingleMapping sourceMap;
TestExpressionCompiler._(
this.setup, this.component, this.compiler, this.metadata, this.sourceMap);
static Future<TestExpressionCompiler> init(SetupCompilerOptions setup,
{required Uri input,
required Uri output,
Uri? packages,
Map<String, bool> experiments = const {}}) async {
setup.diagnosticMessages.clear();
setup.errors.clear();
// Initialize the incremental compiler and module component.
// TODO: extend this for multi-module compilations by storing separate
// compilers/components/names per module.
setup.options.packagesFileUri = packages;
setup.options.explicitExperimentalFlags.addAll(fe.parseExperimentalFlags(
experiments,
onError: (message) => throw Exception(message)));
var frontend = DevelopmentIncrementalCompiler(setup.options, input);
var compilerResult = await frontend.computeDelta();
var component = compilerResult.component;
component.computeCanonicalNames();
// Initialize DDC.
var moduleName = p.basenameWithoutExtension(output.toFilePath());
var classHierarchy = compilerResult.classHierarchy!;
var compilerOptions = SharedCompilerOptions(
replCompile: true,
moduleName: moduleName,
experiments: experiments,
soundNullSafety: setup.soundNullSafety,
emitDebugMetadata: true,
canaryFeatures: setup.canaryFeatures,
enableAsserts: setup.enableAsserts,
);
var coreTypes = compilerResult.coreTypes;
final importToSummary = Map<Library, Component>.identity();
final summaryToModule = Map<Component, String>.identity();
for (var lib in component.libraries) {
importToSummary[lib] = component;
}
summaryToModule[component] = moduleName;
var kernel2jsCompiler = ProgramCompiler(component, classHierarchy,
compilerOptions, importToSummary, summaryToModule,
coreTypes: coreTypes);
var module = kernel2jsCompiler.emitModule(component);
// Perform a full compile, writing the compiled JS + sourcemap.
var code = jsProgramToCode(
module,
setup.moduleFormat,
inlineSourceMap: compilerOptions.inlineSourceMap,
buildSourceMap: compilerOptions.sourceMap,
emitDebugMetadata: compilerOptions.emitDebugMetadata,
emitDebugSymbols: compilerOptions.emitDebugSymbols,
jsUrl: '$output',
mapUrl: '$output.map',
compiler: kernel2jsCompiler,
component: component,
);
var codeBytes = utf8.encode(code.code);
var sourceMapBytes = utf8.encode(json.encode(code.sourceMap));
File(output.toFilePath()).writeAsBytesSync(codeBytes);
File('${output.toFilePath()}.map').writeAsBytesSync(sourceMapBytes);
// Save the expression compiler for future compilation.
var compiler = ExpressionCompiler(
setup.options,
setup.moduleFormat,
setup.errors,
frontend,
kernel2jsCompiler,
component,
);
if (setup.errors.isNotEmpty) {
throw Exception('Compilation failed with: ${setup.errors}');
}
setup.diagnosticMessages.clear();
setup.errors.clear();
var sourceMap = source_maps.SingleMapping.fromJson(
code.sourceMap!.cast<String, dynamic>());
return TestExpressionCompiler._(
setup, component, compiler, code.metadata, sourceMap);
}
Future<TestCompilationResult> compileExpression(
{required Uri input,
required int line,
required int column,
required Map<String, String> scope,
required String expression}) async {
// clear previous errors
setup.errors.clear();
var libraryUri = metadataForLibraryUri(input);
var jsExpression = await compiler.compileExpressionToJs(
libraryUri.importUri, line, column, scope, expression);
if (setup.errors.isNotEmpty) {
jsExpression = setup.errors.toString().replaceAll(
RegExp(
r'org-dartlang-debug:synthetic_debug_expression:[0-9]*:[0-9]*:'),
'');
return TestCompilationResult(jsExpression, false);
}
return TestCompilationResult(jsExpression, true);
}
LibraryMetadata metadataForLibraryUri(Uri libraryUri) =>
metadata!.libraries.entries
.firstWhere((entry) => entry.value.fileUri == '$libraryUri')
.value;
}

View file

@ -5,97 +5,139 @@
import 'package:dev_compiler/src/compiler/module_builder.dart'
show ModuleFormat;
import 'package:dev_compiler/src/kernel/command.dart'
show addGeneratedVariables, getSdkPath;
show addGeneratedVariables;
import 'package:dev_compiler/src/kernel/target.dart' show DevCompilerTarget;
import 'package:front_end/src/api_unstable/ddc.dart';
import 'package:front_end/src/compute_platform_binaries_location.dart';
import 'package:front_end/src/fasta/incremental_serializer.dart';
import 'package:front_end/src/api_unstable/ddc.dart' as fe;
import 'package:front_end/src/compute_platform_binaries_location.dart' as fe;
import 'package:front_end/src/fasta/incremental_serializer.dart' as fe;
import 'package:kernel/ast.dart' show Component;
import 'package:kernel/target/targets.dart';
import 'package:path/path.dart' as p;
import 'package:kernel/target/targets.dart' show TargetFlags;
class DevelopmentIncrementalCompiler extends IncrementalCompiler {
class DevelopmentIncrementalCompiler extends fe.IncrementalCompiler {
Uri entryPoint;
DevelopmentIncrementalCompiler(CompilerOptions options, this.entryPoint,
DevelopmentIncrementalCompiler(fe.CompilerOptions options, this.entryPoint,
[Uri? initializeFrom,
bool? outlineOnly,
IncrementalSerializer? incrementalSerializer])
fe.IncrementalSerializer? incrementalSerializer])
: super(
CompilerContext(
ProcessedOptions(options: options, inputs: [entryPoint])),
fe.CompilerContext(
fe.ProcessedOptions(options: options, inputs: [entryPoint])),
initializeFrom,
outlineOnly,
incrementalSerializer);
DevelopmentIncrementalCompiler.fromComponent(CompilerOptions options,
DevelopmentIncrementalCompiler.fromComponent(fe.CompilerOptions options,
this.entryPoint, Component componentToInitializeFrom,
[bool? outlineOnly, IncrementalSerializer? incrementalSerializer])
[bool? outlineOnly, fe.IncrementalSerializer? incrementalSerializer])
: super.fromComponent(
CompilerContext(
ProcessedOptions(options: options, inputs: [entryPoint])),
fe.CompilerContext(
fe.ProcessedOptions(options: options, inputs: [entryPoint])),
componentToInitializeFrom,
outlineOnly,
incrementalSerializer);
}
class SetupCompilerOptions {
static final sdkRoot = computePlatformBinariesLocation();
// Unsound .dill files are not longer in the released SDK so this file must be
// read from the build output directory.
static final sdkUnsoundSummaryPath =
computePlatformBinariesLocation(forceBuildDir: true)
.resolve('ddc_outline_unsound.dill');
// Use the outline copied to the released SDK.
static final sdkSoundSummaryPath = sdkRoot.resolve('ddc_outline.dill');
static final librariesSpecificationUri =
p.join(p.dirname(p.dirname(getSdkPath())), 'libraries.json');
static final sdkRoot = fe.computePlatformBinariesLocation();
static final buildRoot =
fe.computePlatformBinariesLocation(forceBuildDir: true);
static final _sdkUnsoundSummaryPath =
buildRoot.resolve('ddc_outline_unsound.dill');
static final _sdkSoundSummaryPath = buildRoot.resolve('ddc_outline.dill');
static CompilerOptions getOptions(bool soundNullSafety) {
var options = CompilerOptions()
static final _dartUnsoundComment = '// @dart = 2.9';
static final _dartSoundComment = '//';
String get dartLangComment =>
soundNullSafety ? _dartSoundComment : _dartUnsoundComment;
final bool legacyCode;
final List<String> errors = [];
final List<String> diagnosticMessages = [];
final ModuleFormat moduleFormat;
final fe.CompilerOptions options;
final bool soundNullSafety;
final bool canaryFeatures;
final bool enableAsserts;
static fe.CompilerOptions _getOptions(
{required bool enableAsserts, required bool soundNullSafety}) {
var options = fe.CompilerOptions()
..verbose = false // set to true for debugging
..sdkRoot = sdkRoot
..target =
DevCompilerTarget(TargetFlags(soundNullSafety: soundNullSafety))
..librariesSpecificationUri = Uri.base.resolve('sdk/lib/libraries.json')
..omitPlatform = true
..sdkSummary =
soundNullSafety ? sdkSoundSummaryPath : sdkUnsoundSummaryPath
..environmentDefines = addGeneratedVariables({}, enableAsserts: true)
..nnbdMode = soundNullSafety ? NnbdMode.Strong : NnbdMode.Weak;
soundNullSafety ? _sdkSoundSummaryPath : _sdkUnsoundSummaryPath
..environmentDefines =
addGeneratedVariables({}, enableAsserts: enableAsserts)
..nnbdMode = soundNullSafety ? fe.NnbdMode.Strong : fe.NnbdMode.Weak;
return options;
}
static final String dartUnsoundComment = '// @dart = 2.9';
static final String dartSoundComment = '//';
final List<String> errors = [];
final CompilerOptions options;
final String dartLangComment;
final ModuleFormat moduleFormat;
final bool soundNullSafety;
final bool canaryFeatures;
SetupCompilerOptions({
SetupCompilerOptions._({
this.enableAsserts = true,
this.soundNullSafety = true,
this.legacyCode = false,
this.moduleFormat = ModuleFormat.amd,
this.canaryFeatures = false,
}) : options = getOptions(soundNullSafety),
dartLangComment =
soundNullSafety ? dartSoundComment : dartUnsoundComment {
options.onDiagnostic = (DiagnosticMessage m) {
errors.addAll(m.plainTextFormatted);
}) : options = _getOptions(
soundNullSafety: soundNullSafety, enableAsserts: enableAsserts) {
options.onDiagnostic = (fe.DiagnosticMessage m) {
diagnosticMessages.addAll(m.plainTextFormatted);
if (m.severity == fe.Severity.error ||
m.severity == fe.Severity.internalProblem) {
errors.addAll(m.plainTextFormatted);
}
};
}
String get loadModule {
switch (moduleFormat) {
case ModuleFormat.amd:
return 'require';
case ModuleFormat.ddc:
return 'dart_library.import';
default:
throw UnsupportedError('Module format: $moduleFormat');
/// Creates current compiler setup options.
///
/// Reads options determined by the test configuration from the configuration
/// environment variable set by the test runner.
///
/// To run tests locally using test.py, pass a vm option defining required
/// configuration, for example:
///
/// `./tools/test.py -n web-dev-canary-unittest-asserts-mac`
///
/// To run a single test locally, pass the --canary or --enable-asserts flags
/// to the command line to enable corresponding features, for example:
///
/// `dart test/expression_compiler/assertions_enabled_test.dart --canary --enable-asserts`
factory SetupCompilerOptions({
bool soundNullSafety = true,
bool legacyCode = false,
ModuleFormat moduleFormat = ModuleFormat.amd,
List<String> args = const <String>[],
}) {
// Find if the test is run with arguments overriding the configuration
late bool enableAsserts;
late bool canaryFeatures;
// Read configuration settings from matrix.json
var configuration = String.fromEnvironment('test_runner.configuration');
if (configuration.isEmpty) {
// If not running from test runner, read options from the args
enableAsserts = args.contains('--enable-asserts');
canaryFeatures = args.contains('--canary');
} else {
// If running from the test runner, read options from the environment
// (set to configuration settings from matrix.json).
enableAsserts = configuration.contains('-asserts-');
canaryFeatures = configuration.contains('-canary-');
}
return SetupCompilerOptions._(
enableAsserts: enableAsserts,
soundNullSafety: soundNullSafety,
legacyCode: legacyCode,
moduleFormat: moduleFormat,
canaryFeatures: canaryFeatures,
);
}
String get loadModule =>
moduleFormat == ModuleFormat.amd ? 'require' : 'dart_library.import';
}