[vm, service] Fix discrepancy between VM generating kind _TypeParameters and service.md claiming the existence of kind TypeParameters.

Fix the service type `TypeParameters` to recognize it is a heap object.

Exhaustively test that the service client libraries can handle inflating all types produced by the VM. Compare vm/cc/PrintJSON, which exhaustively tests the VM can generate any type.

TEST=ci
Bug: https://github.com/dart-lang/sdk/issues/52893
Change-Id: Id1f080656ef6e999e69f2ebb5b9961fa3b461e4a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/316862
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Ryan Macnak 2023-08-01 20:34:21 +00:00 committed by Commit Queue
parent c346b78f62
commit 5e49ea5ad8
15 changed files with 212 additions and 36 deletions

View file

@ -1,3 +1,7 @@
## 11.9.0
- Update to version `4.12` of the spec.
- Add `TypeParametersRef`; change supertype of `TypeParameters` to `Obj`.
## 11.8.0
- Update to version `4.11` of the spec.
- Add `isGetter` and `isSetter` properties to `FuncRef` and `Func`.

View file

@ -139,6 +139,7 @@ src/org/dartlang/vm/service/element/Timestamp.java
src/org/dartlang/vm/service/element/TypeArguments.java
src/org/dartlang/vm/service/element/TypeArgumentsRef.java
src/org/dartlang/vm/service/element/TypeParameters.java
src/org/dartlang/vm/service/element/TypeParametersRef.java
src/org/dartlang/vm/service/element/UnresolvedSourceLocation.java
src/org/dartlang/vm/service/element/UriList.java
src/org/dartlang/vm/service/element/VM.java

View file

@ -1 +1 @@
version=4.11
version=4.12

View file

@ -28,7 +28,7 @@ export 'snapshot_graph.dart'
HeapSnapshotObjectNoData,
HeapSnapshotObjectNullData;
const String vmServiceVersion = '4.11.0';
const String vmServiceVersion = '4.12.0';
/// @optional
const String optional = 'optional';
@ -189,6 +189,7 @@ Map<String, Function> _typeFactories = {
'Timestamp': Timestamp.parse,
'@TypeArguments': TypeArgumentsRef.parse,
'TypeArguments': TypeArguments.parse,
'@TypeParameters': TypeParametersRef.parse,
'TypeParameters': TypeParameters.parse,
'UnresolvedSourceLocation': UnresolvedSourceLocation.parse,
'UriList': UriList.parse,
@ -8826,14 +8827,49 @@ class TypeArguments extends Obj implements TypeArgumentsRef {
String toString() => '[TypeArguments id: $id, name: $name, types: $types]';
}
/// `TypeParametersRef` is a reference to a `TypeParameters` object.
class TypeParametersRef extends ObjRef {
static TypeParametersRef? parse(Map<String, dynamic>? json) =>
json == null ? null : TypeParametersRef._fromJson(json);
TypeParametersRef({
required String id,
}) : super(
id: id,
);
TypeParametersRef._fromJson(Map<String, dynamic> json)
: super._fromJson(json);
@override
String get type => '@TypeParameters';
@override
Map<String, dynamic> toJson() {
final json = super.toJson();
json['type'] = type;
return json;
}
@override
int get hashCode => id.hashCode;
@override
bool operator ==(Object other) =>
other is TypeParametersRef && id == other.id;
@override
String toString() => '[TypeParametersRef id: $id]';
}
/// A `TypeParameters` object represents the type argument vector for some
/// uninstantiated generic type.
class TypeParameters {
class TypeParameters extends Obj implements TypeParametersRef {
static TypeParameters? parse(Map<String, dynamic>? json) =>
json == null ? null : TypeParameters._fromJson(json);
/// The names of the type parameters.
List<String>? names;
InstanceRef? names;
/// The bounds set on each type parameter.
TypeArgumentsRef? bounds;
@ -8845,29 +8881,44 @@ class TypeParameters {
this.names,
this.bounds,
this.defaults,
});
required String id,
}) : super(
id: id,
);
TypeParameters._fromJson(Map<String, dynamic> json) {
names = List<String>.from(json['names']);
TypeParameters._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
names = createServiceObject(json['names'], const ['InstanceRef'])
as InstanceRef?;
bounds = createServiceObject(json['bounds'], const ['TypeArgumentsRef'])
as TypeArgumentsRef?;
defaults = createServiceObject(json['defaults'], const ['TypeArgumentsRef'])
as TypeArgumentsRef?;
}
@override
String get type => 'TypeParameters';
@override
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
final json = super.toJson();
json['type'] = type;
json.addAll({
'names': names?.map((f) => f).toList(),
'names': names?.toJson(),
'bounds': bounds?.toJson(),
'defaults': defaults?.toJson(),
});
return json;
}
@override
int get hashCode => id.hashCode;
@override
bool operator ==(Object other) => other is TypeParameters && id == other.id;
@override
String toString() =>
'[TypeParameters names: $names, bounds: $bounds, defaults: $defaults]';
'[TypeParameters id: $id, names: $names, bounds: $bounds, defaults: $defaults]';
}
/// The `UnresolvedSourceLocation` class is used to refer to an unresolved

