6366fddf12
Migrate the test file that is unblocked. Issue: https://github.com/dart-lang/sdk/issues/46617 Change-Id: I65598f5837115d7dce1c51389bb7c762c1ce451e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/248884 Reviewed-by: Anna Gringauze <annagrin@google.com>
640 lines
20 KiB
Dart
640 lines
20 KiB
Dart
// Copyright (c) 2020, 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: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';
|
|
|
|
// 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;
|
|
|
|
TestCompiler(this.setup);
|
|
|
|
Future<TestCompilationResult> compile(
|
|
{required Uri input,
|
|
required Uri packages,
|
|
required int line,
|
|
required int column,
|
|
required Map<String, String> scope,
|
|
required String expression}) async {
|
|
// initialize incremental compiler and create component
|
|
setup.options.packagesFileUri = packages;
|
|
var compiler = DevelopmentIncrementalCompiler(setup.options, input);
|
|
var compilerResult = await compiler.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]);
|
|
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);
|
|
}
|
|
|
|
// create expression compiler
|
|
var evaluator = ExpressionCompiler(
|
|
setup.options,
|
|
setup.moduleFormat,
|
|
setup.errors,
|
|
compiler,
|
|
kernel2jsCompiler,
|
|
component,
|
|
);
|
|
|
|
// collect all module names and paths
|
|
var moduleInfo = _collectModules(component);
|
|
var module = moduleInfo[input]!;
|
|
|
|
setup.errors.clear();
|
|
|
|
// compile
|
|
var jsExpression = await evaluator.compileExpressionToJs(
|
|
module.package, 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);
|
|
}
|
|
|
|
Map<Uri, Module> _collectModules(Component component) {
|
|
var modules = <Uri, Module>{};
|
|
for (var library in component.libraries) {
|
|
modules[library.fileUri] = Module(library.importUri, library.fileUri);
|
|
}
|
|
|
|
return modules;
|
|
}
|
|
}
|
|
|
|
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<void> check(
|
|
{required Map<String, String> scope,
|
|
required String expression,
|
|
String? expectedError,
|
|
String? expectedResult}) async {
|
|
var result = await TestCompiler(options).compile(
|
|
input: input,
|
|
packages: packages,
|
|
line: line,
|
|
column: 1,
|
|
scope: scope,
|
|
expression: expression);
|
|
|
|
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));
|
|
}
|
|
|
|
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() {
|
|
for (var moduleFormat in [ModuleFormat.amd, ModuleFormat.ddc]) {
|
|
group('Module format: $moduleFormat', () {
|
|
group('Unsound null safety:', () {
|
|
var options = SetupCompilerOptions(
|
|
soundNullSafety: false, 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);
|
|
}
|
|
|
|
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 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({
|
|
__proto__: 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({
|
|
__proto__: 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());
|
|
}(
|
|
|
|
))
|
|
''');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
for (var moduleFormat in [ModuleFormat.amd, ModuleFormat.ddc]) {
|
|
group('Module format: $moduleFormat', () {
|
|
group('Sound null safety:', () {
|
|
var options = SetupCompilerOptions(soundNullSafety: true);
|
|
|
|
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());
|
|
}(
|
|
|
|
))
|
|
''');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|