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) {