View file

@ -1,5 +1,5 @@
name: vm_service
version: 11.8.0
version: 11.9.0
description: >-
A library to communicate with a service implementing the Dart VM
service protocol.

View file

@ -0,0 +1,32 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// See https://github.com/dart-lang/sdk/issues/52893
import 'package:vm_service/vm_service.dart';
import 'common/test_helper.dart';
var tests = <IsolateTest>[
(VmService service, IsolateRef isolateRef) async {
var profile = await service.getAllocationProfile(isolateRef.id!);
for (var entry in profile.members!) {
if (entry.instancesCurrent == 0) continue;
var classRef = entry.classRef!;
print(classRef);
var instanceSet =
await service.getInstances(isolateRef.id!, classRef.id!, 10);
for (var instance in instanceSet.instances!) {
await service.getObject(isolateRef.id!, instance.id!);
}
}
},
];
main(args) => runIsolateTests(
args,
tests,
'fetch_all_types_test.dart',
);

View file

@ -149,6 +149,16 @@ class InstanceRefElement extends CustomElement implements Renderable {
new SpanElement()..text = ' (${_instance.pattern!.valueAsString})'
]
];
case M.InstanceKind.userTag:
return [
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
..children = <Element>[
new SpanElement()
..classes = ['emphasize']
..text = _instance.clazz!.name,
new SpanElement()..text = ' (${_instance.name})'
]
];
case M.InstanceKind.stackTrace:
return [
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))

View file

