Prepare DWDS 23.0.0+1 hotfix (#2355)
diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md
index acb5cba..3d524ed 100644
--- a/dwds/CHANGELOG.md
+++ b/dwds/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 23.0.0+1
+
+- Filter out internal type properties from the new DDC type system. - [#2348](https://github.com/dart-lang/webdev/pull/2348)
+- Fix errors on displaying getters when debugging in VS Code. - [#2343](https://github.com/dart-lang/webdev/pull/2343)
+
## 23.0.0
- Restructure `LoadStrategy` to provide build settings. - [#2270](https://github.com/dart-lang/webdev/pull/2270)
- Add `FrontendServerLegacyStrategyProvider` and update bootstrap generation logic for `LegacyStrategy` - [#2285](https://github.com/dart-lang/webdev/pull/2285)
diff --git a/dwds/lib/src/debugging/dart_scope.dart b/dwds/lib/src/debugging/dart_scope.dart
index 341af89..2b64b0a 100644
--- a/dwds/lib/src/debugging/dart_scope.dart
+++ b/dwds/lib/src/debugging/dart_scope.dart
@@ -20,11 +20,11 @@
final previousDdcTemporaryVariableRegExp =
RegExp(r'^(t[0-9]+\$?[0-9]*|__t[\$\w*]+)$');
-/// Find the visible Dart properties from a JS Scope Chain, coming from the
+/// Find the visible Dart variables from a JS Scope Chain, coming from the
/// scopeChain attribute of a Chrome CallFrame corresponding to [frame].
///
/// See chromedevtools.github.io/devtools-protocol/tot/Debugger#type-CallFrame.
-Future<List<Property>> visibleProperties({
+Future<List<Property>> visibleVariables({
required AppInspectorInterface inspector,
required WipCallFrame frame,
}) async {
diff --git a/dwds/lib/src/debugging/debugger.dart b/dwds/lib/src/debugging/debugger.dart
index cdb4a14..b5af290 100644
--- a/dwds/lib/src/debugging/debugger.dart
+++ b/dwds/lib/src/debugging/debugger.dart
@@ -405,7 +405,7 @@
Future<List<BoundVariable>> variablesFor(WipCallFrame frame) async {
// TODO(alanknight): Can these be moved to dart_scope.dart?
final properties =
- await visibleProperties(inspector: inspector, frame: frame);
+ await visibleVariables(inspector: inspector, frame: frame);
final boundVariables = await Future.wait(
properties.map(_boundVariable),
);
diff --git a/dwds/lib/src/debugging/inspector.dart b/dwds/lib/src/debugging/inspector.dart
index 660ee0d..5880513 100644
--- a/dwds/lib/src/debugging/inspector.dart
+++ b/dwds/lib/src/debugging/inspector.dart
@@ -601,9 +601,18 @@
);
return jsProperties
.map<Property>((each) => Property(each as Map<String, dynamic>))
+ .where(_isVisibleProperty)
.toList();
}
+ bool _isVisibleProperty(Property property) {
+ // Filter out any RTI objects from the new DDC type system. See:
+ // https://github.com/dart-lang/webdev/issues/2316
+ final isRtiObject =
+ property.value?.className?.startsWith('dart_rti.Rti') ?? false;
+ return !isRtiObject;
+ }
+
/// Calculate the number of available elements in the range.
static int _calculateRangeCount({
int? count,
diff --git a/dwds/lib/src/debugging/instance.dart b/dwds/lib/src/debugging/instance.dart
index 9db9c78..27fa81a 100644
--- a/dwds/lib/src/debugging/instance.dart
+++ b/dwds/lib/src/debugging/instance.dart
@@ -238,29 +238,49 @@
final objectId = remoteObject.objectId;
if (objectId == null) return null;
+ final fields = await _getInstanceFields(
+ metaData,
+ remoteObject,
+ offset: offset,
+ count: count,
+ );
+
+ final result = Instance(
+ kind: InstanceKind.kPlainInstance,
+ id: objectId,
+ identityHashCode: remoteObject.objectId.hashCode,
+ classRef: metaData.classRef,
+ fields: fields,
+ );
+ return result;
+ }
+
+ Future<List<BoundField>> _getInstanceFields(
+ ClassMetaData metaData,
+ RemoteObject remoteObject, {
+ int? offset,
+ int? count,
+ }) async {
+ final objectId = remoteObject.objectId;
+ if (objectId == null) throw StateError('Object id is null for instance');
+
final properties = await inspector.getProperties(
objectId,
offset: offset,
count: count,
length: metaData.length,
);
+
final dartProperties = await _dartFieldsFor(properties, remoteObject);
- var boundFields = await Future.wait(
+ final boundFields = await Future.wait(
dartProperties
.map<Future<BoundField>>((p) => _fieldFor(p, metaData.classRef)),
);
- boundFields = boundFields
+
+ return boundFields
.where((bv) => inspector.isDisplayableObject(bv.value))
.toList()
..sort(_compareBoundFields);
- final result = Instance(
- kind: InstanceKind.kPlainInstance,
- id: objectId,
- identityHashCode: remoteObject.objectId.hashCode,
- classRef: metaData.classRef,
- fields: boundFields,
- );
- return result;
}
int _compareBoundFields(BoundField a, BoundField b) {
@@ -700,7 +720,13 @@
final objectId = remoteObject.objectId;
if (objectId == null) return null;
- final fields = await _typeFields(metaData.classRef, remoteObject);
+ final fields = await _getInstanceFields(
+ metaData,
+ remoteObject,
+ offset: offset,
+ count: count,
+ );
+
return Instance(
identityHashCode: objectId.hashCode,
kind: InstanceKind.kType,
@@ -714,36 +740,6 @@
);
}
- /// The field types for a Dart RecordType.
- ///
- /// Returns a range of [count] field types, if available, starting from
- /// the [offset].
- ///
- /// If [offset] is `null`, assumes 0 offset.
- /// If [count] is `null`, return all field types starting from the offset.
- Future<List<BoundField>> _typeFields(
- ClassRef classRef,
- RemoteObject type,
- ) async {
- // Present the type as an instance of `core.Type` class and
- // hide the internal implementation.
- final expression = _jsRuntimeFunctionCall('getTypeFields(this)');
-
- final result = await inspector.jsCallFunctionOn(type, expression, []);
- final hashCodeObject = await inspector.loadField(result, 'hashCode');
- final runtimeTypeObject = await inspector.loadField(result, 'runtimeType');
-
- final properties = [
- Property({'name': 'hashCode', 'value': hashCodeObject}),
- Property({'name': 'runtimeType', 'value': runtimeTypeObject}),
- ];
-
- final boundFields = await Future.wait(
- properties.map<Future<BoundField>>((p) => _fieldFor(p, classRef)),
- );
- return boundFields;
- }
-
/// Return the available count of elements in the requested range.
/// Return `null` if the range includes the whole object.
/// [count] is the range length requested by the `getObject` call.
diff --git a/dwds/lib/src/debugging/metadata/provider.dart b/dwds/lib/src/debugging/metadata/provider.dart
index 677bde8..62e2455 100644
--- a/dwds/lib/src/debugging/metadata/provider.dart
+++ b/dwds/lib/src/debugging/metadata/provider.dart
@@ -44,6 +44,7 @@
'dart:_js_primitives',
'dart:_metadata',
'dart:_native_typed_data',
+ 'dart:_rti',
'dart:async',
'dart:collection',
'dart:convert',
diff --git a/dwds/lib/src/services/chrome_proxy_service.dart b/dwds/lib/src/services/chrome_proxy_service.dart
index 913db0f..bd77c47 100644
--- a/dwds/lib/src/services/chrome_proxy_service.dart
+++ b/dwds/lib/src/services/chrome_proxy_service.dart
@@ -618,7 +618,35 @@
await isCompilerInitialized;
_checkIsolate('evaluate', isolateId);
- final library = await inspector.getLibrary(targetId);
+ late Obj object;
+ try {
+ object = await inspector.getObject(targetId);
+ } catch (_) {
+ return ErrorRef(
+ kind: 'error',
+ message: 'Evaluate is called on an unsupported target:'
+ '$targetId',
+ id: createId(),
+ );
+ }
+
+ final library =
+ object is Library ? object : inspector.isolate.rootLib;
+
+ if (object is Instance) {
+ // Evaluate is called on a target - convert this to a dart
+ // expression and scope by adding a target variable to the
+ // expression and the scope, for example:
+ //
+ // Library: 'package:hello_world/main.dart'
+ // Expression: 'hashCode' => 'x.hashCode'
+ // Scope: {} => { 'x' : targetId }
+
+ final target = _newVariableForScope(scope);
+ expression = '$target.$expression';
+ scope = (scope ?? {})..addAll({target: targetId});
+ }
+
return await _getEvaluationResult(
isolateId,
() => evaluator.evaluateExpression(
@@ -631,7 +659,7 @@
);
}
throw RPCError(
- 'evaluateInFrame',
+ 'evaluate',
RPCErrorKind.kInvalidRequest.code,
'Expression evaluation is not supported for this configuration.',
);
@@ -640,6 +668,15 @@
);
}
+ String _newVariableForScope(Map<String, String>? scope) {
+ // Find a new variable not in scope.
+ var candidate = 'x';
+ while (scope?.containsKey(candidate) ?? false) {
+ candidate += '\$1';
+ }
+ return candidate;
+ }
+
@override
Future<Response> evaluateInFrame(
String isolateId,
diff --git a/dwds/test/evaluate_common.dart b/dwds/test/evaluate_common.dart
index 61025b5..2125427 100644
--- a/dwds/test/evaluate_common.dart
+++ b/dwds/test/evaluate_common.dart
@@ -320,7 +320,7 @@
await getInstanceRef(event.topFrame!.index!, 'stream');
final instance = await getInstance(instanceRef);
- expect(instance, matchInstance('_AsBroadcastStream<int>'));
+ expect(instance, matchInstanceClassName('_AsBroadcastStream<int>'));
});
});
@@ -667,24 +667,24 @@
tearDown(() async {});
evaluate(
- libraryId,
+ targetId,
expr, {
scope,
}) async =>
await context.service.evaluate(
isolateId,
- libraryId,
+ targetId,
expr,
scope: scope,
);
getInstanceRef(
- libraryId,
+ targetId,
expr, {
scope,
}) async {
final result = await evaluate(
- libraryId,
+ targetId,
expr,
scope: scope,
);
@@ -698,6 +698,28 @@
return isolate.rootLib!.id!;
}
+ test(
+ 'RecordType getters',
+ () async {
+ final libraryId = getRootLibraryId();
+
+ final type = await getInstanceRef(libraryId, '(0,1).runtimeType');
+ final result = await getInstanceRef(type.id, 'hashCode');
+
+ expect(result, matchInstanceRefKind('Double'));
+ },
+ skip: 'https://github.com/dart-lang/sdk/issues/54609',
+ );
+
+ test('Object getters', () async {
+ final libraryId = getRootLibraryId();
+
+ final type = await getInstanceRef(libraryId, 'Object()');
+ final result = await getInstanceRef(type.id, 'hashCode');
+
+ expect(result, matchInstanceRefKind('Double'));
+ });
+
test('with scope', () async {
final libraryId = getRootLibraryId();
@@ -761,15 +783,13 @@
final evaluation2 = evaluate(libraryId, 'MainClass(1,1).toString()');
final results = await Future.wait([evaluation1, evaluation2]);
-
expect(
results[0],
- matchErrorRef(contains('No batch result object ID')),
+ matchErrorRef(
+ contains('Evaluate is called on an unsupported target'),
+ ),
);
- expect(
- results[1],
- matchErrorRef(contains('No batch result object ID')),
- );
+ expect(results[1], matchInstanceRef('1, 1'));
});
test('with scope override', () async {
@@ -900,13 +920,20 @@
return result.json['result']['breakpointId'];
}
-Matcher matchInstanceRef(dynamic value) => isA<InstanceRef>().having(
- (instance) => instance.valueAsString,
- 'valueAsString',
- value,
+Matcher matchInstanceRefKind(String kind) =>
+ isA<InstanceRef>().having((instance) => instance.kind, 'kind', kind);
+
+Matcher matchInstanceRef(dynamic value) => isA<InstanceRef>()
+ .having((instance) => instance.valueAsString, 'valueAsString', value);
+
+Matcher matchInstanceClassName(dynamic className) => isA<Instance>().having(
+ (instance) => instance.classRef!.name,
+ 'class name',
+ className,
);
-Matcher matchInstance(dynamic className) => isA<Instance>().having(
+Matcher matchInstanceRefClassName(dynamic className) =>
+ isA<InstanceRef>().having(
(instance) => instance.classRef!.name,
'class name',
className,
diff --git a/dwds/test/inspector_test.dart b/dwds/test/inspector_test.dart
index b17fca8..e84cd64 100644
--- a/dwds/test/inspector_test.dart
+++ b/dwds/test/inspector_test.dart
@@ -11,7 +11,6 @@
import 'package:dwds/src/utilities/conversions.dart';
import 'package:test/test.dart';
import 'package:test_common/test_sdk_configuration.dart';
-import 'package:test_common/utilities.dart';
import 'package:vm_service/vm_service.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
@@ -161,10 +160,6 @@
final names =
properties.map((p) => p.name).where((x) => x != '__proto__').toList();
final expected = [
- if (dartSdkIsAtLeast(
- newDdcTypeSystemVersion,
- ))
- '\$ti',
'_privateField',
'abstractField',
'closure',
diff --git a/dwds/test/instances/common/instance_inspection_common.dart b/dwds/test/instances/common/instance_inspection_common.dart
index 5a0a499..312c6a5 100644
--- a/dwds/test/instances/common/instance_inspection_common.dart
+++ b/dwds/test/instances/common/instance_inspection_common.dart
@@ -189,14 +189,32 @@
final instanceId = instanceRef.id!;
expect(await getObject(instanceId), matchListInstance(type: 'int'));
- expect(await getFields(instanceRef), [0, 1, 2]);
- expect(await getFields(instanceRef, offset: 1, count: 0), []);
- expect(await getFields(instanceRef, offset: 0), [0, 1, 2]);
- expect(await getFields(instanceRef, offset: 0, count: 1), [0]);
- expect(await getFields(instanceRef, offset: 1), [1, 2]);
- expect(await getFields(instanceRef, offset: 1, count: 1), [1]);
- expect(await getFields(instanceRef, offset: 1, count: 3), [1, 2]);
- expect(await getFields(instanceRef, offset: 3, count: 3), []);
+ expect(
+ await getFields(instanceRef),
+ {0: 0.0, 1: 1.0, 2: 2.0},
+ );
+ expect(await getFields(instanceRef, offset: 1, count: 0), {});
+ expect(
+ await getFields(instanceRef, offset: 0),
+ {0: 0.0, 1: 1.0, 2: 2.0},
+ );
+ expect(
+ await getFields(instanceRef, offset: 0, count: 1),
+ {0: 0.0},
+ );
+ expect(
+ await getFields(instanceRef, offset: 1),
+ {0: 1.0, 1: 2.0},
+ );
+ expect(
+ await getFields(instanceRef, offset: 1, count: 1),
+ {0: 1.0},
+ );
+ expect(
+ await getFields(instanceRef, offset: 1, count: 3),
+ {0: 1.0, 1: 2.0},
+ );
+ expect(await getFields(instanceRef, offset: 3, count: 3), {});
});
});
@@ -284,13 +302,28 @@
matchSetInstance(type: '_HashSet<int>'),
);
- expect(await getFields(instanceRef), [1, 4, 5, 7]);
- expect(await getFields(instanceRef, offset: 0), [1, 4, 5, 7]);
- expect(await getFields(instanceRef, offset: 1, count: 2), [4, 5]);
- expect(await getFields(instanceRef, offset: 2), [5, 7]);
- expect(await getFields(instanceRef, offset: 2, count: 10), [5, 7]);
- expect(await getFields(instanceRef, offset: 1, count: 0), []);
- expect(await getFields(instanceRef, offset: 10, count: 2), []);
+ expect(
+ await getFields(instanceRef),
+ {0: 1.0, 1: 4.0, 2: 5.0, 3: 7.0},
+ );
+ expect(
+ await getFields(instanceRef, offset: 0),
+ {0: 1.0, 1: 4.0, 2: 5.0, 3: 7.0},
+ );
+ expect(
+ await getFields(instanceRef, offset: 1, count: 2),
+ {0: 4.0, 1: 5.0},
+ );
+ expect(
+ await getFields(instanceRef, offset: 2),
+ {0: 5.0, 1: 7.0},
+ );
+ expect(
+ await getFields(instanceRef, offset: 2, count: 10),
+ {0: 5.0, 1: 7.0},
+ );
+ expect(await getFields(instanceRef, offset: 1, count: 0), {});
+ expect(await getFields(instanceRef, offset: 10, count: 2), {});
});
});
diff --git a/dwds/test/instances/common/patterns_inspection_common.dart b/dwds/test/instances/common/patterns_inspection_common.dart
index b4e14c4..9a303a6 100644
--- a/dwds/test/instances/common/patterns_inspection_common.dart
+++ b/dwds/test/instances/common/patterns_inspection_common.dart
@@ -105,7 +105,7 @@
final frame = event.topFrame!;
final frameIndex = frame.index!;
final instanceRef = await getInstanceRef(frameIndex, 'obj');
- expect(await getFields(instanceRef), [0, 1]);
+ expect(await getFields(instanceRef), {0: 0.0, 1: 1.0});
expect(await getFrameVariables(frame), {
'obj': matchListInstance(type: 'int'),
diff --git a/dwds/test/instances/common/record_type_inspection_common.dart b/dwds/test/instances/common/record_type_inspection_common.dart
index 9e5eb91..7aca569 100644
--- a/dwds/test/instances/common/record_type_inspection_common.dart
+++ b/dwds/test/instances/common/record_type_inspection_common.dart
@@ -5,6 +5,7 @@
import 'package:test/test.dart';
import 'package:test_common/logging.dart';
import 'package:test_common/test_sdk_configuration.dart';
+import 'package:test_common/utilities.dart';
import 'package:vm_service/vm_service.dart';
import '../../fixtures/context.dart';
@@ -43,9 +44,17 @@
getDisplayedFields(InstanceRef ref) =>
testInspector.getDisplayedFields(isolateId, ref);
+ getDisplayedGetters(InstanceRef ref) =>
+ testInspector.getDisplayedGetters(isolateId, ref);
+
getElements(String instanceId) =>
testInspector.getElements(isolateId, instanceId);
+ final matchDisplayedTypeObjectGetters = {
+ 'hashCode': matches('[0-9]*'),
+ 'runtimeType': matchTypeClassName,
+ };
+
group('$compilationMode |', () {
setUpAll(() async {
setCurrentLogWriter(debug: debug);
@@ -79,17 +88,23 @@
tearDown(() => service.resume(isolateId));
test('simple record type', () async {
- await onBreakPoint('printSimpleLocalRecord', (event) async {
- final frame = event.topFrame!.index!;
- final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
- final instanceId = instanceRef.id!;
+ await onBreakPoint(
+ 'printSimpleLocalRecord',
+ (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
+ final instanceId = instanceRef.id!;
- expect(instanceRef, matchRecordTypeInstanceRef(length: 2));
- expect(await getObject(instanceId), matchRecordTypeInstance(length: 2));
+ expect(instanceRef, matchRecordTypeInstanceRef(length: 2));
+ expect(
+ await getObject(instanceId),
+ matchRecordTypeInstance(length: 2),
+ );
- final classId = instanceRef.classRef!.id;
- expect(await getObject(classId), matchRecordTypeClass);
- });
+ final classId = instanceRef.classRef!.id;
+ expect(await getObject(classId), matchRecordTypeClass);
+ },
+ );
});
test('simple record type elements', () async {
@@ -104,11 +119,27 @@
);
expect(
await getDisplayedFields(instanceRef),
- ['bool', 'int'],
+ {1: 'bool', 2: 'int'},
);
});
});
+ test(
+ 'simple record type getters',
+ () async {
+ await onBreakPoint('printSimpleLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
+
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
+
test('simple record type display', () async {
await onBreakPoint('printSimpleLocalRecord', (event) async {
final frame = event.topFrame!.index!;
@@ -126,19 +157,25 @@
});
});
- test('complex record type', () async {
- await onBreakPoint('printComplexLocalRecord', (event) async {
- final frame = event.topFrame!.index!;
- final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
- final instanceId = instanceRef.id!;
+ test(
+ 'complex record type',
+ () async {
+ await onBreakPoint('printComplexLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
+ final instanceId = instanceRef.id!;
- expect(instanceRef, matchRecordTypeInstanceRef(length: 3));
- expect(await getObject(instanceId), matchRecordTypeInstance(length: 3));
+ expect(instanceRef, matchRecordTypeInstanceRef(length: 3));
+ expect(
+ await getObject(instanceId),
+ matchRecordTypeInstance(length: 3),
+ );
- final classId = instanceRef.classRef!.id;
- expect(await getObject(classId), matchRecordTypeClass);
- });
- });
+ final classId = instanceRef.classRef!.id;
+ expect(await getObject(classId), matchRecordTypeClass);
+ });
+ },
+ );
test('complex record type elements', () async {
await onBreakPoint('printComplexLocalRecord', (event) async {
@@ -156,11 +193,27 @@
);
expect(
await getDisplayedFields(instanceRef),
- ['bool', 'int', 'IdentityMap<String, int>'],
+ {1: 'bool', 2: 'int', 3: 'IdentityMap<String, int>'},
);
});
});
+ test(
+ 'complex record type getters',
+ () async {
+ await onBreakPoint('printComplexLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
+
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
+
test('complex record type display', () async {
await onBreakPoint('printComplexLocalRecord', (event) async {
final frame = event.topFrame!.index!;
@@ -185,14 +238,17 @@
final instanceId = instanceRef.id!;
expect(instanceRef, matchRecordTypeInstanceRef(length: 3));
- expect(await getObject(instanceId), matchRecordTypeInstance(length: 3));
+ expect(
+ await getObject(instanceId),
+ matchRecordTypeInstance(length: 3),
+ );
final classId = instanceRef.classRef!.id;
expect(await getObject(classId), matchRecordTypeClass);
});
});
- test('complex record type with named fields elements', () async {
+ test('complex record type with named fields elements', () async {
await onBreakPoint('printComplexNamedLocalRecord', (event) async {
final frame = event.topFrame!.index!;
final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
@@ -209,11 +265,27 @@
expect(
await getDisplayedFields(instanceRef),
- ['bool', 'int', 'IdentityMap<String, int>'],
+ {1: 'bool', 2: 'int', 'array': 'IdentityMap<String, int>'},
);
});
});
+ test(
+ 'complex record type with named fields getters',
+ () async {
+ await onBreakPoint('printComplexNamedLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
+
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
+
test('complex record type with named fields display', () async {
await onBreakPoint('printComplexNamedLocalRecord', (event) async {
final frame = event.topFrame!.index!;
@@ -231,19 +303,25 @@
});
});
- test('nested record type', () async {
- await onBreakPoint('printNestedLocalRecord', (event) async {
- final frame = event.topFrame!.index!;
- final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
- final instanceId = instanceRef.id!;
+ test(
+ 'nested record type',
+ () async {
+ await onBreakPoint('printNestedLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
+ final instanceId = instanceRef.id!;
- expect(instanceRef, matchRecordTypeInstanceRef(length: 2));
- expect(await getObject(instanceId), matchRecordTypeInstance(length: 2));
+ expect(instanceRef, matchRecordTypeInstanceRef(length: 2));
+ expect(
+ await getObject(instanceId),
+ matchRecordTypeInstance(length: 2),
+ );
- final classId = instanceRef.classRef!.id;
- expect(await getObject(classId), matchRecordTypeClass);
- });
- });
+ final classId = instanceRef.classRef!.id;
+ expect(await getObject(classId), matchRecordTypeClass);
+ });
+ },
+ );
test('nested record type elements', () async {
await onBreakPoint('printNestedLocalRecord', (event) async {
@@ -262,15 +340,36 @@
);
expect(
await getDisplayedFields(instanceRef),
- ['bool', '(bool, int)'],
+ {1: 'bool', 2: '(bool, int)'},
);
expect(
await getDisplayedFields(elements[1]),
- ['bool', 'int'],
+ {1: 'bool', 2: 'int'},
);
});
});
+ test(
+ 'nested record type getters',
+ () async {
+ await onBreakPoint('printNestedLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
+ final elements = await getElements(instanceRef.id!);
+
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ expect(
+ await getDisplayedGetters(elements[1]),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
+
test('nested record type display', () async {
await onBreakPoint('printNestedLocalRecord', (event) async {
final frame = event.topFrame!.index!;
@@ -320,36 +419,61 @@
);
expect(
await getDisplayedFields(instanceRef),
- ['bool', '(bool, int)'],
+ {1: 'bool', 'inner': '(bool, int)'},
);
+
expect(
await getDisplayedFields(elements[1]),
- ['bool', 'int'],
+ {1: 'bool', 2: 'int'},
);
});
});
- test('nested record type with named fields display', () async {
- await onBreakPoint('printNestedNamedLocalRecord', (event) async {
- final frame = event.topFrame!.index!;
- final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
- final instance = await getObject(instanceRef.id!);
- final typeClassId = instance.classRef!.id;
+ test(
+ 'nested record type with named fields getters',
+ () async {
+ await onBreakPoint('printNestedNamedLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
+ final elements = await getElements(instanceRef.id!);
- expect(await getObject(typeClassId), matchRecordTypeClass);
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ expect(
+ await getDisplayedGetters(elements[1]),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
- final typeStringRef =
- await getInstanceRef(frame, 'record.runtimeType.toString()');
- final typeStringId = typeStringRef.id!;
+ test(
+ 'nested record type with named fields display',
+ () async {
+ await onBreakPoint('printNestedNamedLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, 'record.runtimeType');
+ final instance = await getObject(instanceRef.id!);
+ final typeClassId = instance.classRef!.id;
- expect(
- await getObject(typeStringId),
- matchPrimitiveInstance(
- kind: InstanceKind.kString,
- value: '(bool, {(bool, int) inner})',
- ),
- );
- });
- });
+ expect(await getObject(typeClassId), matchRecordTypeClass);
+
+ final typeStringRef =
+ await getInstanceRef(frame, 'record.runtimeType.toString()');
+ final typeStringId = typeStringRef.id!;
+
+ expect(
+ await getObject(typeStringId),
+ matchPrimitiveInstance(
+ kind: InstanceKind.kString,
+ value: '(bool, {(bool, int) inner})',
+ ),
+ );
+ });
+ },
+ );
});
}
diff --git a/dwds/test/instances/common/test_inspector.dart b/dwds/test/instances/common/test_inspector.dart
index 1771461..9276820 100644
--- a/dwds/test/instances/common/test_inspector.dart
+++ b/dwds/test/instances/common/test_inspector.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:test/test.dart';
+import 'package:test_common/utilities.dart';
import 'package:vm_service/vm_service.dart';
import '../../fixtures/context.dart';
@@ -42,7 +43,7 @@
}
}
- Future<dynamic> getFields(
+ Future<Map<dynamic, Object?>> getFields(
String isolateId,
InstanceRef instanceRef, {
int? offset,
@@ -87,7 +88,7 @@
depth--;
}
if (depth == 0) {
- return elements == null ? fieldRefs : fieldRefs.values.toList();
+ return fieldRefs;
}
final fieldValues = <dynamic, Object?>{};
@@ -99,7 +100,27 @@
depth: depth,
);
}
- return elements == null ? fieldValues : fieldValues.values.toList();
+ return fieldValues;
+ }
+
+ Future<Map<String, InstanceRef>> getGetters(
+ String isolateId,
+ InstanceRef instanceRef,
+ ) async {
+ final cls =
+ await service.getObject(isolateId, instanceRef.classRef!.id!) as Class;
+ final getters =
+ cls.functions?.where((f) => f.isGetter ?? false).toList() ?? [];
+
+ final results = await Future.wait([
+ for (var getter in getters)
+ service.evaluate(isolateId, instanceRef.id!, getter.name!),
+ ]);
+
+ return Map<String, InstanceRef>.fromIterables(
+ getters.map((e) => e.name!),
+ results.map((e) => e as InstanceRef),
+ );
}
Future<InstanceRef> getInstanceRef(
@@ -160,7 +181,7 @@
await service.invoke(isolateId, instanceId, 'toString', [])
as InstanceRef;
- Future<List<String?>> getDisplayedFields(
+ Future<Map<dynamic, String?>> getDisplayedFields(
String isolateId,
InstanceRef ref,
) async {
@@ -172,7 +193,22 @@
(await getDisplayedRef(isolateId, ref.id!)).valueAsString;
final fields = await Future.wait(fieldRefs.values.map(toStringValue));
- return fields.toList();
+ return Map<dynamic, String?>.fromIterables(fieldRefs.keys, fields);
+ }
+
+ Future<Map<dynamic, String?>> getDisplayedGetters(
+ String isolateId,
+ InstanceRef ref,
+ ) async {
+ final fieldRefs =
+ await getGetters(isolateId, ref) as Map<dynamic, InstanceRef>;
+
+ Future<String?> toStringValue(InstanceRef ref) async =>
+ ref.valueAsString ??
+ (await getDisplayedRef(isolateId, ref.id!)).valueAsString;
+
+ final fields = await Future.wait(fieldRefs.values.map(toStringValue));
+ return Map<dynamic, String?>.fromIterables(fieldRefs.keys, fields);
}
Future<List<Instance>> getElements(
@@ -216,7 +252,15 @@
.having((e) => e.classRef!, 'classRef', matchRecordClassRef);
Matcher matchRecordTypeInstanceRef({required int length}) => isA<InstanceRef>()
- .having((e) => e.kind, 'kind', InstanceKind.kRecordType)
+ .having(
+ (e) => e.kind,
+ 'kind',
+ // See https://github.com/dart-lang/sdk/commit/67e052d7e996be8ad9d02970117ffef07eab1c77.
+ // TODO() Can't compare edge verisons, wait for this to get to a dev release.
+ dartSdkIsAtLeast('3.4.0-edge.eeec4d36e3ea9b166da277a46f62d7d3b9ce645a')
+ ? InstanceKind.kType
+ : InstanceKind.kRecordType,
+ )
.having((e) => e.length, 'length', length)
.having((e) => e.classRef!, 'classRef', matchRecordTypeClassRef);
@@ -279,23 +323,35 @@
Matcher matchRecordClass =
matchClass(name: matchRecordClassName, libraryId: _dartCoreLibrary);
-Matcher matchRecordTypeClass =
- matchClass(name: matchRecordTypeClassName, libraryId: _dartRuntimeLibrary);
Matcher matchTypeClass =
matchClass(name: matchTypeClassName, libraryId: _dartCoreLibrary);
+/// TODO(annagrin): record type class is reported incorrectly
+/// in ddc https://github.com/dart-lang/sdk/issues/54609,
+/// remove when fixed.
+Matcher matchRecordTypeClass = anyOf(
+ matchTypeClass,
+ matchClass(name: matchRecordTypeClassName, libraryId: _dartRuntimeLibrary),
+);
+
Matcher matchClass({dynamic name, String? libraryId}) => isA<Class>()
.having((e) => e.name, 'class name', name)
.having((e) => e.library, 'library', matchLibraryRef(libraryId));
Matcher matchRecordClassRef =
matchClassRef(name: matchRecordClassName, libraryId: _dartCoreLibrary);
-Matcher matchRecordTypeClassRef = matchClassRef(
- name: matchRecordTypeClassName,
- libraryId: _dartRuntimeLibrary,
+
+/// TODO(annagrin): record type class is reported incorrectly
+/// in ddc https://github.com/dart-lang/sdk/issues/54609,
+/// remove when fixed.
+Matcher matchRecordTypeClassRef = anyOf(
+ matchTypeClassRef,
+ matchClassRef(name: matchRecordTypeClassName, libraryId: _dartRuntimeLibrary),
);
-Matcher matchTypeClassRef =
- matchClassRef(name: matchTypeClassName, libraryId: _dartCoreLibrary);
+Matcher matchTypeClassRef = matchClassRef(
+ name: matchTypeClassName,
+ libraryId: _dartCoreLibrary,
+);
Matcher matchListClassRef(String type) => matchClassRef(
name: matchListClassName(type),
libraryId: _matchListLibraryName,
@@ -329,19 +385,25 @@
}
final _dartCoreLibrary = 'dart:core';
-final _dartRuntimeLibrary = 'dart:_runtime';
final _dartInterceptorsLibrary = 'dart:_interceptors';
final _dartJsHelperLibrary = 'dart:_js_helper';
final _dartCollectionLibrary = 'dart:collection';
+final _dartRuntimeLibrary = 'dart:_runtime';
final matchRecordClassName = 'Record';
-final matchRecordTypeClassName = 'RecordType';
/// Match types for old and new type systems.
-/// - Old type system has `dart:_interceptors|List` and `dart:_runtime|_Type`.
-/// - New type system has `dart:_interceptors|JSArray` and `dart:core|Type`.
-/// TODO(annagrin): update when DDC enables new type system.
+/// - Old type system has
+/// - for arrays: `dart:_interceptors|List`
+/// - for type: `dart:_runtime|_Type`.
+/// - New type system has
+/// - for arrays: dart:_interceptors|JSArray`, and
+/// - for type: `dart:core|Type`.
+/// TODO(annagrin): remove old matchers when DDC enables new type system.
+/// TODO(annagrin): `matchTypeClassName` is reported incorrectly
+/// in ddc https://github.com/dart-lang/sdk/issues/54609,
final matchTypeClassName = anyOf(['Type', '_Type']);
+final matchRecordTypeClassName = 'RecordType';
Matcher matchListClassName(String elementType) =>
anyOf(['JSArray<$elementType>', 'List<$elementType>']);
diff --git a/dwds/test/instances/common/type_inspection_common.dart b/dwds/test/instances/common/type_inspection_common.dart
index aadf4a8..70b6f3e 100644
--- a/dwds/test/instances/common/type_inspection_common.dart
+++ b/dwds/test/instances/common/type_inspection_common.dart
@@ -5,6 +5,7 @@
import 'package:test/test.dart';
import 'package:test_common/logging.dart';
import 'package:test_common/test_sdk_configuration.dart';
+import 'package:test_common/utilities.dart';
import 'package:vm_service/vm_service.dart';
import '../../fixtures/context.dart';
@@ -40,6 +41,9 @@
getDisplayedFields(instanceRef) =>
testInspector.getDisplayedFields(isolateId, instanceRef);
+ getDisplayedGetters(instanceRef) =>
+ testInspector.getDisplayedGetters(isolateId, instanceRef);
+
getInstanceRef(frame, expression) =>
testInspector.getInstanceRef(isolateId, frame, expression);
@@ -55,15 +59,15 @@
getElements(String instanceId) =>
testInspector.getElements(isolateId, instanceId);
- final matchTypeObject = {
- 'hashCode': matchPrimitiveInstanceRef(kind: InstanceKind.kDouble),
- 'runtimeType': matchTypeInstanceRef(matchTypeClassName),
+ final matchTypeObjectFields = {};
+
+ final matchDisplayedTypeObjectFields = {};
+
+ final matchDisplayedTypeObjectGetters = {
+ 'hashCode': matches('[0-9]*'),
+ 'runtimeType': matchTypeClassName,
};
- final matchDisplayedTypeObject = [
- matches('[0-9]*'),
- matchTypeClassName,
- ];
group('$compilationMode |', () {
setUpAll(() async {
setCurrentLogWriter(debug: debug);
@@ -108,11 +112,33 @@
final classId = instanceRef.classRef!.id;
expect(await getObject(classId), matchTypeClass);
- expect(await getFields(instanceRef, depth: 1), matchTypeObject);
- expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject);
+ expect(
+ await getFields(instanceRef, depth: 1),
+ matchTypeObjectFields,
+ );
+ expect(
+ await getDisplayedFields(instanceRef),
+ matchDisplayedTypeObjectFields,
+ );
});
});
+ test(
+ 'String type getters',
+ () async {
+ await onBreakPoint('printSimpleLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, "'1'.runtimeType");
+
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
+
test('int type', () async {
await onBreakPoint('printSimpleLocalRecord', (event) async {
final frame = event.topFrame!.index!;
@@ -125,11 +151,33 @@
final classId = instanceRef.classRef!.id;
expect(await getObject(classId), matchTypeClass);
- expect(await getFields(instanceRef, depth: 1), matchTypeObject);
- expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject);
+ expect(
+ await getFields(instanceRef, depth: 1),
+ matchTypeObjectFields,
+ );
+ expect(
+ await getDisplayedFields(instanceRef),
+ matchDisplayedTypeObjectFields,
+ );
});
});
+ test(
+ 'int type getters',
+ () async {
+ await onBreakPoint('printSimpleLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef = await getInstanceRef(frame, '1.runtimeType');
+
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
+
test('list type', () async {
await onBreakPoint('printSimpleLocalRecord', (event) async {
final frame = event.topFrame!.index!;
@@ -142,8 +190,18 @@
final classId = instanceRef.classRef!.id;
expect(await getObject(classId), matchTypeClass);
- expect(await getFields(instanceRef, depth: 1), matchTypeObject);
- expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject);
+ expect(
+ await getFields(instanceRef, depth: 1),
+ matchTypeObjectFields,
+ );
+ expect(
+ await getDisplayedFields(instanceRef),
+ matchDisplayedTypeObjectFields,
+ );
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
});
});
@@ -160,11 +218,31 @@
final classId = instanceRef.classRef!.id;
expect(await getObject(classId), matchTypeClass);
- expect(await getFields(instanceRef, depth: 1), matchTypeObject);
- expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject);
+ expect(await getFields(instanceRef, depth: 1), matchTypeObjectFields);
+ expect(
+ await getDisplayedFields(instanceRef),
+ matchDisplayedTypeObjectFields,
+ );
});
});
+ test(
+ 'map type getters',
+ () async {
+ await onBreakPoint('printSimpleLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef =
+ await getInstanceRef(frame, '<int, String>{}.runtimeType');
+
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
+
test('set type', () async {
await onBreakPoint('printSimpleLocalRecord', (event) async {
final frame = event.topFrame!.index!;
@@ -177,11 +255,34 @@
final classId = instanceRef.classRef!.id;
expect(await getObject(classId), matchTypeClass);
- expect(await getFields(instanceRef, depth: 1), matchTypeObject);
- expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject);
+ expect(
+ await getFields(instanceRef, depth: 1),
+ matchTypeObjectFields,
+ );
+ expect(
+ await getDisplayedFields(instanceRef),
+ matchDisplayedTypeObjectFields,
+ );
});
});
+ test(
+ 'set type getters',
+ () async {
+ await onBreakPoint('printSimpleLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef =
+ await getInstanceRef(frame, '<int>{}.runtimeType');
+
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
+
test('record type', () async {
await onBreakPoint('printSimpleLocalRecord', (event) async {
final frame = event.topFrame!.index!;
@@ -200,15 +301,32 @@
expect(await getObject(classId), matchRecordTypeClass);
expect(
await getFields(instanceRef, depth: 2),
- {1: matchTypeObject, 2: matchTypeObject},
+ {1: matchTypeObjectFields, 2: matchTypeObjectFields},
);
expect(
await getDisplayedFields(instanceRef),
- ['int', 'String'],
+ {1: 'int', 2: 'String'},
);
});
});
+ test(
+ 'record type getters',
+ () async {
+ await onBreakPoint('printSimpleLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef =
+ await getInstanceRef(frame, "(0,'a').runtimeType");
+
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
+
test('class type', () async {
await onBreakPoint('printSimpleLocalRecord', (event) async {
final frame = event.topFrame!.index!;
@@ -222,9 +340,29 @@
final classId = instanceRef.classRef!.id;
expect(await getObject(classId), matchTypeClass);
- expect(await getFields(instanceRef, depth: 1), matchTypeObject);
- expect(await getDisplayedFields(instanceRef), matchDisplayedTypeObject);
+ expect(await getFields(instanceRef, depth: 1), matchTypeObjectFields);
+ expect(
+ await getDisplayedFields(instanceRef),
+ matchDisplayedTypeObjectFields,
+ );
});
});
+
+ test(
+ 'class type getters',
+ () async {
+ await onBreakPoint('printSimpleLocalRecord', (event) async {
+ final frame = event.topFrame!.index!;
+ final instanceRef =
+ await getInstanceRef(frame, "Uri.file('').runtimeType");
+
+ expect(
+ await getDisplayedGetters(instanceRef),
+ matchDisplayedTypeObjectGetters,
+ );
+ });
+ },
+ skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'),
+ );
});
}
diff --git a/dwds/test/variable_scope_test.dart b/dwds/test/variable_scope_test.dart
index ac6ff9b..9036ec7 100644
--- a/dwds/test/variable_scope_test.dart
+++ b/dwds/test/variable_scope_test.dart
@@ -9,7 +9,6 @@
import 'package:test/test.dart';
import 'package:test_common/logging.dart';
import 'package:test_common/test_sdk_configuration.dart';
-import 'package:test_common/utilities.dart';
import 'package:vm_service/vm_service.dart';
import 'fixtures/context.dart';
@@ -208,12 +207,6 @@
expect(
variableNames,
[
- // TODO(https://github.com/dart-lang/webdev/issues/2316): Make sure T
- // doesn't show up here.
- if (dartSdkIsAtLeast(
- newDdcTypeSystemVersion,
- ))
- 'T',
'closureLocalInsideMethod',
'local',
'parameter',
@@ -229,12 +222,6 @@
final variableNames = variables.keys.toList()..sort();
expect(variableNames, [
- // TODO(https://github.com/dart-lang/webdev/issues/2316): Make sure T
- // doesn't show up here.
- if (dartSdkIsAtLeast(
- newDdcTypeSystemVersion,
- ))
- 'T',
'this',
]);
});
diff --git a/fixtures/_test/example/scopes/main.dart b/fixtures/_test/example/scopes/main.dart
index ea7338a..6474feb 100644
--- a/fixtures/_test/example/scopes/main.dart
+++ b/fixtures/_test/example/scopes/main.dart
@@ -93,6 +93,7 @@
String hello() => message;
String Function(String) methodWithVariables() {
+ print('Test class is of type $T');
var local = '$message + something';
print(local);
return (String parameter) {