blob: 1bf8c27dcc395149681bf73bf8f9a8898d03610b [file] [log] [blame]
// Copyright (c) 2021, 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:test/test.dart';
import 'expression_compiler_e2e_suite.dart';
const simpleClassSource = '''
extension NumberParsing on String {
int parseInt() {
return int.parse(this) + 1;
}
}
class C {
static int staticField = 1;
static int _staticField = 2;
static int _unusedStaticField = 3;
int field;
int _field;
int _unusedField = 4;
C(this.field, this._field) {
int y = 1;
// Breakpoint: constructorBP
var nop;
}
C.named(this.field): _field = 42;
C.redirecting(int x) : this(x, 99);
factory C.factory() => C(42, 0);
int methodFieldAccess(int x) {
// Breakpoint: methodBP
var inScope = 1;
{
var innerInScope = global + staticField + field;
// Breakpoint: innerScopeBP
var innerNotInScope = 2;
}
var notInScope = 3;
return x + _field + _staticField;
}
Future<int> asyncMethod(int x) async {
return x + _field + _staticField;
}
}
int global = 42;
main() {
int x = 15;
var c = C(5, 6);
// Breakpoint: globalFunctionBP
c.methodFieldAccess(10);
}
''';
/// 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) {
group('Correct null safety mode used', () {
var source = '''
const soundNullSafety = !(<Null>[] is List<int>);
main() {
// Breakpoint: bp
print('hello world');
}
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('in original source compilation', () async {
await driver.check(
breakpointId: 'bp',
expression: 'soundNullSafety',
expectedResult: setup.soundNullSafety.toString());
});
test('in expression compilation', () async {
await driver.check(
breakpointId: 'bp',
expression: '!(<Null>[] is List<int>)',
expectedResult: setup.soundNullSafety.toString());
});
});
group('Expression compiler tests in method:', () {
var source = simpleClassSource;
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('tear off default constructor', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'C.new.runtimeType.toString()',
expectedResult: '(int, int) => C');
});
test('call default constructor tear off', () async {
await driver.check(
breakpointId: 'methodBP',
expression: '(C.new)(0, 0)',
expectedResult: 'test.C.new {Symbol(_unusedField): 4, '
'Symbol(C.field): 0, Symbol(_field): 0}');
});
test('tear off named constructor', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'C.named.runtimeType.toString()',
expectedResult: '(int) => C');
});
test('call named constructor tear off', () async {
await driver.check(
breakpointId: 'methodBP',
expression: '(C.named)(0)',
expectedResult: 'test.C.named {Symbol(_unusedField): 4, '
'Symbol(C.field): 0, Symbol(_field): 42}');
});
test('tear off redirecting constructor', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'C.redirecting.runtimeType.toString()',
expectedResult: '(int) => C');
});
test('call redirecting constructor tear off', () async {
await driver.check(
breakpointId: 'methodBP',
expression: '(C.redirecting)(0)',
expectedResult: 'test.C.redirecting { Symbol(_unusedField): 4, '
'Symbol(C.field): 0, Symbol(_field): 99}');
});
test('tear off factory constructor', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'C.factory.runtimeType.toString()',
expectedResult: '() => C');
});
test('call factory constructor tear off', () async {
await driver.check(
breakpointId: 'methodBP',
expression: '(C.factory)()',
expectedResult: 'test.C.new { Symbol(_unusedField): 4, '
'Symbol(C.field): 42, Symbol(_field): 0}');
});
});
group('Expression compiler tests in global function:', () {
var source = simpleClassSource;
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('tear off default constructor', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'C.new.runtimeType.toString()',
expectedResult: '(int, int) => C');
});
test('call default constructor tear off', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: '(C.new)(0, 0)',
expectedResult: 'test.C.new {Symbol(_unusedField): 4, '
'Symbol(C.field): 0, Symbol(_field): 0}');
});
test('tear off named constructor', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'C.named.runtimeType.toString()',
expectedResult: '(int) => C');
});
test('call named constructor tear off', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: '(C.named)(0)',
expectedResult: 'test.C.named {Symbol(_unusedField): 4, '
'Symbol(C.field): 0, Symbol(_field): 42}');
});
test('tear off redirecting constructor', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'C.redirecting.runtimeType.toString()',
expectedResult: '(int) => C');
});
test('call redirecting constructor tear off', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: '(C.redirecting)(0)',
expectedResult: 'test.C.redirecting { Symbol(_unusedField): 4, '
'Symbol(C.field): 0, Symbol(_field): 99}');
});
test('tear off factory constructor', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'C.factory.runtimeType.toString()',
expectedResult: '() => C');
});
test('call factory constructor tear off', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: '(C.factory)()',
expectedResult: 'test.C.new { Symbol(_unusedField): 4, '
'Symbol(C.field): 42, Symbol(_field): 0}');
});
});
group('Expression compiler tests in constructor:', () {
var source = simpleClassSource;
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('tear off default constructor', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'C.new.runtimeType.toString()',
expectedResult: '(int, int) => C');
});
test('call default constructor tear off', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: '(C.new)(0, 0)',
expectedResult: 'test.C.new {Symbol(_unusedField): 4, '
'Symbol(C.field): 0, Symbol(_field): 0}');
});
test('tear off named constructor', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'C.named.runtimeType.toString()',
expectedResult: '(int) => C');
});
test('call named constructor tear off', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: '(C.named)(0)',
expectedResult: 'test.C.named {Symbol(_unusedField): 4, '
'Symbol(C.field): 0, Symbol(_field): 42}');
});
test('tear off redirecting constructor', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'C.redirecting.runtimeType.toString()',
expectedResult: '(int) => C');
});
test('call redirecting constructor tear off', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: '(C.redirecting)(0)',
expectedResult: 'test.C.redirecting { Symbol(_unusedField): 4, '
'Symbol(C.field): 0, Symbol(_field): 99}');
});
test('tear off factory constructor', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'C.factory.runtimeType.toString()',
expectedResult: '() => C');
});
test('call factory constructor tear off', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: '(C.factory)()',
expectedResult: 'test.C.new { Symbol(_unusedField): 4, '
'Symbol(C.field): 42, Symbol(_field): 0}');
});
});
group('Enums', () {
var source = r'''
enum E {id1, id2, id3}
enum E2 {id1, id2, id3}
main() {
var e = E.id2;
// Breakpoint: bp
print('hello world');
}
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('evaluate to the correct string', () async {
await driver.check(
breakpointId: 'bp',
expression: 'E.id2.toString()',
expectedResult: 'E.id2');
});
test('evaluate to the correct index', () async {
await driver.check(
breakpointId: 'bp', expression: 'E.id3.index', expectedResult: '2');
});
test('compare properly against themselves', () async {
await driver.check(
breakpointId: 'bp',
expression: 'e == E.id2 && E.id2 == E.id2',
expectedResult: 'true');
});
test('compare properly against other enums', () async {
await driver.check(
breakpointId: 'bp',
expression: 'e != E2.id2 && E.id2 != E2.id2',
expectedResult: 'true');
});
});
group('Synthetic variables', () {
var source = r'''
dynamic couldReturnNull() => null;
main() {
var i = couldReturnNull() ?? 10;
// Breakpoint: bp
print(i);
}
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('do not cause a crash the expression compiler', () async {
// The null aware code in the test source causes the compiler to introduce
// a let statement that includes a synthetic variable declaration.
// That variable has no name and was causing a crash in the expression
// compiler https://github.com/dart-lang/sdk/issues/49373.
await driver.check(
breakpointId: 'bp', expression: 'true', expectedResult: 'true');
});
});
}
/// Shared tests that are valid in legacy (before 2.12) and are agnostic to
/// changes in modern versions of Dart.
///
/// Tests that exercise language features introduced strictly before 2.12 are
/// valid here.
void runAgnosticSharedTests(SetupCompilerOptions setup, TestDriver driver) {
group('Correct null safety mode used', () {
var source = '''
const soundNullSafety = !(<Null>[] is List<int>);
main() {
// Breakpoint: bp
print('hello world');
}
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('in original source compilation', () async {
await driver.check(
breakpointId: 'bp',
expression: 'soundNullSafety',
expectedResult: setup.soundNullSafety.toString());
});
test('in expression compilation', () async {
await driver.check(
breakpointId: 'bp',
expression: '!(<Null>[] is List<int>)',
expectedResult: setup.soundNullSafety.toString());
});
});
group('Expression compiler scope collection tests', () {
var source = simpleClassSource;
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('local in scope', () async {
await driver.check(
breakpointId: 'innerScopeBP',
expression: 'inScope',
expectedResult: '1');
});
test('local in inner scope', () async {
await driver.check(
breakpointId: 'innerScopeBP',
expression: 'innerInScope',
expectedResult: '48');
});
test('global in scope', () async {
await driver.check(
breakpointId: 'innerScopeBP',
expression: 'global',
expectedResult: '42');
});
test('static field in scope', () async {
await driver.check(
breakpointId: 'innerScopeBP',
expression: 'staticField',
expectedResult: '1');
});
test('field in scope', () async {
await driver.check(
breakpointId: 'innerScopeBP',
expression: 'field',
expectedResult: '5');
});
test('parameter in scope', () async {
await driver.check(
breakpointId: 'innerScopeBP', expression: 'x', expectedResult: '10');
});
test('local not in scope', () async {
await driver.check(
breakpointId: 'innerScopeBP',
expression: 'notInScope',
expectedError: "Error: The getter 'notInScope' isn't defined for the"
" class 'C'.");
});
test('local not in inner scope', () async {
await driver.check(
breakpointId: 'innerScopeBP',
expression: 'innerNotInScope',
expectedError:
"Error: The getter 'innerNotInScope' isn't defined for the"
" class 'C'.");
});
});
group('Expression compiler extension symbols tests', () {
var source = '''
main() {
List<int> list = [];
list.add(0);
// Breakpoint: bp
}
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('extension symbol used only in expression compilation', () async {
await driver.check(
breakpointId: 'bp', expression: 'list.first', expectedResult: '0');
});
test('extension symbol used in original compilation', () async {
await driver.check(
breakpointId: 'bp',
expression: '() { list.add(1); return list.last; }()',
expectedResult: '1');
});
});
group('Expression compiler tests in extension method:', () {
var source = '''
extension NumberParsing on String {
int parseInt() {
var ret = int.parse(this);
// Breakpoint: bp
return ret;
}
}
main() => "1234".parseInt();
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('compilation error', () async {
await driver.check(
breakpointId: 'bp',
expression: 'typo',
expectedError: "Error: Undefined name 'typo'");
});
test('local (trimmed scope)', () async {
await driver.check(
breakpointId: 'bp', expression: 'ret', expectedResult: '1234');
});
test('this (full scope)', () async {
// Note: this currently fails due to
// - incremental compiler not mapping 'this' from user input to '#this'
// - incremental compiler not allowing #this as a parameter name
await driver.check(
breakpointId: 'bp',
expression: 'this',
expectedError: "Error: Expected identifier, but got 'this'");
});
});
group('Expression compiler tests in static function:', () {
var source = '''
int foo(int x, {int y: 0}) {
int z = 3;
// Breakpoint: bp
return x + y + z;
}
main() => foo(1, y: 2);
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('compilation error', () async {
await driver.check(
breakpointId: 'bp',
expression: 'typo',
expectedError: "Undefined name 'typo'");
});
test('local', () async {
await driver.check(
breakpointId: 'bp', expression: 'x', expectedResult: '1');
});
test('formal', () async {
await driver.check(
breakpointId: 'bp', expression: 'y', expectedResult: '2');
});
test('named formal', () async {
await driver.check(
breakpointId: 'bp', expression: 'z', expectedResult: '3');
});
test('function', () async {
await driver
.check(breakpointId: 'bp', expression: 'main', expectedResult: '''
function main() {
return test.foo(1, {y: 2});
}''');
});
});
group('Expression compiler tests in method:', () {
var source = simpleClassSource;
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('compilation error', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'typo',
expectedError: "The getter 'typo' isn't defined for the class 'C'");
});
test('local', () async {
await driver.check(
breakpointId: 'methodBP', expression: 'x', expectedResult: '10');
});
test('this', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'this',
expectedResult:
'test.C.new {Symbol(_unusedField): 4, Symbol(C.field): 5,'
' Symbol(_field): 6}');
});
test('expression using locals', () async {
await driver.check(
breakpointId: 'methodBP', expression: 'x + 1', expectedResult: '11');
});
test('expression using static fields', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'x + staticField',
expectedResult: '11');
});
test('expression using private static fields', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'x + _staticField',
expectedResult: '12');
});
test('expression using fields', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'x + field',
expectedResult: '15');
});
test('expression using private fields', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'x + _field',
expectedResult: '16');
});
test('expression using globals', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'x + global',
expectedResult: '52');
});
test('expression using fields not referred to in the original code',
() async {
await driver.check(
breakpointId: 'methodBP',
expression: '_unusedField + _unusedStaticField',
expectedResult: '7');
});
test('private field modification', () async {
await driver.check(
breakpointId: 'methodBP',
expression: '_field = 2',
expectedResult: '2');
});
test('field modification', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'field = 3',
expectedResult: '3');
});
test('private static field modification', () async {
await driver.check(
breakpointId: 'methodBP',
expression: '_staticField = 4',
expectedResult: '4');
});
test('static field modification', () async {
await driver.check(
breakpointId: 'methodBP',
expression: 'staticField = 5',
expectedResult: '5');
});
});
group('Expression compiler tests in global function:', () {
var source = simpleClassSource;
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('compilation error', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'typo',
expectedError: "Undefined name 'typo'.");
});
test('local with primitive type', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'x',
expectedResult: '15');
});
test('local object', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'c',
expectedResult:
'test.C.new {Symbol(_unusedField): 4, Symbol(C.field): 5, '
'Symbol(_field): 6}');
});
test('create new object', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'C(3, 4)',
expectedResult:
'test.C.new {Symbol(_unusedField): 4, Symbol(C.field): 3, '
'Symbol(_field): 4}');
});
test('access field of new object', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'C(3, 4)._field',
expectedResult: '4');
});
test('access static field', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'C.staticField',
expectedResult: '1');
});
test('expression using private static fields', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'C._staticField',
expectedResult: '2');
});
test('access field', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'c.field',
expectedResult: '5');
});
test('access private field', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'c._field',
expectedResult: '6');
});
test('method call', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'c.methodFieldAccess(2)',
expectedResult: '10');
});
test('async method call', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'c.asyncMethod(2).runtimeType.toString()',
expectedResult: '_Future<int>');
});
test('extension method call', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: '"1234".parseInt()',
expectedResult: '1235');
});
test('private field modification', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'c._field = 10',
expectedResult: '10');
});
test('field modification', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'c._field = 11',
expectedResult: '11');
});
test('private static field modification', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'C._staticField = 2',
expectedResult: '2');
});
test('static field modification', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'C.staticField = 20',
expectedResult: '20');
});
test('call global function from core library', () async {
await driver.check(
breakpointId: 'globalFunctionBP',
expression: 'identical(1, 1)',
expectedResult: 'true');
});
});
group('Expression compiler tests in constructor:', () {
var source = simpleClassSource;
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('compilation error', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'typo',
expectedError: "The getter 'typo' isn't defined for the class 'C'");
});
test('local', () async {
await driver.check(
breakpointId: 'constructorBP', expression: 'y', expectedResult: '1');
});
test('this', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'this',
expectedResult:
'test.C.new {Symbol(_unusedField): 4, Symbol(C.field): 5,'
' Symbol(_field): 6}');
});
test('expression using locals', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'y + 1',
expectedResult: '2');
});
test('expression using static fields', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'y + staticField',
expectedResult: '2');
});
test('expression using private static fields', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'y + _staticField',
expectedResult: '3');
});
test('expression using fields', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'y + field',
expectedResult: '6');
});
test('expression using private fields', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'y + _field',
expectedResult: '7');
});
test('expression using globals', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'y + global',
expectedResult: '43');
});
test('method call', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'methodFieldAccess(2)',
expectedResult: '10');
});
test('async method call', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'asyncMethod(2).runtimeType.toString()',
expectedResult: '_Future<int>');
});
test('extension method call', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: '"1234".parseInt()',
expectedResult: '1235');
});
test('private field modification', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: '_field = 2',
expectedResult: '2');
});
test('field modification', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'field = 2',
expectedResult: '2');
});
test('private static field modification', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: '_staticField = 2',
expectedResult: '2');
});
test('static field modification', () async {
await driver.check(
breakpointId: 'constructorBP',
expression: 'staticField = 2',
expectedResult: '2');
});
});
group('Expression compiler tests in async method:', () {
var source = '''
class C {
static int staticField = 1;
static int _staticField = 2;
int _field;
int field;
C(this.field, this._field);
Future<int> asyncMethod(int x) async {
// Breakpoint: bp
return x + global + _field + field + staticField + _staticField;
}
}
Future<int> entrypoint() async {
var c = C(5, 7);
// Breakpoint: bp1
return await c.asyncMethod(1);
}
int global = 42;
void main() async {
await entrypoint();
}
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('compilation error', () async {
await driver.check(
breakpointId: 'bp',
expression: 'typo',
expectedError: "The getter 'typo' isn't defined for the class 'C'");
});
test('local', () async {
await driver.check(
breakpointId: 'bp', expression: 'x', expectedResult: '1');
});
test('this', () async {
await driver.check(
breakpointId: 'bp',
expression: 'this',
expectedResult: 'test.C.new {Symbol(C.field): 5, Symbol(_field): 7}');
});
test('awaited method call', () async {
await driver.check(
breakpointId: 'bp1',
expression: 'c.asyncMethod(1).runtimeType.toString()',
expectedResult: '_Future<int>');
}, skip: "'await' is not yet supported in expression evaluation.");
test('awaited method call', () async {
await driver.check(
breakpointId: 'bp1',
expression: 'await c.asyncMethod(1)',
expectedResult: '58');
}, skip: "'await' is not yet supported in expression evaluation.");
});
group('Expression compiler tests in closures:', () {
var source = '''
void globalFunction() {
int x = 15;
var outerClosure = (int y) {
var closureCaptureInner = (int z) {
// Breakpoint: bp
var temp = x + y + z;
return;
};
closureCaptureInner(0);
};
outerClosure(3);
return;
}
main() => globalFunction();
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('compilation error', () async {
await driver.check(
breakpointId: 'bp',
expression: 'typo',
expectedError: "Undefined name 'typo'.");
});
test('expression using captured variables', () async {
await driver.check(
breakpointId: 'bp', expression: r"'$y+$z'", expectedResult: '3+0');
});
test('expression using uncaptured variables', () async {
await driver.check(
breakpointId: 'bp',
expression: r"'$x+$y+$z'",
expectedResult: '15+3+0');
});
});
group('Expression compiler tests in method with no type use:', () {
var source = '''
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;
}
String baz(String t) {
return t;
}
String main() {
var k = Key('t');
MyClass c = MyClass(0);
int p = 1;
const t = 1;
// Breakpoint: bp
return '\$c, \$k, \$t';
}
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('call function not using type', () async {
await driver.check(
breakpointId: 'bp', expression: 'bar(p)', expectedResult: '1');
});
test('call function using type', () async {
await driver.check(
breakpointId: 'bp', expression: "baz('\$p')", expectedResult: '1');
});
test('evaluate new const expression', () async {
await driver.check(
breakpointId: 'bp',
expression: 'const MyClass(1)',
expectedResult: 'MyClass {Symbol(MyClass._t): 1}');
});
test('evaluate optimized const expression', () async {
await driver.check(
breakpointId: 'bp', expression: 't', expectedResult: '1');
},
skip: 'Cannot compile constants optimized away by the frontend. '
'Issue: https://github.com/dart-lang/sdk/issues/41999');
test('evaluate factory constructor call', () async {
await driver.check(
breakpointId: 'bp',
expression: "Key('t')",
expectedResult: 'test.ValueKey.new {Symbol(ValueKey.value): t}');
});
test('evaluate const factory constructor call', () async {
await driver.check(
breakpointId: 'bp',
expression: "const Key('t')",
expectedResult: 'ValueKey {Symbol(ValueKey.value): t}');
});
});
group('Expression compiler tests in simple loops:', () {
var source = '''
void globalFunction() {
int x = 15;
for(int i = 0; i < 10; i++) {
// Breakpoint: bp
var calculation = '\$i+\$x';
};
}
main() => globalFunction();
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('expression using local', () async {
await driver.check(
breakpointId: 'bp', expression: 'x', expectedResult: '15');
});
test('expression using loop variable', () async {
await driver.check(
breakpointId: 'bp', expression: 'i', expectedResult: '0');
});
});
group('Expression compiler tests in conditional:', () {
var source = '''
int globalFunction(int x) {
if (x == 1) {
int y = 3;
// Breakpoint: thenBP
var calculation = '\$y+\$x';
} else {
int z = 4;
// Breakpoint: elseBP
var calculation = '\$z+\$x';
}
// Breakpoint: postBP
return 0;
}
void main() {
globalFunction(1);
globalFunction(2);
}
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('(then) expression using local', () async {
await driver.check(
breakpointId: 'thenBP', expression: 'y', expectedResult: '3');
});
test('(then) expression using local out of scope', () async {
await driver.check(
breakpointId: 'thenBP',
expression: 'z',
expectedError: "Error: Undefined name 'z'");
});
test('(else) expression using local', () async {
await driver.check(
breakpointId: 'elseBP', expression: 'z', expectedResult: '4');
});
test('(else) expression using local out of scope', () async {
await driver.check(
breakpointId: 'elseBP',
expression: 'y',
expectedError: "Error: Undefined name 'y'");
});
test('(post) expression using local', () async {
await driver.check(
breakpointId: 'postBP', expression: 'x', expectedResult: '1');
});
test('(post) expression using local out of scope', () async {
await driver.check(
breakpointId: 'postBP',
expression: 'z',
expectedError: "Error: Undefined name 'z'");
});
test('(post) expression using local out of scope', () async {
await driver.check(
breakpointId: 'postBP',
expression: 'y',
expectedError: "Error: Undefined name 'y'");
});
});
group('Expression compiler tests in iterator loops:', () {
var source = '''
int globalFunction() {
var l = <String>['1', '2', '3'];
for (var e in l) {
// Breakpoint: bp
var calculation = '\$e';
};
return 0;
}
main() => globalFunction();
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('expression loop variable', () async {
await driver.check(
breakpointId: 'bp', expression: 'e', expectedResult: '1');
});
});
group('Expression compiler tests in generic method:', () {
var source = '''
class C<T1> {
void generic<T2>(T1 a, T2 b) {
// Breakpoint: bp
print(a);
print(b);
}
}
void main() => C<int>().generic<String>(0, 'hi');
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('evaluate formals', () async {
await driver.check(
breakpointId: 'bp',
expression: "'\${a} \$b'",
expectedResult: '0 hi');
});
test('evaluate class type parameters', () async {
await driver.check(
breakpointId: 'bp', expression: "'\$T1'", expectedResult: 'int');
});
test('evaluate method type parameters', () async {
await driver.check(
breakpointId: 'bp', expression: "'\$T2'", expectedResult: 'String');
});
});
group('Expression compiler tests for interactions with module containers:',
() {
var source = '''
class A {
const A();
}
class B {
const B();
}
void foo() {
const a = A();
var check = a is int;
// Breakpoint: bp
return;
}
void main() => foo();
''';
setUpAll(() async {
await driver.initSource(setup, source);
});
tearDownAll(() async {
await driver.cleanupTest();
});
test('evaluation that non-destructively appends to the type container',
() async {
await driver.check(
breakpointId: 'bp',
expression: 'a is String',
expectedResult: 'false');
});
test('evaluation that reuses the type container', () async {
await driver.check(
breakpointId: 'bp', expression: 'a is int', expectedResult: 'false');
});
test('evaluation that non-destructively appends to the constant container',
() async {
await driver.check(
breakpointId: 'bp',
expression: 'const B() == const B()',
expectedResult: 'true');
});
test('evaluation that properly canonicalizes constants', () async {
await driver.check(
breakpointId: 'bp',
expression: 'a == const A()',
expectedResult: 'true');
});
});
}