blob: 5636b5f119f315d07204c70eb031743c659b04bb [file] [log] [blame]
// 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.
// @dart = 2.9
import 'dart:io' show Directory, File;
import 'package:cli_util/cli_util.dart';
import 'package:dev_compiler/dev_compiler.dart';
import 'package:front_end/src/api_unstable/ddc.dart';
import 'package:front_end/src/api_prototype/compiler_options.dart'
show CompilerOptions;
import 'package:front_end/src/compute_platform_binaries_location.dart';
import 'package:front_end/src/fasta/incremental_serializer.dart';
import 'package:kernel/ast.dart' show Component;
import 'package:kernel/target/targets.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.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
class DevelopmentIncrementalCompiler extends IncrementalCompiler {
Uri entryPoint;
DevelopmentIncrementalCompiler(CompilerOptions options, this.entryPoint,
[Uri initializeFrom,
bool outlineOnly,
IncrementalSerializer incrementalSerializer])
: super(
CompilerContext(
ProcessedOptions(options: options, inputs: [entryPoint])),
initializeFrom,
outlineOnly,
incrementalSerializer);
DevelopmentIncrementalCompiler.fromComponent(CompilerOptions options,
this.entryPoint, Component componentToInitializeFrom,
[bool outlineOnly, IncrementalSerializer incrementalSerializer])
: super.fromComponent(
CompilerContext(
ProcessedOptions(options: options, inputs: [entryPoint])),
componentToInitializeFrom,
outlineOnly,
incrementalSerializer);
}
class SetupCompilerOptions {
static final sdkRoot = computePlatformBinariesLocation();
static final sdkSummaryPath = p.join(sdkRoot.path, 'ddc_sdk.dill');
static final librariesSpecificationUri =
p.join(p.dirname(p.dirname(getSdkPath())), 'libraries.json');
static CompilerOptions getOptions() {
var options = CompilerOptions()
..verbose = false // set to true for debugging
..sdkRoot = sdkRoot
..target = DevCompilerTarget(TargetFlags())
..librariesSpecificationUri = Uri.base.resolve('sdk/lib/libraries.json')
..omitPlatform = true
..sdkSummary = sdkRoot.resolve(sdkSummaryPath)
..environmentDefines = const {};
return options;
}
List<String> errors;
final CompilerOptions options;
SetupCompilerOptions() : options = getOptions() {
errors = <String>[];
options.onDiagnostic = (DiagnosticMessage m) {
errors.addAll(m.plainTextFormatted);
};
}
}
/// 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 = importUri.pathSegments.last.replaceAll('.dart', ''),
path = importUri.scheme == '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(
{Uri input,
int line,
int column,
Map<String, String> scope,
String expression}) async {
// initialize incremental compiler and create component
var compiler = DevelopmentIncrementalCompiler(setup.options, input);
var component = await compiler.computeDelta();
// initialize ddc
var classHierarchy = compiler.getClassHierarchy();
var compilerOptions = SharedCompilerOptions(replCompile: true);
var coreTypes = compiler.getCoreTypes();
var kernel2jsCompiler = ProgramCompiler(
component, classHierarchy, compilerOptions, const {}, const {},
coreTypes: coreTypes);
kernel2jsCompiler.emitModule(component);
// create expression compiler
var evaluator = ExpressionCompiler(
setup.options,
setup.errors,
compiler,
kernel2jsCompiler,
component,
);
// collect all module names and paths
var moduleInfo = _collectModules(component);
var modules =
moduleInfo.map((k, v) => MapEntry<String, String>(v.name, v.path));
modules['dart'] = 'dart_sdk';
modules['core'] = 'dart_sdk';
var module = moduleInfo[input];
setup.errors.clear();
// compile
var jsExpression = await evaluator.compileExpressionToJs(
module.package, line, column, modules, scope, module.name, 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;
Directory tempDir;
final String source;
Uri input;
File file;
int line;
TestDriver(this.options, this.source) {
var systemTempDir = Directory.systemTemp;
tempDir = systemTempDir.createTempSync('foo bar');
line = _getEvaluationLine(source);
input = tempDir.uri.resolve('foo.dart');
file = File.fromUri(input)..createSync();
file.writeAsStringSync(source);
}
void delete() {
tempDir.delete(recursive: true);
}
void check(
{Map<String, String> scope,
String expression,
String expectedError,
String expectedResult}) async {
var result = await TestCompiler(options).compile(
input: input,
line: line,
column: 1,
scope: scope,
expression: expression);
if (expectedError != null) {
expect(result.isSuccess, isFalse);
expect(_normalize(result.result), matches(expectedError));
} else if (expectedResult != null) {
expect(result.isSuccess, isTrue);
expect(_normalize(result.result), _matches(expectedResult));
}
}
String _normalize(String text) {
return text.replaceAll(RegExp('\'.*foo.dart\''), '\'foo.dart\'');
}
Matcher _matches(String text) {
var indent = text.indexOf(RegExp('[^ ]'));
var unindented =
text.split('\n').map((line) => line.substring(indent)).join('\n');
return matches(RegExp(RegExp.escape(unindented), multiLine: true));
}
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() {
var options = SetupCompilerOptions();
group('Expression compiler tests in extension method:', () {
const source = '''
// @dart = 2.9
extension NumberParsing on String {
int parseInt() {
var ret = int.parse(this);
/* evaluation placeholder */
return ret;
}
}
main() => 0;
''';
TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('compilation error', () async {
await driver.check(
scope: <String, String>{'ret': '1234'},
expression: 'typo',
expectedError: "Error: Getter not found: 'typo'");
});
test('local', () async {
await driver.check(
scope: <String, String>{'ret': '1234'},
expression: 'ret',
expectedResult: '''
(function(ret) {
return ret;
}(
1234
))
''');
});
test('this', () async {
await driver.check(
scope: <String, String>{'ret': '1234'},
expression: 'this',
expectedError: "Expected identifier, but got 'this'.");
});
});
group('Expression compiler tests in method:', () {
const source = '''
// @dart = 2.9
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
}
int global = 42;
class C {
C(int this.field, int this._field);
static int staticField = 0;
static int _staticField = 1;
int _field;
int field;
int methodFieldAccess(int x) {
/* evaluation placeholder */
return x + _field + _staticField;
}
Future<int> asyncMethod(int x) async {
return x;
}
}
main() => 0;
''';
TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('compilation error', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'typo',
expectedError: "The getter 'typo' isn't defined for the class 'C'");
});
test('local', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x',
expectedResult: '''
(function(x) {
return x;
}.bind(this)(
1
))
''');
});
test('this', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'this',
expectedResult: '''
(function(x) {
return this;
}.bind(this)(
1
))
''');
});
test('expression using locals', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + 1',
expectedResult: '''
(function(x) {
return dart.notNull(x) + 1;
}.bind(this)(
1
))
''');
});
test('expression using static fields', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + staticField',
expectedResult: '''
(function(x) {
return dart.notNull(x) + dart.notNull(foo.C.staticField);
}.bind(this)(
1
))
''');
});
test('expression using private static fields', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _staticField',
expectedResult: '''
(function(x) {
let foo = require('foo.dart').foo;
let _staticField = dart.privateName(foo, "_staticField");
return dart.notNull(x) + dart.notNull(foo.C._staticField);
}.bind(this)(
1
))
''');
});
test('expression using fields', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + field',
expectedResult: '''
(function(x) {
return dart.notNull(x) + dart.notNull(this.field);
}.bind(this)(
1
))
''');
});
test('expression using private fields', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _field',
expectedResult: '''
(function(x) {
let foo = require('foo.dart').foo;
let _field = dart.privateName(foo, "_field");
return dart.notNull(x) + dart.notNull(this[_field]);
}.bind(this)(
1
))
''');
});
test('expression using globals', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + global',
expectedResult: '''
(function(x) {
return dart.notNull(x) + dart.notNull(foo.global);
}.bind(this)(
1
))
''');
});
test('method call', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'methodFieldAccess(2)',
expectedResult: '''
(function(x) {
return this.methodFieldAccess(2);
}.bind(this)(
1
))
''');
});
test('async method call', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'asyncMethod(2)',
expectedResult: '''
(function(x) {
return this.asyncMethod(2);
}.bind(this)(
1
))
''');
});
test('extension method call', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: '"1234".parseInt()',
expectedResult: '''
(function(x) {
return foo['NumberParsing|parseInt']("1234");
}.bind(this)(
1
))
''');
});
test('private field modification', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_field = 2',
expectedResult: '''
(function(x) {
let foo = require('foo.dart').foo;
let _field = dart.privateName(foo, "_field");
return this[_field] = 2;
}.bind(this)(
1
))
''');
});
test('field modification', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'field = 2',
expectedResult: '''
(function(x) {
return this.field = 2;
}.bind(this)(
1
))
''');
});
test('private static field modification', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_staticField = 2',
expectedResult: '''
(function(x) {
let foo = require('foo.dart').foo;
let _staticField = dart.privateName(foo, "_staticField");
return foo.C._staticField = 2;
}.bind(this)(
1
))
''');
});
test('static field modification', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'staticField = 2',
expectedResult: '''
(function(x) {
return foo.C.staticField = 2;
}.bind(this)(
1
))
''');
});
});
group('Expression compiler tests in method with no field access:', () {
const source = '''
// @dart = 2.9
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
}
int global = 42;
class C {
C(int this.field, int this._field);
static int staticField = 0;
static int _staticField = 1;
int _field;
int field;
int methodNoFieldAccess(int x) {
/* evaluation placeholder */
return x;
}
Future<int> asyncMethod(int x) async {
return x;
}
}
main() => 0;
''';
TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('compilation error', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'typo',
expectedError: "The getter 'typo' isn't defined for the class 'C'");
});
test('expression using static fields', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + staticField',
expectedResult: '''
(function(x) {
return dart.notNull(x) + dart.notNull(foo.C.staticField);
}.bind(this)(
1
))
''');
});
test('expression using private static fields', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _staticField',
expectedResult: '''
(function(x) {
let foo = require('foo.dart').foo;
let _staticField = dart.privateName(foo, "_staticField");
return dart.notNull(x) + dart.notNull(foo.C._staticField);
}.bind(this)(
1
))
''');
});
test('expression using fields', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + field',
expectedResult: '''
(function(x) {
return dart.notNull(x) + dart.notNull(this.field);
}.bind(this)(
1
))
''');
});
test('expression using private fields', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x + _field',
expectedResult: '''
(function(x) {
let foo = require('foo.dart').foo;
let _field = dart.privateName(foo, "_field");
return dart.notNull(x) + dart.notNull(this[_field]);
}.bind(this)(
1
))
''');
});
test('private field modification', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_field = 2',
expectedResult: '''
(function(x) {
let foo = require('foo.dart').foo;
let _field = dart.privateName(foo, "_field");
return this[_field] = 2;
}.bind(this)(
1
))
''');
});
test('field modification', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'field = 2',
expectedResult: '''
(function(x) {
return this.field = 2;
}.bind(this)(
1
))
''');
});
test('private static field modification', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: '_staticField = 2',
expectedResult: '''
(function(x) {
let foo = require('foo.dart').foo;
let _staticField = dart.privateName(foo, "_staticField");
return foo.C._staticField = 2;
}.bind(this)(
1
))
''');
});
test('static field modification', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'staticField = 2',
expectedResult: '''
(function(x) {
return foo.C.staticField = 2;
}.bind(this)(
1
))
''');
});
});
group('Expression compiler tests in async method:', () {
const source = '''
// @dart = 2.9
class C {
C(int this.field, int this._field);
int _field;
int field;
Future<int> asyncMethod(int x) async {
/* evaluation placeholder */
return x;
}
}
main() => 0;
''';
TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('compilation error', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'typo',
expectedError: "The getter 'typo' isn't defined for the class 'C'");
});
test('local', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'x',
expectedResult: '''
(function(x) {
return x;
}.bind(this)(
1
))
''');
});
test('this', () async {
await driver.check(
scope: <String, String>{'x': '1'},
expression: 'this',
expectedResult: '''
(function(x) {
return this;
}.bind(this)(
1
))
''');
});
});
group('Expression compiler tests in global function:', () {
const source = '''
// @dart = 2.9
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
}
int global = 42;
class C {
C(int this.field, int this._field);
static int staticField = 0;
static int _staticField = 1;
int _field;
int field;
int methodFieldAccess(int x) {
return (x + _field + _staticField);
}
int methodFieldAccess(int x) {
return (x)
}
Future<int> asyncMethod(int x) async {
return x;
}
}
int main() {
int x = 15;
var c = C(1, 2);
/* evaluation placeholder */
return 0;
}
''';
TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('compilation error', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'typo',
expectedError: "Getter not found: 'typo'.");
});
test('local with primitive type', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'x',
expectedResult: '''
(function(x, c) {
return x;
}(
1, null
))
''');
});
test('local object', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c',
expectedResult: '''
(function(x, c) {
return c;
}(
1, null
))
''');
});
test('create new object', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C(1,3)',
expectedResult: '''
(function(x, c) {
return new foo.C.new(1, 3);
}(
1, null
))
''');
});
test('access field of new object', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C(1,3)._field',
expectedResult: '''
(function(x, c) {
let foo = require('foo.dart').foo;
let _field = dart.privateName(foo, "_field");
return new foo.C.new(1, 3)[_field];
}(
1, null
))
''');
});
test('access static field', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C.staticField',
expectedResult: '''
(function(x, c) {
return foo.C.staticField;
}(
1, null
))
''');
});
test('expression using private static fields', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C._staticField',
expectedError: "Error: Getter not found: '_staticField'.");
});
test('access field', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c.field',
expectedResult: '''
(function(x, c) {
return c.field;
}(
1, null
))
''');
});
test('access private field', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c._field',
expectedResult: '''
(function(x, c) {
let foo = require('foo.dart').foo;
let _field = dart.privateName(foo, "_field");
return c[_field];
}(
1, null
))
''');
});
test('method call', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c.methodFieldAccess(2)',
expectedResult: '''
(function(x, c) {
return c.methodFieldAccess(2);
}(
1, null
))
''');
});
test('async method call', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c.asyncMethod(2)',
expectedResult: '''
(function(x, c) {
return c.asyncMethod(2);
}(
1, null
))
''');
});
test('extension method call', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: '"1234".parseInt()',
expectedResult: '''
(function(x, c) {
return foo['NumberParsing|parseInt']("1234");
}(
1, null
))
''');
});
test('private field modification', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c._field = 2',
expectedResult: '''
(function(x, c) {
let foo = require('foo.dart').foo;
let _field = dart.privateName(foo, "_field");
return c[_field] = 2;
}(
1, null
))
''');
});
test('field modification', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'c.field = 2',
expectedResult: '''
(function(x, c) {
return c.field = 2;
}(
1, null
))
''');
});
test('private static field modification', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C._staticField = 2',
expectedError: "Setter not found: '_staticField'.");
});
test('static field modification', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'C.staticField = 2',
expectedResult: '''
(function(x, c) {
return foo.C.staticField = 2;
}(
1, null
))
''');
});
test('call global function from core library', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null'},
expression: 'print(x)',
expectedResult: '''
(function(x, c) {
return core.print(x);
}(
1, null
))
''');
});
});
group('Expression compiler tests in closures:', () {
const source = r'''
// @dart = 2.9
int globalFunction() {
int x = 15;
var c = C(1, 2);
var outerClosure = (int y) {
var closureCaptureInner = (int z) {
/* evaluation placeholder */
print('$y+$z');
};
closureCaptureInner(0);
};
outerClosure(3);
return 0;
}
main() => 0;
''';
TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('compilation error', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null', 'y': '3', 'z': '0'},
expression: 'typo',
expectedError: "Getter not found: 'typo'.");
});
test('expression using uncaptured variables', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null', 'y': '3', 'z': '0'},
expression: r"'$x+$y+$z'",
expectedResult: '''
(function(x, c, y, z) {
return dart.str(x) + "+" + dart.str(y) + "+" + dart.str(z);
}(
1, null, 3, 0
))
''');
});
test('expression using captured variables', () async {
await driver.check(
scope: <String, String>{'x': '1', 'c': 'null', 'y': '3', 'z': '0'},
expression: r"'$y+$z'",
expectedResult: '''
(function(x, c, y, z) {
return dart.str(y) + "+" + dart.str(z);
}(
1, null, 3, 0
))
''');
});
});
group('Expression compiler tests in method with no type use', () {
const source = '''
// @dart = 2.9
abstract class Key {
const factory Key(String value) = ValueKey;
const Key.empty();
}
abstract class LocalKey extends Key {
const LocalKey() : super.empty();
}
class ValueKey implements LocalKey {
const ValueKey(this.value);
final String value;
}
class MyClass {
const MyClass(this._t);
final int _t;
}
int bar(int p){
return p;
}
int baz(String t){
return t;
}
void main() {
var k = Key('t');
MyClass c = MyClass(0);
int p = 1;
const t = 1;
/* evaluation placeholder */
print('\$c, \$k, \$t');
}
''';
TestDriver driver;
setUp(() {
driver = TestDriver(options, source);
});
tearDown(() {
driver.delete();
});
test('call function using type', () async {
await driver.check(
scope: <String, String>{'p': '1'},
expression: 'bar(p)',
expectedResult: '''
(function(p) {
return foo.bar(p);
}(
1
))
''');
});
test('call function using type', () async {
await driver.check(
scope: <String, String>{'p': '0'},
expression: 'baz(p as String)',
expectedResult: '''
(function(p) {
var StringL = () => (StringL = dart.constFn(dart.legacy(core.String)))();
return foo.baz(StringL().as(p));
}(
0
))
''');
});
test('evaluate new const expression', () async {
await driver.check(
scope: <String, String>{'p': '1'},
expression: 'const MyClass(1)',
expectedResult: '''
(function(p) {
return C0 || CT.C0;
}(
1
))
''');
});
test('evaluate optimized const expression', () async {
await driver.check(
scope: <String, String>{},
expression: 't',
expectedResult: '''
(function() {
return 1;
}(
))
''');
},
skip:
'Cannot compile constants optimized away by the frontend'); // https://github.com/dart-lang/sdk/issues/41999
test('evaluate factory constructor call', () async {
await driver.check(
scope: <String, String>{'p': '1'},
expression: "Key('t')",
expectedResult: '''
(function(p) {
return new foo.ValueKey.new("t");
}(
1
))
''');
});
test('evaluate const factory constructor call', () async {
await driver.check(
scope: <String, String>{'p': '1'},
expression: "const Key('t')",
expectedResult: '''
(function(p) {
return C0 || CT.C0;
}(
1
))
''');
});
});
}