@ -139,6 +139,9 @@ enum InstanceKind {
/// An instance of WeakReference
weakReference,
/// An instance of UserTag
userTag,
}
bool isTypedData(InstanceKind? kind) {

View file

@ -266,6 +266,24 @@ abstract class ServiceObject implements M.ObjectRef {
case 'UnlinkedCall':
obj = new UnlinkedCall._empty(owner);
break;
case 'ClosureData':
case 'CodeSourceMap':
case 'ContextScope':
case 'ExceptionHandlers':
case 'FfiTrampolineData':
case 'Instructions':
case 'InstructionsSection':
case 'KernelProgramInfo':
case 'LibraryPrefix':
case 'Namespace':
case 'PatchClass':
case 'WeakArray':
obj = new GenericHeapObject._empty(owner);
break;
default:
print('Unknown vmType: $vmType');
obj = new GenericHeapObject._empty(owner);
break;
}
break;
case 'Event':
@ -286,6 +304,9 @@ abstract class ServiceObject implements M.ObjectRef {
case 'TypeArguments':
obj = new TypeArguments._empty(owner);
break;
case 'TypeParameters':
obj = new GenericHeapObject._empty(owner);
break;
case 'Instance':
obj = new Instance._empty(owner);
break;
@ -427,6 +448,15 @@ abstract class HeapObject extends ServiceObject implements M.Object {
}
}
class GenericHeapObject extends HeapObject {
GenericHeapObject._empty(ServiceObjectOwner? owner) : super._empty(owner);
void _update(Map map, bool mapIsRef) {
_upgradeCollection(map, isolate);
super._update(map, mapIsRef);
}
}
class RetainingObject implements M.RetainingObject {
int get retainedSize => object.retainedSize!;
final HeapObject object;
@ -2788,6 +2818,8 @@ M.InstanceKind stringToInstanceKind(String s) {
return M.InstanceKind.finalizer;
case 'WeakReference':
return M.InstanceKind.weakReference;
case 'UserTag':
return M.InstanceKind.userTag;
}
var message = 'Unrecognized instance kind: $s';
Logger.root.severe(message);
@ -2885,6 +2917,7 @@ class Instance extends HeapObject implements M.Instance {
bool get isRegExp => kind == M.InstanceKind.regExp;
bool get isMirrorReference => kind == M.InstanceKind.mirrorReference;
bool get isWeakProperty => kind == M.InstanceKind.weakProperty;
bool get isUserTag => kind == M.InstanceKind.userTag;
bool get isClosure => kind == M.InstanceKind.closure;
bool get isStackTrace => kind == M.InstanceKind.stackTrace;
bool get isStackOverflowError {
@ -2926,6 +2959,9 @@ class Instance extends HeapObject implements M.Instance {
valueAsStringIsTruncated = map['valueAsStringIsTruncated'] == true;
closureFunction = map['closureFunction'];
name = map['name']?.toString();
if (map['label'] != null) {
name = map['label'];
}
length = map['length'];
pattern = map['pattern'];
typeClass = map['typeClass'];
@ -3040,7 +3076,9 @@ class Instance extends HeapObject implements M.Instance {
} else {
typedElements = null;
}
parameterizedClass = map['parameterizedClass'];
if (map['parameterizedClass'] is Class) {
parameterizedClass = map['parameterizedClass'];
}
typeArguments = map['typeArguments'];
parameterIndex = map['parameterIndex'];
bound = map['bound'];
@ -3623,7 +3661,9 @@ class Script extends HeapObject implements M.Script {
loadTime = new DateTime.fromMillisecondsSinceEpoch(loadTimeMillis);
lineOffset = map['lineOffset'];
columnOffset = map['columnOffset'];
_parseTokenPosTable(map['tokenPosTable']);
if (map['tokenPosTable'] != null) {
_parseTokenPosTable(map['tokenPosTable']);
}
source = map['source'];
_processSource(map['source']);
library = map['library'];
@ -3875,21 +3915,18 @@ class PcDescriptor {
}
}
class PcDescriptors extends ServiceObject implements M.PcDescriptorsRef {
Class? clazz;
int? size;
class PcDescriptors extends HeapObject implements M.PcDescriptorsRef {
bool get immutable => true;
final List<PcDescriptor> descriptors = <PcDescriptor>[];
PcDescriptors._empty(ServiceObjectOwner? owner) : super._empty(owner) {}
void _update(Map m, bool mapIsRef) {
_upgradeCollection(m, isolate);
super._update(m, mapIsRef);
if (mapIsRef) {
return;
}
_upgradeCollection(m, isolate);
clazz = m['class'];
size = m['size'];
descriptors.clear();
for (var descriptor in m['members']) {
var pcOffset = int.parse(descriptor['pcOffset'], radix: 16);
@ -4435,7 +4472,12 @@ class Code extends HeapObject implements M.Code {
var tryIndex = descriptor['tryIndex'];
var kind = descriptor['kind'].trim();
var instruction = instructionsByAddressOffset![address - startAddress];
CodeInstruction? instruction = null;
int addressOffset = address - startAddress;
if ((addressOffset >= 0) &&
(addressOffset < instructionsByAddressOffset!.length)) {
instruction = instructionsByAddressOffset![addressOffset];
}
if (instruction != null) {
instruction.descriptors
.add(new PcDescriptor(pcOffset, deoptId, tokenPos, tryIndex, kind));

View file

@ -0,0 +1,28 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// See https://github.com/dart-lang/sdk/issues/52893
import 'package:observatory/service_io.dart';
import 'test_helper.dart';
var tests = <IsolateTest>[
(Isolate isolate) async {
dynamic profile = await isolate.invokeRpc('getAllocationProfile', {});
for (var entry in profile["members"]) {
if (entry["instancesCurrent"] == 0) continue;
Class cls = entry["class"];
print(cls);
dynamic rawInstanceSet = await isolate.invokeRpcNoUpgrade(
'getInstances', {'objectId': cls.id, 'limit': 10});
dynamic instanceSet = await isolate.getInstances(cls, 10);
for (var instance in instanceSet.instances) {
await instance.load();
}
}
},
];
main(args) async => runIsolateTests(args, tests);

View file

@ -12,7 +12,7 @@ var tests = <VMTest>[
final result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], 'Version');
expect(result['major'], 4);
expect(result['minor'], 11);
expect(result['minor'], 12);
expect(result['_privateMajor'], 0);
expect(result['_privateMinor'], 0);
},

View file

@ -12,7 +12,7 @@ var tests = <VMTest>[
final result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], equals('Version'));
expect(result['major'], equals(4));
expect(result['minor'], equals(11));
expect(result['minor'], equals(12));
expect(result['_privateMajor'], equals(0));
expect(result['_privateMinor'], equals(0));
},

View file

@ -203,10 +203,12 @@ void Class::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {
}
void TypeParameters::PrintJSONImpl(JSONStream* stream, bool ref) const {
// Consider making this type public if we decide to expose TypeParameters
// through the protocol.
JSONObject jsobj(stream);
jsobj.AddProperty("kind", "_TypeParameters");
AddCommonObjectProperties(&jsobj, "TypeParameters", ref);
jsobj.AddServiceId(*this);
if (ref) {
return;
}
jsobj.AddProperty("flags", Array::Handle(flags()));
jsobj.AddProperty("names", Array::Handle(names()));
jsobj.AddProperty("bounds", TypeArguments::Handle(bounds()));
@ -736,12 +738,7 @@ void KernelProgramInfo::PrintImplementationFieldsImpl(
const JSONArray& jsarr_fields) const {}
void Instructions::PrintJSONImpl(JSONStream* stream, bool ref) const {
JSONObject jsobj(stream);
AddCommonObjectProperties(&jsobj, "Object", ref);
jsobj.AddServiceId(*this);
if (ref) {
return;
}
Object::PrintJSONImpl(stream, ref);
}
void Instructions::PrintImplementationFieldsImpl(

View file

@ -17,7 +17,7 @@
namespace dart {
#define SERVICE_PROTOCOL_MAJOR_VERSION 4
#define SERVICE_PROTOCOL_MINOR_VERSION 11
#define SERVICE_PROTOCOL_MINOR_VERSION 12
class Array;
class EmbedderServiceHandler;

View file

@ -1,8 +1,8 @@
# Dart VM Service Protocol 4.11
# Dart VM Service Protocol 4.12
> Please post feedback to the [observatory-discuss group][discuss-list]
This document describes of _version 4.11_ of the Dart VM Service Protocol. This
This document describes of _version 4.12_ of the Dart VM Service Protocol. This
protocol is used to communicate with a running Dart Virtual Machine.
To use the Service Protocol, start the VM with the *--observe* flag.
@ -4508,9 +4508,16 @@ instantiated generic type.
### TypeParameters
```
class TypeParameters {
class @TypeParameters extends @Object {
}
```
_@TypeParameters_ is a reference to a _TypeParameters_ object.
```
class TypeParameters extends Object {
// The names of the type parameters.
string[] names;
@Instance names;
// The bounds set on each type parameter.
@TypeArguments bounds;
@ -4719,5 +4726,6 @@ version | comments
4.9 | Added `isolateGroup` property to `Event`.
4.10 | Deprecated `isSyntheticAsyncContinuation` on `Breakpoint`.
4.11 | Added `isGetter` and `isSetter` properties to `@Function` and `Function`.
4.12 | Added `@TypeParameters` and changed `TypeParameters` to extend `Object`.
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss