mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:49:00 +00:00
Reland "[ddc] Add module local caches for new types"
This is a reland of commit a9fc9ffc4d
Additional changes:
- Set incremental mode on generic class table
- fixes expression evaluation failure discovered by dwds tests
(generic class table was not always defined in compiled expression)
- Allow expression evaluation while the app is running in e2e suite
- Add regression tests for the expression evaluation failure.
Original change's description:
> [ddc] Add module local caches for new types
>
> - Provides fast access for types that are used multiple times in the
> same module.
> - Enable the existing type table cache when running with new types.
> - Add a similar cache for instantiated generic classes. This cache
> is used in the current type system as well to help keep the
> difference between types and classes more clear.
>
> Issue: https://github.com/dart-lang/sdk/issues/48585
> Change-Id: I32103cf0c0bcf9b9771e789c0d04e63a4365a066
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/321320
> Commit-Queue: Nicholas Shahan <nshahan@google.com>
> Reviewed-by: Mark Zhou <markzipan@google.com>
Change-Id: I9c31d1d07d7f9bb15645ac9aa6e91d35e8906e85
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/323501
Reviewed-by: Mark Zhou <markzipan@google.com>
Commit-Queue: Anna Gringauze <annagrin@google.com>
Reviewed-by: Nicholas Shahan <nshahan@google.com>
This commit is contained in:
parent
36c6daa920
commit
3740e620cd
11 changed files with 616 additions and 306 deletions
|
@ -172,6 +172,11 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
/// Table of named and possibly hoisted types.
|
||||
late TypeTable _typeTable;
|
||||
|
||||
/// Table of instantiated generic class references.
|
||||
///
|
||||
/// Provides a cache for the instantiated generic types local to a module.
|
||||
late TypeTable _genericClassTable;
|
||||
|
||||
/// The global extension type table.
|
||||
// TODO(jmesserly): rename to `_nativeTypes`
|
||||
final NativeTypeSet _extensionTypes;
|
||||
|
@ -367,6 +372,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
_assertInteropMethod = sdk.getTopLevelMember(
|
||||
'dart:_runtime', 'assertInterop') as Procedure,
|
||||
_futureOrNormalizer = FutureOrNormalizer(_coreTypes),
|
||||
_extensionTypeEraser = ExtensionTypeEraser(),
|
||||
_typeRecipeGenerator = TypeRecipeGenerator(_coreTypes, _hierarchy),
|
||||
_extensionIndex =
|
||||
ExtensionIndex(_coreTypes, _staticTypeContext.typeEnvironment);
|
||||
|
@ -390,6 +396,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
|
||||
final FutureOrNormalizer _futureOrNormalizer;
|
||||
|
||||
final ExtensionTypeEraser _extensionTypeEraser;
|
||||
|
||||
/// Module can be emitted only once, and the compiler can be reused after
|
||||
/// only in incremental mode, for expression compilation only.
|
||||
js_ast.Program emitModule(Component component) {
|
||||
|
@ -451,7 +459,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
}
|
||||
|
||||
_nullableInference.allowNotNullDeclarations = isBuildingSdk;
|
||||
_typeTable = TypeTable(runtimeCall);
|
||||
_typeTable = TypeTable('T', runtimeCall);
|
||||
_genericClassTable = TypeTable('G', runtimeCall);
|
||||
|
||||
// Collect all class/type Element -> Node mappings
|
||||
// in case we need to forward declare any classes.
|
||||
|
@ -616,6 +625,10 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
items.addAll(_typeTable.dischargeBoundTypes());
|
||||
_ticker?.logMs('Emitted type table');
|
||||
|
||||
// Emit the hoisted instantiated generic class table cache variables
|
||||
items.addAll(_genericClassTable.dischargeBoundTypes());
|
||||
_ticker?.logMs('Emitted instantiated generic class table');
|
||||
|
||||
var module = finishModule(items, _options.moduleName,
|
||||
header: generateCompilationHeader());
|
||||
_ticker?.logMs('Finished emitting module');
|
||||
|
@ -1057,8 +1070,13 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
body = js_ast.Statement.from([body, varianceStatement]);
|
||||
}
|
||||
|
||||
var typeConstructor = js.call('(#) => { #; #; return #; }',
|
||||
[jsFormals, _typeTable.dischargeFreeTypes(formals), body, className]);
|
||||
var typeConstructor = js.call('(#) => { #; #; #; return #; }', [
|
||||
jsFormals,
|
||||
_typeTable.dischargeFreeTypes(formals),
|
||||
_genericClassTable.dischargeFreeTypes(formals),
|
||||
body,
|
||||
className
|
||||
]);
|
||||
|
||||
var genericArgs = [
|
||||
typeConstructor,
|
||||
|
@ -1268,6 +1286,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
if (t is RecordType) {
|
||||
return t.positional.any(defer) || t.named.any((n) => defer(n.type));
|
||||
}
|
||||
if (t is ExtensionType) return defer(t.typeErasure);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1631,7 +1650,10 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
_classEmittingSignatures = c;
|
||||
|
||||
var interfaces = c.implementedTypes.toList()..addAll(c.onClause);
|
||||
if (interfaces.isNotEmpty) {
|
||||
if (interfaces.isNotEmpty &&
|
||||
// New runtime types don't use this data structure to lookup interfaces
|
||||
// a class implements.
|
||||
!_options.newRuntimeTypes) {
|
||||
body.add(js.statement('#[#] = () => [#];', [
|
||||
className,
|
||||
runtimeCall('implements'),
|
||||
|
@ -3260,6 +3282,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
if (type is TypedefType) {
|
||||
return type.typeArguments.every(_canEmitTypeAtTopLevel);
|
||||
}
|
||||
if (type is ExtensionType) return _canEmitTypeAtTopLevel(type.typeErasure);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3304,6 +3327,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
// An environment with a single type parameter can be simplified to
|
||||
// just that parameter.
|
||||
env = _emitTypeParameter(environment.parameters.single);
|
||||
// Skip a no-op evaluation and just return the parameter.
|
||||
if (recipe == '0') return env;
|
||||
} else {
|
||||
var environmentTypes = environment.parameters;
|
||||
// Create a dummy interface type to "hold" type arguments.
|
||||
|
@ -3334,16 +3359,15 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(nshahan) Avoid calling _emitType when we actually want a
|
||||
// reference to an rti that already exists in scope.
|
||||
if (type is TypeParameterType && type.isPotentiallyNonNullable) {
|
||||
return _emitTypeParameterType(type, emitNullability: false);
|
||||
}
|
||||
var normalizedType = _futureOrNormalizer.normalize(type);
|
||||
var normalizedType =
|
||||
_futureOrNormalizer.normalize(_extensionTypeEraser.erase(type));
|
||||
try {
|
||||
var result = _typeRecipeGenerator.recipeInEnvironment(
|
||||
normalizedType, _currentTypeEnvironment);
|
||||
return evalInEnvironment(result.requiredEnvironment, result.recipe);
|
||||
var typeRep =
|
||||
evalInEnvironment(result.requiredEnvironment, result.recipe);
|
||||
if (_cacheTypes) typeRep = _typeTable.nameType(normalizedType, typeRep);
|
||||
return typeRep;
|
||||
} on UnsupportedError catch (e) {
|
||||
_typeCompilationError(normalizedType, e.message ?? 'Unknown Error');
|
||||
}
|
||||
|
@ -3496,7 +3520,10 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
if (args.any((a) => a != const DynamicType())) {
|
||||
jsArgs = args.map(_emitType);
|
||||
}
|
||||
if (jsArgs != null) return _emitGenericClassType(type, jsArgs);
|
||||
if (jsArgs != null) {
|
||||
return _genericClassTable.nameType(
|
||||
type, _emitGenericClassType(type, jsArgs));
|
||||
}
|
||||
return _emitTopLevelNameNoExternalInterop(type.classNode);
|
||||
}
|
||||
|
||||
|
@ -3567,11 +3594,12 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
bool get _emittingClassExtends =>
|
||||
_currentClass != null && identical(_currentClass, _classEmittingExtends);
|
||||
|
||||
bool get _cacheTypes =>
|
||||
!_emittingDeferredType &&
|
||||
!_emittingClassExtends &&
|
||||
!_emittingClassSignatures ||
|
||||
_currentFunction != null;
|
||||
bool get _cacheTypes => _options.newRuntimeTypes
|
||||
? !_emittingDeferredType && !_emittingClassExtends
|
||||
: !_emittingDeferredType &&
|
||||
!_emittingClassExtends &&
|
||||
!_emittingClassSignatures ||
|
||||
_currentFunction != null;
|
||||
|
||||
js_ast.Expression _emitGenericClassType(
|
||||
InterfaceType t, Iterable<js_ast.Expression> typeArgs) {
|
||||
|
@ -3812,6 +3840,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
_uriContainer = ModuleItemContainer<String>.asArray('I');
|
||||
|
||||
_typeTable.typeContainer.setIncrementalMode();
|
||||
_genericClassTable.typeContainer.setIncrementalMode();
|
||||
}
|
||||
|
||||
/// Emits function after initial compilation.
|
||||
|
@ -3853,6 +3882,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
var body = js_ast.Block([
|
||||
...extensionSymbols,
|
||||
..._typeTable.dischargeBoundTypes(),
|
||||
..._genericClassTable.dischargeBoundTypes(),
|
||||
...symbolContainer.emit(),
|
||||
..._emitConstTable(),
|
||||
..._uriContainer.emit(),
|
||||
|
@ -6851,23 +6881,21 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
js_ast.Expression _emitMapImplType(InterfaceType type, {bool? identity}) {
|
||||
var typeArgs = type.typeArguments;
|
||||
if (typeArgs.isEmpty) {
|
||||
return _emitInterfaceType(type, emitNullability: false);
|
||||
return _emitClassRef(type);
|
||||
}
|
||||
identity ??= _typeRep.isPrimitive(typeArgs[0]);
|
||||
var c = identity ? _identityHashMapImplClass : _linkedHashMapImplClass;
|
||||
return _emitInterfaceType(InterfaceType(c, Nullability.legacy, typeArgs),
|
||||
emitNullability: false);
|
||||
return _emitClassRef(InterfaceType(c, Nullability.legacy, typeArgs));
|
||||
}
|
||||
|
||||
js_ast.Expression _emitSetImplType(InterfaceType type, {bool? identity}) {
|
||||
var typeArgs = type.typeArguments;
|
||||
if (typeArgs.isEmpty) {
|
||||
return _emitInterfaceType(type, emitNullability: false);
|
||||
return _emitClassRef(type);
|
||||
}
|
||||
identity ??= _typeRep.isPrimitive(typeArgs[0]);
|
||||
var c = identity ? _identityHashSetImplClass : _linkedHashSetImplClass;
|
||||
return _emitInterfaceType(InterfaceType(c, Nullability.legacy, typeArgs),
|
||||
emitNullability: false);
|
||||
return _emitClassRef(InterfaceType(c, Nullability.legacy, typeArgs));
|
||||
}
|
||||
|
||||
js_ast.Expression _emitObjectLiteral(Arguments node, Member ctor) {
|
||||
|
@ -7199,9 +7227,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
if (itemType == const DynamicType()) return list;
|
||||
|
||||
// Call `new JSArray<E>.of(list)`
|
||||
var arrayType = _emitInterfaceType(
|
||||
InterfaceType(_jsArrayClass, Nullability.legacy, [itemType]),
|
||||
emitNullability: false);
|
||||
var arrayType = _emitClassRef(
|
||||
InterfaceType(_jsArrayClass, Nullability.nonNullable, [itemType]));
|
||||
return js.call('#.of(#)', [arrayType, list]);
|
||||
}
|
||||
|
||||
|
@ -7217,10 +7244,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
js_ast.Expression visitSetLiteral(SetLiteral node) {
|
||||
// TODO(markzipan): remove const check when we use front-end const eval
|
||||
if (!node.isConst) {
|
||||
var setType = _emitInterfaceType(
|
||||
InterfaceType(
|
||||
_linkedHashSetClass, Nullability.legacy, [node.typeArgument]),
|
||||
emitNullability: false);
|
||||
var setType = _emitClassRef(InterfaceType(
|
||||
_linkedHashSetClass, Nullability.legacy, [node.typeArgument]));
|
||||
if (node.expressions.isEmpty) {
|
||||
return js.call('#.new()', [setType]);
|
||||
}
|
||||
|
@ -7674,8 +7699,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
var type = node
|
||||
.getType(_staticTypeContext)
|
||||
.withDeclaredNullability(Nullability.nonNullable);
|
||||
var classRef =
|
||||
_emitInterfaceType(type as InterfaceType, emitNullability: false);
|
||||
var classRef = _emitClassRef(type as InterfaceType);
|
||||
var prototype = js.call('#.prototype', [classRef]);
|
||||
var properties = [
|
||||
if (_options.newRuntimeTypes && type.typeArguments.isNotEmpty)
|
||||
|
|
|
@ -379,13 +379,9 @@ class ExpressionCompiler {
|
|||
|
||||
var args = localJsScope.join(',\n ');
|
||||
jsExpression = jsExpression.split('\n').join('\n ');
|
||||
var callExpression = '\ntry {'
|
||||
'\n ($jsExpression('
|
||||
var callExpression = '\n ($jsExpression('
|
||||
'\n $args'
|
||||
'\n ))'
|
||||
'\n} catch (error) {'
|
||||
'\n error.name + ": " + error.message;'
|
||||
'\n}';
|
||||
'\n ))';
|
||||
|
||||
_log('Compiled expression \n$expression to $callExpression');
|
||||
return callExpression;
|
||||
|
|
|
@ -60,6 +60,7 @@ class JSTypeRep extends SharedJSTypeRep<DartType> {
|
|||
if (type == const DynamicType() || type == const VoidType()) {
|
||||
return JSType.jsUnknown;
|
||||
}
|
||||
if (type is ExtensionType) typeFor(type.typeErasure);
|
||||
return JSType.jsObject;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'dart:collection';
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/kernel.dart' hide Pattern;
|
||||
import 'package:kernel/src/replacement_visitor.dart';
|
||||
|
||||
Constructor? unnamedConstructor(Class c) =>
|
||||
c.constructors.firstWhereOrNull((c) => c.name.text == '');
|
||||
|
@ -290,6 +291,7 @@ class LabelContinueFinder extends RecursiveVisitor<void> {
|
|||
/// code if used in an assert.
|
||||
bool isKnownDartTypeImplementor(DartType t) {
|
||||
return t is DynamicType ||
|
||||
t is ExtensionType ||
|
||||
t is FunctionType ||
|
||||
t is FutureOrType ||
|
||||
t is InterfaceType ||
|
||||
|
@ -366,3 +368,15 @@ class InterfaceTypeExtractor extends RecursiveVisitor<DartType> {
|
|||
return _found;
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionTypeEraser extends ReplacementVisitor {
|
||||
const ExtensionTypeEraser();
|
||||
|
||||
/// Erases all `ExtensionType` nodes found in [type].
|
||||
DartType erase(DartType type) =>
|
||||
type.accept1(this, Variance.unrelated) ?? type;
|
||||
|
||||
@override
|
||||
DartType? visitExtensionType(ExtensionType node, int variance) =>
|
||||
node.typeErasure.accept1(this, Variance.unrelated) ?? node.typeErasure;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ Set<TypeParameter> freeTypeParameters(DartType t) {
|
|||
} else if (t is RecordType) {
|
||||
t.positional.forEach((p) => find(p));
|
||||
t.named.forEach((n) => find(n.type));
|
||||
} else if (t is ExtensionType) {
|
||||
find(t.typeErasure);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,6 +104,7 @@ String _typeString(DartType type, {bool flat = false}) {
|
|||
}
|
||||
return 'Rec${nullability}Of$elements';
|
||||
}
|
||||
if (type is ExtensionType) return _typeString(type.typeErasure);
|
||||
return 'invalid';
|
||||
}
|
||||
|
||||
|
@ -120,12 +123,13 @@ class TypeTable {
|
|||
final _unboundTypeIds = HashMap<DartType, js_ast.Identifier>();
|
||||
|
||||
/// Holds JS type generators keyed by their underlying DartType.
|
||||
final typeContainer = ModuleItemContainer<DartType>.asObject('T',
|
||||
keyToString: (DartType t) => escapeIdentifier(_typeString(t))!);
|
||||
final ModuleItemContainer<DartType> typeContainer;
|
||||
|
||||
final js_ast.Expression Function(String, [List<Object>]) _runtimeCall;
|
||||
|
||||
TypeTable(this._runtimeCall);
|
||||
TypeTable(String name, this._runtimeCall)
|
||||
: typeContainer = ModuleItemContainer<DartType>.asObject(name,
|
||||
keyToString: (DartType t) => escapeIdentifier(_typeString(t))!);
|
||||
|
||||
/// Returns true if [type] is already recorded in the table.
|
||||
bool _isNamed(DartType type) =>
|
||||
|
|
|
@ -8,8 +8,11 @@ import '../shared_test_options.dart';
|
|||
import 'expression_compiler_e2e_suite.dart';
|
||||
|
||||
void main(List<String> args) async {
|
||||
final debug = false;
|
||||
|
||||
var driver = await ExpressionEvaluationTestDriver.init();
|
||||
var setup = SetupCompilerOptions(args: args);
|
||||
setup.options.verbose = debug;
|
||||
|
||||
group('Asserts', () {
|
||||
const source = r'''
|
||||
|
@ -34,27 +37,26 @@ void main(List<String> args) async {
|
|||
if (setup.enableAsserts) {
|
||||
group('enabled |', () {
|
||||
test('dart.web.assertions_enabled is set', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'b', expectedResult: 'true');
|
||||
});
|
||||
|
||||
test('assert errors in the source code', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'myAssert()',
|
||||
expectedResult: allOf(
|
||||
expectedError: allOf(
|
||||
contains('Error: Assertion failed:'),
|
||||
contains('test.dart:8:16'),
|
||||
contains('false'),
|
||||
contains('is not true'),
|
||||
));
|
||||
});
|
||||
|
||||
test('assert errors in evaluated expression', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: '() { assert(false); return 0; } ()',
|
||||
expectedResult: allOf(
|
||||
expectedError: allOf(
|
||||
contains('Error: Assertion failed:'),
|
||||
contains('<unknown source>:-1:-1'),
|
||||
contains('BoolLiteral(false)'),
|
||||
|
@ -67,19 +69,19 @@ void main(List<String> args) async {
|
|||
if (!setup.enableAsserts) {
|
||||
group('disabled |', () {
|
||||
test('dart.web.assertions_enabled is not set', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'b', expectedResult: 'false');
|
||||
});
|
||||
|
||||
test('no assert errors in the source code', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'myAssert()',
|
||||
expectedResult: '0');
|
||||
});
|
||||
|
||||
test('no assert errors in evaluated expression', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: '() { assert(false); return 0; } ()',
|
||||
expectedResult: '0');
|
||||
|
|
|
@ -110,31 +110,31 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('in top level method', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'topLevelMethod(param3: 3, 1, param4: "four", "two")',
|
||||
expectedResult: '1, two, 3, four');
|
||||
});
|
||||
test('in local method', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'topLevelMethod(param3: 3, 1, param4: "four", "two")',
|
||||
expectedResult: '1, two, 3, four');
|
||||
});
|
||||
test('in class constructor', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'C(param3: 3, 1, param4: "four", "two").toString()',
|
||||
expectedResult: '1, two, 3, four');
|
||||
});
|
||||
test('in class static method', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'C.staticMethod(param3: 3, 1, param4: "four", "two")',
|
||||
expectedResult: '1, two, 3, four');
|
||||
});
|
||||
test('in class instance method', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'c.instanceMethod(param3: 3, 1, param4: "four", "two")',
|
||||
expectedResult: '1, two, 3, four');
|
||||
|
@ -179,27 +179,27 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('in constructor mixed with regular parameters', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'c.i1', expectedResult: '1');
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'c.i', expectedResult: '2');
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'c.i2', expectedResult: '3');
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'c.s', expectedResult: 'bar');
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'c.d', expectedResult: '3.14');
|
||||
});
|
||||
test('in named constructor mixed with regular parameters', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'c2.i1', expectedResult: '10');
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'c2.i', expectedResult: '20');
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'c2.i2', expectedResult: '30');
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'c2.s', expectedResult: 'default');
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp', expression: 'c2.d', expectedResult: '2.71');
|
||||
});
|
||||
});
|
||||
|
@ -245,61 +245,61 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('evaluate to the correct string', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'E.id_string.toString()',
|
||||
expectedResult: 'E.id_string');
|
||||
});
|
||||
test('evaluate to the correct index', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'E.id_string.index',
|
||||
expectedResult: '2');
|
||||
});
|
||||
test('compare properly against themselves', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'e == E.id_string && E.id_string == E.id_string',
|
||||
expectedResult: 'true');
|
||||
});
|
||||
test('compare properly against other enums', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'e != E2.id_string && E.id_string != E2.id_string',
|
||||
expectedResult: 'true');
|
||||
});
|
||||
test('with instance methods', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'E.id_bool.instanceMethod()',
|
||||
expectedResult: '42');
|
||||
});
|
||||
test('with instance methods from local instance', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'e.instanceMethod()',
|
||||
expectedResult: '13');
|
||||
});
|
||||
test('with getters', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'E.id_int.fieldGetter',
|
||||
expectedResult: '0');
|
||||
});
|
||||
test('with getters from local instance', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'e.fieldGetter',
|
||||
expectedResult: 'hello world');
|
||||
});
|
||||
test('with mixin calls', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'E.id_string.mixinMethod()',
|
||||
expectedResult: '200');
|
||||
});
|
||||
test('with mixin calls through overridden indices', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'E2.v2.mixinMethod()',
|
||||
expectedResult: '100');
|
||||
|
|
|
@ -87,84 +87,84 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('simple record', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'r.toString()',
|
||||
expectedResult: '(true, 3)');
|
||||
});
|
||||
|
||||
test('simple record type', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'r.runtimeType.toString()',
|
||||
expectedResult: '(bool, int)');
|
||||
});
|
||||
|
||||
test('simple record field one', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'r.\$1.toString()',
|
||||
expectedResult: 'true');
|
||||
});
|
||||
|
||||
test('simple record field two', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'r.\$2.toString()',
|
||||
expectedResult: '3');
|
||||
});
|
||||
|
||||
test('complex record', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'cr.toString()',
|
||||
expectedResult: '(true, {a: 1, b: 2})');
|
||||
});
|
||||
|
||||
test('complex record type', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'cr.runtimeType.toString()',
|
||||
expectedResult: '(bool, IdentityMap<String, int>)');
|
||||
});
|
||||
|
||||
test('complex record field one', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'cr.\$1.toString()',
|
||||
expectedResult: 'true');
|
||||
});
|
||||
|
||||
test('complex record field two', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'cr.\$2.toString()',
|
||||
expectedResult: '{a: 1, b: 2}');
|
||||
});
|
||||
|
||||
test('nested record', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'nr.toString()',
|
||||
expectedResult: '(true, (false, 3))');
|
||||
});
|
||||
|
||||
test('nested record type', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'nr.runtimeType.toString()',
|
||||
expectedResult: '(bool, (bool, int))');
|
||||
});
|
||||
|
||||
test('nested record field one', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'nr.\$1.toString()',
|
||||
expectedResult: 'true');
|
||||
});
|
||||
|
||||
test('nested record field two', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp',
|
||||
expression: 'nr.\$2.toString()',
|
||||
expectedResult: '(false, 3)');
|
||||
|
@ -206,40 +206,40 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('first case match', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp1', expression: 'a.toString()', expectedResult: '1');
|
||||
});
|
||||
|
||||
test('second case match', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp2',
|
||||
expression: 'a.toString()',
|
||||
expectedResult: '10');
|
||||
});
|
||||
|
||||
test('default case match', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp3',
|
||||
expression: 'obj.toString()',
|
||||
expectedResult: '0');
|
||||
});
|
||||
|
||||
test('first case match result', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp4',
|
||||
expression: 'one.toString()',
|
||||
expectedResult: '1');
|
||||
});
|
||||
|
||||
test('second case match result', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp4',
|
||||
expression: 'ten.toString()',
|
||||
expectedResult: '10');
|
||||
});
|
||||
|
||||
test('default match result', () async {
|
||||
await driver.check(
|
||||
await driver.checkInFrame(
|
||||
breakpointId: 'bp4',
|
||||
expression: 'zero.toString()',
|
||||
expectedResult: '0');
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,19 +23,22 @@ class ExpressionEvaluationTestDriver {
|
|||
final Directory chromeDir;
|
||||
final wip.WipConnection connection;
|
||||
final wip.WipDebugger debugger;
|
||||
final wip.WipRuntime runtime;
|
||||
final ExecutionContext executionContext;
|
||||
late TestExpressionCompiler compiler;
|
||||
late Uri htmlBootstrapper;
|
||||
late Uri input;
|
||||
late Uri output;
|
||||
late Uri packagesFile;
|
||||
late String preemptiveBp;
|
||||
String? preemptiveBp;
|
||||
late SetupCompilerOptions setup;
|
||||
late String source;
|
||||
late Directory testDir;
|
||||
late String dartSdkPath;
|
||||
|
||||
ExpressionEvaluationTestDriver._(
|
||||
this.chrome, this.chromeDir, this.connection, this.debugger);
|
||||
this.chrome, this.chromeDir, this.connection, this.debugger, this.runtime)
|
||||
: executionContext = ExecutionContext(runtime);
|
||||
|
||||
/// Initializes a Chrome browser instance, tab connection, and debugger.
|
||||
static Future<ExpressionEvaluationTestDriver> init() async {
|
||||
|
@ -79,19 +82,27 @@ class ExpressionEvaluationTestDriver {
|
|||
await connection.page.enable().timeout(Duration(seconds: 5),
|
||||
onTimeout: (() => throw Exception('Unable to enable WIP tab page')));
|
||||
|
||||
var runtime = connection.runtime;
|
||||
await runtime.enable().timeout(Duration(seconds: 5),
|
||||
onTimeout: (() => throw Exception('Unable to enable WIP runtime')));
|
||||
|
||||
var debugger = connection.debugger;
|
||||
await debugger.enable().timeout(Duration(seconds: 5),
|
||||
onTimeout: (() => throw Exception('Unable to enable WIP debugger')));
|
||||
|
||||
return ExpressionEvaluationTestDriver._(
|
||||
chrome, chromeDir, connection, debugger);
|
||||
chrome, chromeDir, connection, debugger, runtime);
|
||||
}
|
||||
|
||||
/// Must be called when testing a new Dart program.
|
||||
///
|
||||
/// Depends on SDK artifacts (such as the sound and unsound dart_sdk.js
|
||||
/// files) generated from the 'ddc_stable_test' and 'ddc_canary_test' targets.
|
||||
Future<void> initSource(SetupCompilerOptions setup, String source,
|
||||
{Map<String, bool> experiments = const {}}) async {
|
||||
Future<void> initSource(
|
||||
SetupCompilerOptions setup,
|
||||
String source, {
|
||||
Map<String, bool> experiments = const {},
|
||||
}) async {
|
||||
// Perform setup sanity checks.
|
||||
var summaryPath = setup.options.sdkSummary!.toFilePath();
|
||||
if (!File(summaryPath).existsSync()) {
|
||||
|
@ -169,6 +180,7 @@ class ExpressionEvaluationTestDriver {
|
|||
<script src='$outputPath'></script>
|
||||
<script>
|
||||
'use strict';
|
||||
let dartApplication = true;
|
||||
var sound = ${setup.soundNullSafety};
|
||||
var sdk = dart_library.import('dart_sdk');
|
||||
|
||||
|
@ -216,6 +228,7 @@ class ExpressionEvaluationTestDriver {
|
|||
},
|
||||
waitSeconds: 15
|
||||
});
|
||||
let dartApplication = true;
|
||||
var sound = ${setup.soundNullSafety};
|
||||
|
||||
require(['dart_sdk', '$moduleName'],
|
||||
|
@ -272,7 +285,9 @@ class ExpressionEvaluationTestDriver {
|
|||
|
||||
Future<void> cleanupTest() async {
|
||||
await setBreakpointsActive(debugger, false);
|
||||
await debugger.removeBreakpoint(preemptiveBp);
|
||||
if (preemptiveBp != null) {
|
||||
await debugger.removeBreakpoint(preemptiveBp!);
|
||||
}
|
||||
setup.diagnosticMessages.clear();
|
||||
setup.errors.clear();
|
||||
}
|
||||
|
@ -290,8 +305,8 @@ class ExpressionEvaluationTestDriver {
|
|||
|
||||
Future<wip.WipScript> _loadScript() async {
|
||||
final scriptController = StreamController<wip.ScriptParsedEvent>();
|
||||
final consoleSub =
|
||||
debugger.connection.runtime.onConsoleAPICalled.listen(print);
|
||||
final consoleSub = debugger.connection.runtime.onConsoleAPICalled
|
||||
.listen((e) => printOnFailure('$e'));
|
||||
|
||||
// Fail on exceptions in JS code.
|
||||
await debugger.setPauseOnExceptions(wip.PauseState.uncaught);
|
||||
|
@ -330,6 +345,7 @@ class ExpressionEvaluationTestDriver {
|
|||
}
|
||||
}
|
||||
|
||||
/// Load the script and run [onPause] when the app pauses on [breakpointId].
|
||||
Future<T> _onBreakpoint<T>(String breakpointId,
|
||||
{required Future<T> Function(wip.DebuggerPausedEvent) onPause}) async {
|
||||
// The next two pause events will correspond to:
|
||||
|
@ -386,6 +402,22 @@ class ExpressionEvaluationTestDriver {
|
|||
}
|
||||
}
|
||||
|
||||
/// Load the script and run the [body] while the app is running.
|
||||
Future<T> _whileRunning<T>({required Future<T> Function() body}) async {
|
||||
final consoleSub = debugger.connection.runtime.onConsoleAPICalled
|
||||
.listen((e) => printOnFailure('$e'));
|
||||
|
||||
await _loadScript();
|
||||
try {
|
||||
// Continue running, ignoring the first pause event since it corresponds
|
||||
// to the preemptive URI breakpoint made prior to page navigation.
|
||||
await debugger.resume();
|
||||
return await body();
|
||||
} finally {
|
||||
await consoleSub.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, String>> getScope(String breakpointId) async {
|
||||
return await _onBreakpoint(breakpointId, onPause: (event) async {
|
||||
// Retrieve the call frame and its scope variables.
|
||||
|
@ -397,13 +429,13 @@ class ExpressionEvaluationTestDriver {
|
|||
/// Evaluates a dart [expression] on a breakpoint.
|
||||
///
|
||||
/// [breakpointId] is the ID of the breakpoint from the source.
|
||||
Future<String> evaluateDartExpression({
|
||||
Future<String> evaluateDartExpressionInFrame({
|
||||
required String breakpointId,
|
||||
required String expression,
|
||||
}) async {
|
||||
var dartLine = _findBreakpointLine(breakpointId);
|
||||
return await _onBreakpoint(breakpointId, onPause: (event) async {
|
||||
var result = await _evaluateDartExpression(
|
||||
var result = await _evaluateDartExpressionInFrame(
|
||||
event,
|
||||
expression,
|
||||
dartLine,
|
||||
|
@ -412,6 +444,14 @@ class ExpressionEvaluationTestDriver {
|
|||
});
|
||||
}
|
||||
|
||||
/// Evaluates a dart [expression] while the app is running.
|
||||
Future<String> evaluateDartExpression({required String expression}) async {
|
||||
return await _whileRunning(body: () async {
|
||||
var result = await _evaluateDartExpression(expression);
|
||||
return await stringifyRemoteObject(result);
|
||||
});
|
||||
}
|
||||
|
||||
/// Evaluates a js [expression] on a breakpoint.
|
||||
///
|
||||
/// [breakpointId] is the ID of the breakpoint from the source.
|
||||
|
@ -456,7 +496,7 @@ class ExpressionEvaluationTestDriver {
|
|||
/// [expression] is a dart expression.
|
||||
/// [expectedResult] is the JSON for the returned remote object.
|
||||
/// [expectedError] is the error string if the error is expected.
|
||||
Future<void> check(
|
||||
Future<void> checkInFrame(
|
||||
{required String breakpointId,
|
||||
required String expression,
|
||||
dynamic expectedError,
|
||||
|
@ -466,7 +506,7 @@ class ExpressionEvaluationTestDriver {
|
|||
|
||||
var dartLine = _findBreakpointLine(breakpointId);
|
||||
return await _onBreakpoint(breakpointId, onPause: (event) async {
|
||||
var evalResult = await _evaluateDartExpression(
|
||||
var evalResult = await _evaluateDartExpressionInFrame(
|
||||
event,
|
||||
expression,
|
||||
dartLine,
|
||||
|
@ -493,6 +533,42 @@ class ExpressionEvaluationTestDriver {
|
|||
});
|
||||
}
|
||||
|
||||
/// Evaluates a dart [expression] without breakpoint and validates result.
|
||||
///
|
||||
/// [expression] is a dart expression.
|
||||
/// [expectedResult] is the JSON for the returned remote object.
|
||||
/// [expectedError] is the error string if the error is expected.
|
||||
Future<void> check(
|
||||
{required String expression,
|
||||
dynamic expectedError,
|
||||
dynamic expectedResult}) async {
|
||||
assert(expectedError == null || expectedResult == null,
|
||||
'Cannot expect both an error and result.');
|
||||
|
||||
return await _whileRunning(body: () async {
|
||||
var evalResult = await _evaluateDartExpression(expression);
|
||||
|
||||
var error = evalResult.json['error'];
|
||||
if (error != null) {
|
||||
expect(
|
||||
expectedError,
|
||||
isNotNull,
|
||||
reason: 'Unexpected expression evaluation failure:\n$error',
|
||||
);
|
||||
expect(error, _matches(expectedError!));
|
||||
} else {
|
||||
expect(
|
||||
expectedResult,
|
||||
isNotNull,
|
||||
reason:
|
||||
'Unexpected expression evaluation success:\n${evalResult.json}',
|
||||
);
|
||||
var actual = await stringifyRemoteObject(evalResult);
|
||||
expect(actual, _matches(expectedResult!));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<wip.RemoteObject> _evaluateJsExpression(
|
||||
wip.DebuggerPausedEvent event,
|
||||
String expression, {
|
||||
|
@ -521,41 +597,94 @@ class ExpressionEvaluationTestDriver {
|
|||
);
|
||||
}
|
||||
|
||||
Future<TestCompilationResult> _compileDartExpression(
|
||||
Future<TestCompilationResult> _compileDartExpressionInFrame(
|
||||
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);
|
||||
input: input,
|
||||
line: dartLine,
|
||||
column: 1,
|
||||
scope: scope,
|
||||
expression: expression,
|
||||
);
|
||||
}
|
||||
|
||||
Future<wip.RemoteObject> _evaluateDartExpression(
|
||||
Future<TestCompilationResult> _compileDartExpression(
|
||||
String expression) async {
|
||||
// Perform an incremental compile.
|
||||
return await compiler.compileExpression(
|
||||
input: input,
|
||||
line: 1,
|
||||
column: 1,
|
||||
scope: {},
|
||||
expression: expression,
|
||||
);
|
||||
}
|
||||
|
||||
Future<wip.RemoteObject> _evaluateDartExpressionInFrame(
|
||||
wip.DebuggerPausedEvent event,
|
||||
String expression,
|
||||
int dartLine, {
|
||||
bool returnByValue = false,
|
||||
}) async {
|
||||
var frame = event.getCallFrames().first;
|
||||
var result = await _compileDartExpression(frame, expression, dartLine);
|
||||
var result = await _compileDartExpressionInFrame(
|
||||
frame,
|
||||
expression,
|
||||
dartLine,
|
||||
);
|
||||
|
||||
if (!result.isSuccess) {
|
||||
setup.diagnosticMessages.clear();
|
||||
setup.errors.clear();
|
||||
return wip.RemoteObject({'error': result.result});
|
||||
return _createCompilationError(result);
|
||||
}
|
||||
|
||||
// Evaluate the compiled expression.
|
||||
return await debugger.evaluateOnCallFrame(
|
||||
frame.callFrameId,
|
||||
result.result!,
|
||||
returnByValue: returnByValue,
|
||||
);
|
||||
try {
|
||||
return await debugger.evaluateOnCallFrame(
|
||||
frame.callFrameId,
|
||||
result.result!,
|
||||
returnByValue: returnByValue,
|
||||
);
|
||||
} on wip.ExceptionDetails catch (e) {
|
||||
return _createRuntimeError(e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<wip.RemoteObject> _evaluateDartExpression(
|
||||
String expression, {
|
||||
bool returnByValue = false,
|
||||
}) async {
|
||||
var result = await _compileDartExpression(expression);
|
||||
if (!result.isSuccess) {
|
||||
return _createCompilationError(result);
|
||||
}
|
||||
|
||||
// Find the execution context for the dart app.
|
||||
final context = await executionContext.id;
|
||||
|
||||
// Evaluate the compiled expression.
|
||||
try {
|
||||
return await runtime.evaluate(
|
||||
result.result!,
|
||||
contextId: context,
|
||||
returnByValue: returnByValue,
|
||||
);
|
||||
} on wip.ExceptionDetails catch (e) {
|
||||
return _createRuntimeError(e);
|
||||
}
|
||||
}
|
||||
|
||||
wip.RemoteObject _createCompilationError(TestCompilationResult result) {
|
||||
setup.diagnosticMessages.clear();
|
||||
setup.errors.clear();
|
||||
return wip.RemoteObject({'error': result.result});
|
||||
}
|
||||
|
||||
wip.RemoteObject _createRuntimeError(wip.ExceptionDetails error) {
|
||||
return wip.RemoteObject({'error': error.exception!.description});
|
||||
}
|
||||
|
||||
/// Generate simple string representation of a RemoteObject that closely
|
||||
|
@ -664,6 +793,59 @@ class ExpressionEvaluationTestDriver {
|
|||
}
|
||||
}
|
||||
|
||||
/// The execution context in which to do remote evaluations.
|
||||
///
|
||||
/// Copied and simplified from webdev/dwds/lib/src/debugging/execution_context.dart.
|
||||
class ExecutionContext {
|
||||
static const _nextContextTimeoutDuration = Duration(milliseconds: 100);
|
||||
final wip.WipRuntime _runtime;
|
||||
|
||||
/// Contexts that may contain a Dart application.
|
||||
late StreamQueue<int> _contexts;
|
||||
|
||||
int? _id;
|
||||
|
||||
Future<int> get id async {
|
||||
if (_id != null) return _id!;
|
||||
while (await _contexts.hasNext.timeout(
|
||||
_nextContextTimeoutDuration,
|
||||
onTimeout: () => false,
|
||||
)) {
|
||||
final context = await _contexts.next;
|
||||
printOnFailure('Trying context: $context');
|
||||
try {
|
||||
// Confirm the context belongs to a dart application.
|
||||
final result = await _runtime.evaluate(
|
||||
'dartApplication',
|
||||
contextId: context,
|
||||
returnByValue: true,
|
||||
);
|
||||
if (result.value != null) {
|
||||
printOnFailure('Found dart app context: $context');
|
||||
_id = context;
|
||||
break;
|
||||
}
|
||||
} catch (_) {
|
||||
printOnFailure('Failed context: $context, trying again...');
|
||||
}
|
||||
}
|
||||
|
||||
if (_id == null) {
|
||||
throw StateError('No context with the running Dart application.');
|
||||
}
|
||||
return _id!;
|
||||
}
|
||||
|
||||
ExecutionContext(this._runtime) {
|
||||
final contextController = StreamController<int>();
|
||||
_runtime.onExecutionContextsCleared.listen((_) => _id = null);
|
||||
_runtime.onExecutionContextDestroyed.listen((_) => _id = null);
|
||||
_runtime.onExecutionContextCreated
|
||||
.listen((e) => contextController.add(e.id));
|
||||
_contexts = StreamQueue(contextController.stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// Filters the provided frame scopes to those that are pertinent for Dart
|
||||
/// debugging.
|
||||
///
|
||||
|
|
|
@ -417,7 +417,7 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('typeName (int type)', () async {
|
||||
var typeName = await driver.evaluateDartExpression(
|
||||
var typeName = await driver.evaluateDartExpressionInFrame(
|
||||
breakpointId: 'BP',
|
||||
expression: 'xType.toString()',
|
||||
);
|
||||
|
@ -425,7 +425,7 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('typeName (base type)', () async {
|
||||
var typeName = await driver.evaluateDartExpression(
|
||||
var typeName = await driver.evaluateDartExpressionInFrame(
|
||||
breakpointId: 'BP',
|
||||
expression: 'baseType.toString()',
|
||||
);
|
||||
|
@ -433,7 +433,7 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('getObjectMetadata (int type)', () async {
|
||||
var typeName = await driver.evaluateDartExpression(
|
||||
var typeName = await driver.evaluateDartExpressionInFrame(
|
||||
breakpointId: 'BP',
|
||||
expression: 'xType.toString()',
|
||||
);
|
||||
|
@ -452,7 +452,7 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('getObjectMetadata (base type)', () async {
|
||||
var typeName = await driver.evaluateDartExpression(
|
||||
var typeName = await driver.evaluateDartExpressionInFrame(
|
||||
breakpointId: 'BP',
|
||||
expression: 'baseType.toString()',
|
||||
);
|
||||
|
@ -471,7 +471,7 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('getObjectMetadata (type)', () async {
|
||||
var typeName = await driver.evaluateDartExpression(
|
||||
var typeName = await driver.evaluateDartExpressionInFrame(
|
||||
breakpointId: 'BP',
|
||||
expression: 'baseTypeType.toString()',
|
||||
);
|
||||
|
@ -490,7 +490,7 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('getObjectMetadata (Set type)', () async {
|
||||
var typeName = await driver.evaluateDartExpression(
|
||||
var typeName = await driver.evaluateDartExpressionInFrame(
|
||||
breakpointId: 'BP',
|
||||
expression: 'setType.toString()',
|
||||
);
|
||||
|
@ -509,7 +509,7 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('getObjectMetadata (List type)', () async {
|
||||
var typeName = await driver.evaluateDartExpression(
|
||||
var typeName = await driver.evaluateDartExpressionInFrame(
|
||||
breakpointId: 'BP',
|
||||
expression: 'listType.toString()',
|
||||
);
|
||||
|
@ -528,7 +528,7 @@ void runSharedTests(
|
|||
});
|
||||
|
||||
test('getObjectMetadata (Map type)', () async {
|
||||
var typeName = await driver.evaluateDartExpression(
|
||||
var typeName = await driver.evaluateDartExpressionInFrame(
|
||||
breakpointId: 'BP',
|
||||
expression: 'mapType.toString()',
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue