From 239dd4d502164b0ae412a9cd58439aa12750ff97 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:08:53 -0800 Subject: [PATCH 1/4] Prepare CHANGELOG --- dwds/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index acb5cbad3..a8392134e 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,3 +1,6 @@ +## 23.0.0+1 +- TODO + ## 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) From 5e0be8e2d468133b44d9ba46254bb5fe847a62a8 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Mon, 22 Jan 2024 09:15:06 -0800 Subject: [PATCH 2/4] Skip record type inspection tests (#2352) --- .../common/record_type_inspection_common.dart | 330 ++++++++++-------- .../test/instances/common/test_inspector.dart | 21 +- .../common/type_inspection_common.dart | 57 +-- 3 files changed, 238 insertions(+), 170 deletions(-) diff --git a/dwds/test/instances/common/record_type_inspection_common.dart b/dwds/test/instances/common/record_type_inspection_common.dart index 9e5eb9189..1d41bdbff 100644 --- a/dwds/test/instances/common/record_type_inspection_common.dart +++ b/dwds/test/instances/common/record_type_inspection_common.dart @@ -78,19 +78,30 @@ void runTests({ setUp(() => setCurrentLogWriter(debug: debug)); 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!; - - expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); - expect(await getObject(instanceId), matchRecordTypeInstance(length: 2)); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }); + 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!; + + expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + expect( + await getObject(instanceId), + matchRecordTypeInstance(length: 2), + ); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }, + ); + }, + skip: 'https://github.com/dart-lang/webdev/issues/2351', + ); test('simple record type elements', () async { await onBreakPoint('printSimpleLocalRecord', (event) async { @@ -126,19 +137,26 @@ void runTests({ }); }); - 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)); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }); + 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), + ); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }); + }, + skip: 'https://github.com/dart-lang/webdev/issues/2351', + ); test('complex record type elements', () async { await onBreakPoint('printComplexLocalRecord', (event) async { @@ -178,19 +196,26 @@ void runTests({ }); }); - test('complex record type with named fields ', () async { - await onBreakPoint('printComplexNamedLocalRecord', (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)); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }); + test( + 'complex record type with named fields ', + () async { + await onBreakPoint('printComplexNamedLocalRecord', (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), + ); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }); + }, + skip: 'https://github.com/dart-lang/webdev/issues/2351', + ); test('complex record type with named fields elements', () async { await onBreakPoint('printComplexNamedLocalRecord', (event) async { @@ -231,45 +256,56 @@ void runTests({ }); }); - 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)); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }); - - test('nested record type elements', () async { - await onBreakPoint('printNestedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - final elements = await getElements(instanceId); - expect( - elements, - [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], - ); - expect( - await getElements(elements[1].id!), - [matchTypeInstance('bool'), matchTypeInstance('int')], - ); - expect( - await getDisplayedFields(instanceRef), - ['bool', '(bool, int)'], - ); - expect( - await getDisplayedFields(elements[1]), - ['bool', 'int'], - ); - }); - }); + 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), + ); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }); + }, + skip: 'https://github.com/dart-lang/webdev/issues/2351', + ); + + test( + 'nested record type elements', + () async { + await onBreakPoint('printNestedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + final elements = await getElements(instanceId); + expect( + elements, + [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], + ); + expect( + await getElements(elements[1].id!), + [matchTypeInstance('bool'), matchTypeInstance('int')], + ); + expect( + await getDisplayedFields(instanceRef), + ['bool', '(bool, int)'], + ); + expect( + await getDisplayedFields(elements[1]), + ['bool', 'int'], + ); + }); + }, + skip: 'https://github.com/dart-lang/webdev/issues/2351', + ); test('nested record type display', () async { await onBreakPoint('printNestedLocalRecord', (event) async { @@ -288,68 +324,80 @@ void runTests({ }); }); - test('nested record type with named fields', () async { - await onBreakPoint('printNestedNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - - expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); - expect(instance, matchRecordTypeInstance(length: 2)); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }); - - test('nested record type with named fields elements', () async { - await onBreakPoint('printNestedNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - - final elements = await getElements(instanceId); - expect( - elements, - [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], - ); - expect( - await getElements(elements[1].id!), - [matchTypeInstance('bool'), matchTypeInstance('int')], - ); - expect( - await getDisplayedFields(instanceRef), - ['bool', '(bool, int)'], - ); - expect( - await getDisplayedFields(elements[1]), - ['bool', '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; - - 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})', - ), - ); - }); - }); + test( + 'nested record type with named fields', + () async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + + expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + expect(instance, matchRecordTypeInstance(length: 2)); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }); + }, + skip: 'https://github.com/dart-lang/webdev/issues/2351', + ); + + test( + 'nested record type with named fields elements', + () async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + final elements = await getElements(instanceId); + expect( + elements, + [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], + ); + expect( + await getElements(elements[1].id!), + [matchTypeInstance('bool'), matchTypeInstance('int')], + ); + expect( + await getDisplayedFields(instanceRef), + ['bool', '(bool, int)'], + ); + expect( + await getDisplayedFields(elements[1]), + ['bool', 'int'], + ); + }); + }, + skip: 'https://github.com/dart-lang/webdev/issues/2351', + ); + + 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(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})', + ), + ); + }); + }, + skip: 'https://github.com/dart-lang/webdev/issues/2351', + ); }); } diff --git a/dwds/test/instances/common/test_inspector.dart b/dwds/test/instances/common/test_inspector.dart index 17714617b..dc500cd5b 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'; @@ -216,7 +217,15 @@ Matcher matchRecordInstanceRef({required int length}) => isA() .having((e) => e.classRef!, 'classRef', matchRecordClassRef); Matcher matchRecordTypeInstanceRef({required int length}) => isA() - .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,8 +288,14 @@ Matcher matchTypeInstance(dynamic name) => isA() Matcher matchRecordClass = matchClass(name: matchRecordClassName, libraryId: _dartCoreLibrary); -Matcher matchRecordTypeClass = - matchClass(name: matchRecordTypeClassName, libraryId: _dartRuntimeLibrary); +Matcher matchRecordTypeClass = matchClass( + name: + // See https://github.com/dart-lang/sdk/commit/67e052d7e996be8ad9d02970117ffef07eab1c77: + dartSdkIsAtLeast('3.4.0-edge.eeec4d36e3ea9b166da277a46f62d7d3b9ce645a') + ? InstanceKind.kType + : InstanceKind.kRecordType, + libraryId: _dartRuntimeLibrary, +); Matcher matchTypeClass = matchClass(name: matchTypeClassName, libraryId: _dartCoreLibrary); diff --git a/dwds/test/instances/common/type_inspection_common.dart b/dwds/test/instances/common/type_inspection_common.dart index aadf4a828..a847314a7 100644 --- a/dwds/test/instances/common/type_inspection_common.dart +++ b/dwds/test/instances/common/type_inspection_common.dart @@ -182,32 +182,37 @@ void runTests({ }); }); - test('record type', () async { - await onBreakPoint('printSimpleLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, "(0,'a').runtimeType"); - expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); - - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - expect(instance, matchRecordTypeInstance(length: 2)); - expect( - await getElements(instanceId), - [matchTypeInstance('int'), matchTypeInstance('String')], - ); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - expect( - await getFields(instanceRef, depth: 2), - {1: matchTypeObject, 2: matchTypeObject}, - ); - expect( - await getDisplayedFields(instanceRef), - ['int', 'String'], - ); - }); - }); + test( + 'record type', + () async { + await onBreakPoint('printSimpleLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = + await getInstanceRef(frame, "(0,'a').runtimeType"); + expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + expect(instance, matchRecordTypeInstance(length: 2)); + expect( + await getElements(instanceId), + [matchTypeInstance('int'), matchTypeInstance('String')], + ); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + expect( + await getFields(instanceRef, depth: 2), + {1: matchTypeObject, 2: matchTypeObject}, + ); + expect( + await getDisplayedFields(instanceRef), + ['int', 'String'], + ); + }); + }, + skip: 'https://github.com/dart-lang/webdev/issues/2351', + ); test('class type', () async { await onBreakPoint('printSimpleLocalRecord', (event) async { From 0a0aa28ee9e299824714f6a8cdd7c97a9e6ca1a2 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Fri, 19 Jan 2024 09:35:23 -0800 Subject: [PATCH 3/4] Resolve merge conflicts in CHANGELOG --- dwds/CHANGELOG.md | 3 ++- dwds/lib/src/debugging/dart_scope.dart | 4 ++-- dwds/lib/src/debugging/debugger.dart | 2 +- dwds/lib/src/debugging/inspector.dart | 9 +++++++++ dwds/test/inspector_test.dart | 5 ----- dwds/test/variable_scope_test.dart | 13 ------------- fixtures/_test/example/scopes/main.dart | 1 + 7 files changed, 15 insertions(+), 22 deletions(-) diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index a8392134e..def277315 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,5 +1,6 @@ ## 23.0.0+1 -- TODO + +- Filter out internal type properties from the new DDC type system. - [#2348](https://github.com/dart-lang/webdev/pull/2348) ## 23.0.0 - Restructure `LoadStrategy` to provide build settings. - [#2270](https://github.com/dart-lang/webdev/pull/2270) diff --git a/dwds/lib/src/debugging/dart_scope.dart b/dwds/lib/src/debugging/dart_scope.dart index 341af8904..2b64b0ac3 100644 --- a/dwds/lib/src/debugging/dart_scope.dart +++ b/dwds/lib/src/debugging/dart_scope.dart @@ -20,11 +20,11 @@ final ddcTemporaryTypeVariableRegExp = RegExp(r'^__t[\$\w*]+$'); 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> visibleProperties({ +Future> 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 cdb4a1456..b5af29092 100644 --- a/dwds/lib/src/debugging/debugger.dart +++ b/dwds/lib/src/debugging/debugger.dart @@ -405,7 +405,7 @@ class Debugger extends Domain { Future> 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 660ee0dac..588051396 100644 --- a/dwds/lib/src/debugging/inspector.dart +++ b/dwds/lib/src/debugging/inspector.dart @@ -601,9 +601,18 @@ class AppInspector implements AppInspectorInterface { ); return jsProperties .map((each) => Property(each as Map)) + .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/test/inspector_test.dart b/dwds/test/inspector_test.dart index b17fca8de..e84cd64ad 100644 --- a/dwds/test/inspector_test.dart +++ b/dwds/test/inspector_test.dart @@ -11,7 +11,6 @@ import 'package:dwds/src/debugging/inspector.dart'; 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 @@ void main() { 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/variable_scope_test.dart b/dwds/test/variable_scope_test.dart index ac6ff9b1b..9036ec744 100644 --- a/dwds/test/variable_scope_test.dart +++ b/dwds/test/variable_scope_test.dart @@ -9,7 +9,6 @@ import 'package:dwds/src/services/chrome_proxy_service.dart'; 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 @@ void main() { 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 @@ void main() { 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 ea7338a16..6474febd0 100644 --- a/fixtures/_test/example/scopes/main.dart +++ b/fixtures/_test/example/scopes/main.dart @@ -93,6 +93,7 @@ class MyTestClass extends MyAbstractClass { String hello() => message; String Function(String) methodWithVariables() { + print('Test class is of type $T'); var local = '$message + something'; print(local); return (String parameter) { From 724a1862f93d756097de72c8729762e3f0c81681 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 22 Jan 2024 12:23:39 -0800 Subject: [PATCH 4/4] Resolve merge conflicts in CHANGELOG --- dwds/CHANGELOG.md | 1 + dwds/lib/src/debugging/instance.dart | 78 +++-- dwds/lib/src/debugging/metadata/provider.dart | 1 + .../src/services/chrome_proxy_service.dart | 41 ++- dwds/test/evaluate_common.dart | 59 ++-- .../common/instance_inspection_common.dart | 63 +++- .../common/patterns_inspection_common.dart | 2 +- .../common/record_type_inspection_common.dart | 270 +++++++++++------- .../test/instances/common/test_inspector.dart | 93 ++++-- .../common/type_inspection_common.dart | 205 ++++++++++--- 10 files changed, 582 insertions(+), 231 deletions(-) diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index def277315..3d524ed34 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,6 +1,7 @@ ## 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) diff --git a/dwds/lib/src/debugging/instance.dart b/dwds/lib/src/debugging/instance.dart index 9db9c78c9..27fa81a1b 100644 --- a/dwds/lib/src/debugging/instance.dart +++ b/dwds/lib/src/debugging/instance.dart @@ -238,29 +238,49 @@ class InstanceHelper extends Domain { 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> _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>((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 @@ class InstanceHelper extends Domain { 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 @@ class InstanceHelper extends Domain { ); } - /// 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> _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>((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 677bde885..62e2455e9 100644 --- a/dwds/lib/src/debugging/metadata/provider.dart +++ b/dwds/lib/src/debugging/metadata/provider.dart @@ -44,6 +44,7 @@ class MetadataProvider { '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 913db0f5b..bd77c4701 100644 --- a/dwds/lib/src/services/chrome_proxy_service.dart +++ b/dwds/lib/src/services/chrome_proxy_service.dart @@ -618,7 +618,35 @@ ${globalToolConfiguration.loadStrategy.loadModuleSnippet}("dart_sdk").developer. 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 @@ ${globalToolConfiguration.loadStrategy.loadModuleSnippet}("dart_sdk").developer. ); } throw RPCError( - 'evaluateInFrame', + 'evaluate', RPCErrorKind.kInvalidRequest.code, 'Expression evaluation is not supported for this configuration.', ); @@ -640,6 +668,15 @@ ${globalToolConfiguration.loadStrategy.loadModuleSnippet}("dart_sdk").developer. ); } + String _newVariableForScope(Map? scope) { + // Find a new variable not in scope. + var candidate = 'x'; + while (scope?.containsKey(candidate) ?? false) { + candidate += '\$1'; + } + return candidate; + } + @override Future evaluateInFrame( String isolateId, diff --git a/dwds/test/evaluate_common.dart b/dwds/test/evaluate_common.dart index 61025b5be..2125427d0 100644 --- a/dwds/test/evaluate_common.dart +++ b/dwds/test/evaluate_common.dart @@ -320,7 +320,7 @@ void testAll({ await getInstanceRef(event.topFrame!.index!, 'stream'); final instance = await getInstance(instanceRef); - expect(instance, matchInstance('_AsBroadcastStream')); + expect(instance, matchInstanceClassName('_AsBroadcastStream')); }); }); @@ -667,24 +667,24 @@ void testAll({ 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 @@ void testAll({ 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 @@ void testAll({ 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')), - ); - expect( - results[1], - matchErrorRef(contains('No batch result object ID')), + matchErrorRef( + contains('Evaluate is called on an unsupported target'), + ), ); + expect(results[1], matchInstanceRef('1, 1')); }); test('with scope override', () async { @@ -900,13 +920,20 @@ Future _setBreakpointInInjectedClient(WipDebugger debugger) async { return result.json['result']['breakpointId']; } -Matcher matchInstanceRef(dynamic value) => isA().having( - (instance) => instance.valueAsString, - 'valueAsString', - value, +Matcher matchInstanceRefKind(String kind) => + isA().having((instance) => instance.kind, 'kind', kind); + +Matcher matchInstanceRef(dynamic value) => isA() + .having((instance) => instance.valueAsString, 'valueAsString', value); + +Matcher matchInstanceClassName(dynamic className) => isA().having( + (instance) => instance.classRef!.name, + 'class name', + className, ); -Matcher matchInstance(dynamic className) => isA().having( +Matcher matchInstanceRefClassName(dynamic className) => + isA().having( (instance) => instance.classRef!.name, 'class name', className, diff --git a/dwds/test/instances/common/instance_inspection_common.dart b/dwds/test/instances/common/instance_inspection_common.dart index 5a0a499ed..312c6a5b6 100644 --- a/dwds/test/instances/common/instance_inspection_common.dart +++ b/dwds/test/instances/common/instance_inspection_common.dart @@ -189,14 +189,32 @@ void runTests({ 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 @@ void runTests({ matchSetInstance(type: '_HashSet'), ); - 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 b4e14c41d..9a303a632 100644 --- a/dwds/test/instances/common/patterns_inspection_common.dart +++ b/dwds/test/instances/common/patterns_inspection_common.dart @@ -105,7 +105,7 @@ void runTests({ 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 1d41bdbff..7aca5699a 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 @@ void runTests({ 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); @@ -78,30 +87,25 @@ void runTests({ setUp(() => setCurrentLogWriter(debug: debug)); 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!; - - expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); - expect( - await getObject(instanceId), - matchRecordTypeInstance(length: 2), - ); - - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }, - ); - }, - skip: 'https://github.com/dart-lang/webdev/issues/2351', - ); + 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!; + + expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + expect( + await getObject(instanceId), + matchRecordTypeInstance(length: 2), + ); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }, + ); + }); test('simple record type elements', () async { await onBreakPoint('printSimpleLocalRecord', (event) async { @@ -115,11 +119,27 @@ void runTests({ ); 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!; @@ -155,7 +175,6 @@ void runTests({ expect(await getObject(classId), matchRecordTypeClass); }); }, - skip: 'https://github.com/dart-lang/webdev/issues/2351', ); test('complex record type elements', () async { @@ -174,11 +193,27 @@ void runTests({ ); expect( await getDisplayedFields(instanceRef), - ['bool', 'int', 'IdentityMap'], + {1: 'bool', 2: 'int', 3: 'IdentityMap'}, ); }); }); + 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!; @@ -196,28 +231,24 @@ void runTests({ }); }); - test( - 'complex record type with named fields ', - () async { - await onBreakPoint('printComplexNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; + test('complex record type with named fields ', () async { + await onBreakPoint('printComplexNamedLocalRecord', (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); - }); - }, - skip: 'https://github.com/dart-lang/webdev/issues/2351', - ); + 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'); @@ -234,11 +265,27 @@ void runTests({ expect( await getDisplayedFields(instanceRef), - ['bool', 'int', 'IdentityMap'], + {1: 'bool', 2: 'int', 'array': 'IdentityMap'}, ); }); }); + 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!; @@ -274,37 +321,53 @@ void runTests({ expect(await getObject(classId), matchRecordTypeClass); }); }, - skip: 'https://github.com/dart-lang/webdev/issues/2351', ); + test('nested record type elements', () async { + await onBreakPoint('printNestedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + final elements = await getElements(instanceId); + expect( + elements, + [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], + ); + expect( + await getElements(elements[1].id!), + [matchTypeInstance('bool'), matchTypeInstance('int')], + ); + expect( + await getDisplayedFields(instanceRef), + {1: 'bool', 2: '(bool, int)'}, + ); + expect( + await getDisplayedFields(elements[1]), + {1: 'bool', 2: 'int'}, + ); + }); + }); + test( - 'nested record type elements', + 'nested record type getters', () async { await onBreakPoint('printNestedLocalRecord', (event) async { final frame = event.topFrame!.index!; final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; + final elements = await getElements(instanceRef.id!); - final elements = await getElements(instanceId); expect( - elements, - [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], + await getDisplayedGetters(instanceRef), + matchDisplayedTypeObjectGetters, ); expect( - await getElements(elements[1].id!), - [matchTypeInstance('bool'), matchTypeInstance('int')], - ); - expect( - await getDisplayedFields(instanceRef), - ['bool', '(bool, int)'], - ); - expect( - await getDisplayedFields(elements[1]), - ['bool', 'int'], + await getDisplayedGetters(elements[1]), + matchDisplayedTypeObjectGetters, ); }); }, - skip: 'https://github.com/dart-lang/webdev/issues/2351', + skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'), ); test('nested record type display', () async { @@ -324,53 +387,67 @@ void runTests({ }); }); - test( - 'nested record type with named fields', - () async { - await onBreakPoint('printNestedNamedLocalRecord', (event) async { - final frame = event.topFrame!.index!; - final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); + test('nested record type with named fields', () async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); - expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); - expect(instance, matchRecordTypeInstance(length: 2)); + expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + expect(instance, matchRecordTypeInstance(length: 2)); - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - }); - }, - skip: 'https://github.com/dart-lang/webdev/issues/2351', - ); + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + }); + }); + + test('nested record type with named fields elements', () async { + await onBreakPoint('printNestedNamedLocalRecord', (event) async { + final frame = event.topFrame!.index!; + final instanceRef = await getInstanceRef(frame, 'record.runtimeType'); + final instanceId = instanceRef.id!; + + final elements = await getElements(instanceId); + expect( + elements, + [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], + ); + expect( + await getElements(elements[1].id!), + [matchTypeInstance('bool'), matchTypeInstance('int')], + ); + expect( + await getDisplayedFields(instanceRef), + {1: 'bool', 'inner': '(bool, int)'}, + ); + + expect( + await getDisplayedFields(elements[1]), + {1: 'bool', 2: 'int'}, + ); + }); + }); test( - 'nested record type with named fields elements', + '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 instanceId = instanceRef.id!; + final elements = await getElements(instanceRef.id!); - final elements = await getElements(instanceId); - expect( - elements, - [matchTypeInstance('bool'), matchRecordTypeInstance(length: 2)], - ); - expect( - await getElements(elements[1].id!), - [matchTypeInstance('bool'), matchTypeInstance('int')], - ); expect( - await getDisplayedFields(instanceRef), - ['bool', '(bool, int)'], + await getDisplayedGetters(instanceRef), + matchDisplayedTypeObjectGetters, ); expect( - await getDisplayedFields(elements[1]), - ['bool', 'int'], + await getDisplayedGetters(elements[1]), + matchDisplayedTypeObjectGetters, ); }); }, - skip: 'https://github.com/dart-lang/webdev/issues/2351', + skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'), ); test( @@ -397,7 +474,6 @@ void runTests({ ); }); }, - skip: 'https://github.com/dart-lang/webdev/issues/2351', ); }); } diff --git a/dwds/test/instances/common/test_inspector.dart b/dwds/test/instances/common/test_inspector.dart index dc500cd5b..92768200b 100644 --- a/dwds/test/instances/common/test_inspector.dart +++ b/dwds/test/instances/common/test_inspector.dart @@ -43,7 +43,7 @@ class TestInspector { } } - Future getFields( + Future> getFields( String isolateId, InstanceRef instanceRef, { int? offset, @@ -88,7 +88,7 @@ class TestInspector { depth--; } if (depth == 0) { - return elements == null ? fieldRefs : fieldRefs.values.toList(); + return fieldRefs; } final fieldValues = {}; @@ -100,7 +100,27 @@ class TestInspector { depth: depth, ); } - return elements == null ? fieldValues : fieldValues.values.toList(); + return fieldValues; + } + + Future> 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.fromIterables( + getters.map((e) => e.name!), + results.map((e) => e as InstanceRef), + ); } Future getInstanceRef( @@ -161,7 +181,7 @@ class TestInspector { await service.invoke(isolateId, instanceId, 'toString', []) as InstanceRef; - Future> getDisplayedFields( + Future> getDisplayedFields( String isolateId, InstanceRef ref, ) async { @@ -173,7 +193,22 @@ class TestInspector { (await getDisplayedRef(isolateId, ref.id!)).valueAsString; final fields = await Future.wait(fieldRefs.values.map(toStringValue)); - return fields.toList(); + return Map.fromIterables(fieldRefs.keys, fields); + } + + Future> getDisplayedGetters( + String isolateId, + InstanceRef ref, + ) async { + final fieldRefs = + await getGetters(isolateId, ref) as Map; + + Future toStringValue(InstanceRef ref) async => + ref.valueAsString ?? + (await getDisplayedRef(isolateId, ref.id!)).valueAsString; + + final fields = await Future.wait(fieldRefs.values.map(toStringValue)); + return Map.fromIterables(fieldRefs.keys, fields); } Future> getElements( @@ -288,29 +323,35 @@ Matcher matchTypeInstance(dynamic name) => isA() Matcher matchRecordClass = matchClass(name: matchRecordClassName, libraryId: _dartCoreLibrary); -Matcher matchRecordTypeClass = matchClass( - name: - // See https://github.com/dart-lang/sdk/commit/67e052d7e996be8ad9d02970117ffef07eab1c77: - dartSdkIsAtLeast('3.4.0-edge.eeec4d36e3ea9b166da277a46f62d7d3b9ce645a') - ? InstanceKind.kType - : InstanceKind.kRecordType, - 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() .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, @@ -344,19 +385,25 @@ Object? _getValue(InstanceRef instanceRef) { } 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 a847314a7..70b6f3ed4 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 @@ void runTests({ getDisplayedFields(instanceRef) => testInspector.getDisplayedFields(isolateId, instanceRef); + getDisplayedGetters(instanceRef) => + testInspector.getDisplayedGetters(isolateId, instanceRef); + getInstanceRef(frame, expression) => testInspector.getInstanceRef(isolateId, frame, expression); @@ -55,15 +59,15 @@ void runTests({ 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 @@ void runTests({ 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 @@ void runTests({ 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 @@ void runTests({ 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 @@ void runTests({ 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, '{}.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,41 +255,76 @@ void runTests({ 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( - 'record type', + 'set type getters', () async { await onBreakPoint('printSimpleLocalRecord', (event) async { final frame = event.topFrame!.index!; final instanceRef = - await getInstanceRef(frame, "(0,'a').runtimeType"); - expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + await getInstanceRef(frame, '{}.runtimeType'); - final instanceId = instanceRef.id!; - final instance = await getObject(instanceId); - expect(instance, matchRecordTypeInstance(length: 2)); expect( - await getElements(instanceId), - [matchTypeInstance('int'), matchTypeInstance('String')], + 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!; + final instanceRef = await getInstanceRef(frame, "(0,'a').runtimeType"); + expect(instanceRef, matchRecordTypeInstanceRef(length: 2)); + + final instanceId = instanceRef.id!; + final instance = await getObject(instanceId); + expect(instance, matchRecordTypeInstance(length: 2)); + expect( + await getElements(instanceId), + [matchTypeInstance('int'), matchTypeInstance('String')], + ); + + final classId = instanceRef.classRef!.id; + expect(await getObject(classId), matchRecordTypeClass); + expect( + await getFields(instanceRef, depth: 2), + {1: matchTypeObjectFields, 2: matchTypeObjectFields}, + ); + expect( + await getDisplayedFields(instanceRef), + {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"); - final classId = instanceRef.classRef!.id; - expect(await getObject(classId), matchRecordTypeClass); - expect( - await getFields(instanceRef, depth: 2), - {1: matchTypeObject, 2: matchTypeObject}, - ); expect( - await getDisplayedFields(instanceRef), - ['int', 'String'], + await getDisplayedGetters(instanceRef), + matchDisplayedTypeObjectGetters, ); }); }, - skip: 'https://github.com/dart-lang/webdev/issues/2351', + skip: !dartSdkIsAtLeast('3.4.0-56.0.dev'), ); test('class type', () async { @@ -227,9 +340,29 @@ void runTests({ 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'), + ); }); }