blob: 277b4637b53be01564cd97b26a67921ba1965ded [file] [log] [blame]
// Copyright (c) 2015, 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.
// This is a generated file.
/// A library to access the VM Service API.
///
/// The main entry-point for this library is the [VmService] class.
import 'dart:async';
import 'dart:convert' show base64, jsonDecode, jsonEncode, utf8;
import 'dart:typed_data';
import 'service_extension_registry.dart';
export 'service_extension_registry.dart' show ServiceExtensionRegistry;
export 'snapshot_graph.dart'
show
HeapSnapshotClass,
HeapSnapshotExternalProperty,
HeapSnapshotField,
HeapSnapshotGraph,
HeapSnapshotObject,
HeapSnapshotObjectLengthData,
HeapSnapshotObjectNoData,
HeapSnapshotObjectNullData;
const String vmServiceVersion = '3.51.0';
/// @optional
const String optional = 'optional';
/// Decode a string in Base64 encoding into the equivalent non-encoded string.
/// This is useful for handling the results of the Stdout or Stderr events.
String decodeBase64(String str) => utf8.decode(base64.decode(str));
// Returns true if a response is the Dart `null` instance.
bool _isNullInstance(Map json) =>
((json['type'] == '@Instance') && (json['kind'] == 'Null'));
Object? createServiceObject(dynamic json, List<String> expectedTypes) {
if (json == null) return null;
if (json is List) {
return json.map((e) => createServiceObject(e, expectedTypes)).toList();
} else if (json is Map<String, dynamic>) {
String? type = json['type'];
// Not a Response type.
if (type == null) {
// If there's only one expected type, we'll just use that type.
if (expectedTypes.length == 1) {
type = expectedTypes.first;
} else {
return Response.parse(json);
}
} else if (_isNullInstance(json) &&
(!expectedTypes.contains('InstanceRef'))) {
// Replace null instances with null when we don't expect an instance to
// be returned.
return null;
}
final typeFactory = _typeFactories[type];
if (typeFactory == null) {
return null;
} else {
return typeFactory(json);
}
} else {
// Handle simple types.
return json;
}
}
dynamic _createSpecificObject(
dynamic json, dynamic creator(Map<String, dynamic> map)) {
if (json == null) return null;
if (json is List) {
return json.map((e) => creator(e)).toList();
} else if (json is Map) {
return creator({
for (String key in json.keys) key: json[key],
});
} else {
// Handle simple types.
return json;
}
}
void _setIfNotNull(Map<String, dynamic> json, String key, Object? value) {
if (value == null) return;
json[key] = value;
}
Future<T> extensionCallHelper<T>(VmService service, String method, Map args) {
return service._call(method, args);
}
typedef ServiceCallback = Future<Map<String, dynamic>> Function(
Map<String, dynamic> params);
void addTypeFactory(String name, Function factory) {
if (_typeFactories.containsKey(name)) {
throw StateError('Factory already registered for $name');
}
_typeFactories[name] = factory;
}
Map<String, Function> _typeFactories = {
'AllocationProfile': AllocationProfile.parse,
'BoundField': BoundField.parse,
'BoundVariable': BoundVariable.parse,
'Breakpoint': Breakpoint.parse,
'@Class': ClassRef.parse,
'Class': Class.parse,
'ClassHeapStats': ClassHeapStats.parse,
'ClassList': ClassList.parse,
'@Code': CodeRef.parse,
'Code': Code.parse,
'@Context': ContextRef.parse,
'Context': Context.parse,
'ContextElement': ContextElement.parse,
'CpuSamples': CpuSamples.parse,
'CpuSample': CpuSample.parse,
'@Error': ErrorRef.parse,
'Error': Error.parse,
'Event': Event.parse,
'ExtensionData': ExtensionData.parse,
'@Field': FieldRef.parse,
'Field': Field.parse,
'Flag': Flag.parse,
'FlagList': FlagList.parse,
'Frame': Frame.parse,
'@Function': FuncRef.parse,
'Function': Func.parse,
'@Instance': InstanceRef.parse,
'Instance': Instance.parse,
'@Isolate': IsolateRef.parse,
'Isolate': Isolate.parse,
'IsolateFlag': IsolateFlag.parse,
'@IsolateGroup': IsolateGroupRef.parse,
'IsolateGroup': IsolateGroup.parse,
'InboundReferences': InboundReferences.parse,
'InboundReference': InboundReference.parse,
'InstanceSet': InstanceSet.parse,
'@Library': LibraryRef.parse,
'Library': Library.parse,
'LibraryDependency': LibraryDependency.parse,
'LogRecord': LogRecord.parse,
'MapAssociation': MapAssociation.parse,
'MemoryUsage': MemoryUsage.parse,
'Message': Message.parse,
'NativeFunction': NativeFunction.parse,
'@Null': NullValRef.parse,
'Null': NullVal.parse,
'@Object': ObjRef.parse,
'Object': Obj.parse,
'Parameter': Parameter.parse,
'PortList': PortList.parse,
'ProfileFunction': ProfileFunction.parse,
'ProtocolList': ProtocolList.parse,
'Protocol': Protocol.parse,
'ProcessMemoryUsage': ProcessMemoryUsage.parse,
'ProcessMemoryItem': ProcessMemoryItem.parse,
'ReloadReport': ReloadReport.parse,
'RetainingObject': RetainingObject.parse,
'RetainingPath': RetainingPath.parse,
'Response': Response.parse,
'Sentinel': Sentinel.parse,
'@Script': ScriptRef.parse,
'Script': Script.parse,
'ScriptList': ScriptList.parse,
'SourceLocation': SourceLocation.parse,
'SourceReport': SourceReport.parse,
'SourceReportCoverage': SourceReportCoverage.parse,
'SourceReportRange': SourceReportRange.parse,
'Stack': Stack.parse,
'Success': Success.parse,
'Timeline': Timeline.parse,
'TimelineEvent': TimelineEvent.parse,
'TimelineFlags': TimelineFlags.parse,
'Timestamp': Timestamp.parse,
'@TypeArguments': TypeArgumentsRef.parse,
'TypeArguments': TypeArguments.parse,
'TypeParameters': TypeParameters.parse,
'UnresolvedSourceLocation': UnresolvedSourceLocation.parse,
'Version': Version.parse,
'@VM': VMRef.parse,
'VM': VM.parse,
};
Map<String, List<String>> _methodReturnTypes = {
'addBreakpoint': const ['Breakpoint'],
'addBreakpointWithScriptUri': const ['Breakpoint'],
'addBreakpointAtEntry': const ['Breakpoint'],
'clearCpuSamples': const ['Success'],
'clearVMTimeline': const ['Success'],
'invoke': const ['InstanceRef', 'ErrorRef'],
'evaluate': const ['InstanceRef', 'ErrorRef'],
'evaluateInFrame': const ['InstanceRef', 'ErrorRef'],
'getAllocationProfile': const ['AllocationProfile'],
'getAllocationTraces': const ['CpuSamples'],
'getClassList': const ['ClassList'],
'getCpuSamples': const ['CpuSamples'],
'getFlagList': const ['FlagList'],
'getInboundReferences': const ['InboundReferences'],
'getInstances': const ['InstanceSet'],
'getIsolate': const ['Isolate'],
'getIsolateGroup': const ['IsolateGroup'],
'getMemoryUsage': const ['MemoryUsage'],
'getIsolateGroupMemoryUsage': const ['MemoryUsage'],
'getScripts': const ['ScriptList'],
'getObject': const ['Obj'],
'getPorts': const ['PortList'],
'getRetainingPath': const ['RetainingPath'],
'getProcessMemoryUsage': const ['ProcessMemoryUsage'],
'getStack': const ['Stack'],
'getSupportedProtocols': const ['ProtocolList'],
'getSourceReport': const ['SourceReport'],
'getVersion': const ['Version'],
'getVM': const ['VM'],
'getVMTimeline': const ['Timeline'],
'getVMTimelineFlags': const ['TimelineFlags'],
'getVMTimelineMicros': const ['Timestamp'],
'pause': const ['Success'],
'kill': const ['Success'],
'registerService': const ['Success'],
'reloadSources': const ['ReloadReport'],
'removeBreakpoint': const ['Success'],
'requestHeapSnapshot': const ['Success'],
'resume': const ['Success'],
'setBreakpointState': const ['Breakpoint'],
'setExceptionPauseMode': const ['Success'],
'setFlag': const ['Success', 'Error'],
'setLibraryDebuggable': const ['Success'],
'setName': const ['Success'],
'setTraceClassAllocation': const ['Success'],
'setVMName': const ['Success'],
'setVMTimelineFlags': const ['Success'],
'streamCancel': const ['Success'],
'streamListen': const ['Success'],
};
/// A class representation of the Dart VM Service Protocol.
///
/// Both clients and servers should implement this interface.
abstract class VmServiceInterface {
/// Returns the stream for a given stream id.
///
/// This is not a part of the spec, but is needed for both the client and
/// server to get access to the real event streams.
Stream<Event> onEvent(String streamId);
/// Handler for calling extra service extensions.
Future<Response> callServiceExtension(String method,
{String? isolateId, Map<String, dynamic>? args});
/// The `addBreakpoint` RPC is used to add a breakpoint at a specific line of
/// some script.
///
/// The `scriptId` parameter is used to specify the target script.
///
/// The `line` parameter is used to specify the target line for the
/// breakpoint. If there are multiple possible breakpoints on the target line,
/// then the VM will place the breakpoint at the location which would execute
/// soonest. If it is not possible to set a breakpoint at the target line, the
/// breakpoint will be added at the next possible breakpoint location within
/// the same function.
///
/// The `column` parameter may be optionally specified. This is useful for
/// targeting a specific breakpoint on a line with multiple possible
/// breakpoints.
///
/// If no breakpoint is possible at that line, the `102` (Cannot add
/// breakpoint) [RPC error] code is returned.
///
/// Note that breakpoints are added and removed on a per-isolate basis.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Breakpoint].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Breakpoint> addBreakpoint(
String isolateId,
String scriptId,
int line, {
int? column,
});
/// The `addBreakpoint` RPC is used to add a breakpoint at a specific line of
/// some script. This RPC is useful when a script has not yet been assigned an
/// id, for example, if a script is in a deferred library which has not yet
/// been loaded.
///
/// The `scriptUri` parameter is used to specify the target script.
///
/// The `line` parameter is used to specify the target line for the
/// breakpoint. If there are multiple possible breakpoints on the target line,
/// then the VM will place the breakpoint at the location which would execute
/// soonest. If it is not possible to set a breakpoint at the target line, the
/// breakpoint will be added at the next possible breakpoint location within
/// the same function.
///
/// The `column` parameter may be optionally specified. This is useful for
/// targeting a specific breakpoint on a line with multiple possible
/// breakpoints.
///
/// If no breakpoint is possible at that line, the `102` (Cannot add
/// breakpoint) [RPC error] code is returned.
///
/// Note that breakpoints are added and removed on a per-isolate basis.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Breakpoint].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Breakpoint> addBreakpointWithScriptUri(
String isolateId,
String scriptUri,
int line, {
int? column,
});
/// The `addBreakpointAtEntry` RPC is used to add a breakpoint at the
/// entrypoint of some function.
///
/// If no breakpoint is possible at the function entry, the `102` (Cannot add
/// breakpoint) [RPC error] code is returned.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Breakpoint].
///
/// Note that breakpoints are added and removed on a per-isolate basis.
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Breakpoint> addBreakpointAtEntry(String isolateId, String functionId);
/// Clears all CPU profiling samples.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Success].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Success> clearCpuSamples(String isolateId);
/// Clears all VM timeline events.
///
/// See [Success].
Future<Success> clearVMTimeline();
/// The `invoke` RPC is used to perform regular method invocation on some
/// receiver, as if by dart:mirror's ObjectMirror.invoke. Note this does not
/// provide a way to perform getter, setter or constructor invocation.
///
/// `targetId` may refer to a [Library], [Class], or [Instance].
///
/// Each elements of `argumentId` may refer to an [Instance].
///
/// If `disableBreakpoints` is provided and set to true, any breakpoints hit
/// as a result of this invocation are ignored, including pauses resulting
/// from a call to `debugger()` from `dart:developer`. Defaults to false if
/// not provided.
///
/// If `targetId` or any element of `argumentIds` is a temporary id which has
/// expired, then the `Expired` [Sentinel] is returned.
///
/// If `targetId` or any element of `argumentIds` refers to an object which
/// has been collected by the VM's garbage collector, then the `Collected`
/// [Sentinel] is returned.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// If invocation triggers a failed compilation then [RPC error] 113
/// "Expression compilation error" is returned.
///
/// If a runtime error occurs while evaluating the invocation, an [ErrorRef]
/// reference will be returned.
///
/// If the invocation is evaluated successfully, an [InstanceRef] reference
/// will be returned.
///
/// The return value can be one of [InstanceRef] or [ErrorRef].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Response> invoke(
String isolateId,
String targetId,
String selector,
List<String> argumentIds, {
bool? disableBreakpoints,
});
/// The `evaluate` RPC is used to evaluate an expression in the context of
/// some target.
///
/// `targetId` may refer to a [Library], [Class], or [Instance].
///
/// If `targetId` is a temporary id which has expired, then the `Expired`
/// [Sentinel] is returned.
///
/// If `targetId` refers to an object which has been collected by the VM's
/// garbage collector, then the `Collected` [Sentinel] is returned.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// If `scope` is provided, it should be a map from identifiers to object ids.
/// These bindings will be added to the scope in which the expression is
/// evaluated, which is a child scope of the class or library for
/// instance/class or library targets respectively. This means bindings
/// provided in `scope` may shadow instance members, class members and
/// top-level members.
///
/// If `disableBreakpoints` is provided and set to true, any breakpoints hit
/// as a result of this evaluation are ignored. Defaults to false if not
/// provided.
///
/// If the expression fails to parse and compile, then [RPC error] 113
/// "Expression compilation error" is returned.
///
/// If an error occurs while evaluating the expression, an [ErrorRef]
/// reference will be returned.
///
/// If the expression is evaluated successfully, an [InstanceRef] reference
/// will be returned.
///
/// The return value can be one of [InstanceRef] or [ErrorRef].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Response> evaluate(
String isolateId,
String targetId,
String expression, {
Map<String, String>? scope,
bool? disableBreakpoints,
});
/// The `evaluateInFrame` RPC is used to evaluate an expression in the context
/// of a particular stack frame. `frameIndex` is the index of the desired
/// [Frame], with an index of `0` indicating the top (most recent) frame.
///
/// If `scope` is provided, it should be a map from identifiers to object ids.
/// These bindings will be added to the scope in which the expression is
/// evaluated, which is a child scope of the frame's current scope. This means
/// bindings provided in `scope` may shadow instance members, class members,
/// top-level members, parameters and locals.
///
/// If `disableBreakpoints` is provided and set to true, any breakpoints hit
/// as a result of this evaluation are ignored. Defaults to false if not
/// provided.
///
/// If the expression fails to parse and compile, then [RPC error] 113
/// "Expression compilation error" is returned.
///
/// If an error occurs while evaluating the expression, an [ErrorRef]
/// reference will be returned.
///
/// If the expression is evaluated successfully, an [InstanceRef] reference
/// will be returned.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// The return value can be one of [InstanceRef] or [ErrorRef].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Response> evaluateInFrame(
String isolateId,
int frameIndex,
String expression, {
Map<String, String>? scope,
bool? disableBreakpoints,
});
/// The `getAllocationProfile` RPC is used to retrieve allocation information
/// for a given isolate.
///
/// If `reset` is provided and is set to true, the allocation accumulators
/// will be reset before collecting allocation information.
///
/// If `gc` is provided and is set to true, a garbage collection will be
/// attempted before collecting allocation information. There is no guarantee
/// that a garbage collection will be actually be performed.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<AllocationProfile> getAllocationProfile(String isolateId,
{bool? reset, bool? gc});
/// The `getAllocationTraces` RPC allows for the retrieval of allocation
/// traces for objects of a specific set of types (see
/// [setTraceClassAllocation]). Only samples collected in the time range
/// `[timeOriginMicros, timeOriginMicros + timeExtentMicros]` will be
/// reported.
///
/// If `classId` is provided, only traces for allocations with the matching
/// `classId` will be reported.
///
/// If the profiler is disabled, an RPC error response will be returned.
///
/// If isolateId refers to an isolate which has exited, then the Collected
/// Sentinel is returned.
///
/// See [CpuSamples].
Future<CpuSamples> getAllocationTraces(
String isolateId, {
int? timeOriginMicros,
int? timeExtentMicros,
String? classId,
});
/// The `getClassList` RPC is used to retrieve a `ClassList` containing all
/// classes for an isolate based on the isolate's `isolateId`.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [ClassList].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<ClassList> getClassList(String isolateId);
/// The `getCpuSamples` RPC is used to retrieve samples collected by the CPU
/// profiler. Only samples collected in the time range `[timeOriginMicros,
/// timeOriginMicros + timeExtentMicros]` will be reported.
///
/// If the profiler is disabled, an [RPC error] response will be returned.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [CpuSamples].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<CpuSamples> getCpuSamples(
String isolateId, int timeOriginMicros, int timeExtentMicros);
/// The `getFlagList` RPC returns a list of all command line flags in the VM
/// along with their current values.
///
/// See [FlagList].
Future<FlagList> getFlagList();
/// Returns a set of inbound references to the object specified by `targetId`.
/// Up to `limit` references will be returned.
///
/// The order of the references is undefined (i.e., not related to allocation
/// order) and unstable (i.e., multiple invocations of this method against the
/// same object can give different answers even if no Dart code has executed
/// between the invocations).
///
/// The references may include multiple `objectId`s that designate the same
/// object.
///
/// The references may include objects that are unreachable but have not yet
/// been garbage collected.
///
/// If `targetId` is a temporary id which has expired, then the `Expired`
/// [Sentinel] is returned.
///
/// If `targetId` refers to an object which has been collected by the VM's
/// garbage collector, then the `Collected` [Sentinel] is returned.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [InboundReferences].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<InboundReferences> getInboundReferences(
String isolateId, String targetId, int limit);
/// The `getInstances` RPC is used to retrieve a set of instances which are of
/// a specific class. This does not include instances of subclasses of the
/// given class.
///
/// The order of the instances is undefined (i.e., not related to allocation
/// order) and unstable (i.e., multiple invocations of this method against the
/// same class can give different answers even if no Dart code has executed
/// between the invocations).
///
/// The set of instances may include objects that are unreachable but have not
/// yet been garbage collected.
///
/// `objectId` is the ID of the `Class` to retrieve instances for. `objectId`
/// must be the ID of a `Class`, otherwise an [RPC error] is returned.
///
/// `limit` is the maximum number of instances to be returned.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [InstanceSet].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<InstanceSet> getInstances(
String isolateId, String objectId, int limit);
/// The `getIsolate` RPC is used to lookup an `Isolate` object by its `id`.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Isolate].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Isolate> getIsolate(String isolateId);
/// The `getIsolateGroup` RPC is used to lookup an `IsolateGroup` object by
/// its `id`.
///
/// If `isolateGroupId` refers to an isolate group which has exited, then the
/// `Expired` [Sentinel] is returned.
///
/// `IsolateGroup` `id` is an opaque identifier that can be fetched from an
/// `IsolateGroup`. List of active `IsolateGroup`'s, for example, is available
/// on `VM` object.
///
/// See [IsolateGroup], [VM].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<IsolateGroup> getIsolateGroup(String isolateGroupId);
/// The `getMemoryUsage` RPC is used to lookup an isolate's memory usage
/// statistics by its `id`.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Isolate].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<MemoryUsage> getMemoryUsage(String isolateId);
/// The `getIsolateGroupMemoryUsage` RPC is used to lookup an isolate group's
/// memory usage statistics by its `id`.
///
/// If `isolateGroupId` refers to an isolate group which has exited, then the
/// `Expired` [Sentinel] is returned.
///
/// See [IsolateGroup].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<MemoryUsage> getIsolateGroupMemoryUsage(String isolateGroupId);
/// The `getScripts` RPC is used to retrieve a `ScriptList` containing all
/// scripts for an isolate based on the isolate's `isolateId`.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [ScriptList].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<ScriptList> getScripts(String isolateId);
/// The `getObject` RPC is used to lookup an `object` from some isolate by its
/// `id`.
///
/// If `objectId` is a temporary id which has expired, then the `Expired`
/// [Sentinel] is returned.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// If `objectId` refers to a heap object which has been collected by the VM's
/// garbage collector, then the `Collected` [Sentinel] is returned.
///
/// If `objectId` refers to a non-heap object which has been deleted, then the
/// `Collected` [Sentinel] is returned.
///
/// If the object handle has not expired and the object has not been
/// collected, then an [Obj] will be returned.
///
/// The `offset` and `count` parameters are used to request subranges of
/// Instance objects with the kinds: String, List, Map, Uint8ClampedList,
/// Uint8List, Uint16List, Uint32List, Uint64List, Int8List, Int16List,
/// Int32List, Int64List, Flooat32List, Float64List, Inst32x3List,
/// Float32x4List, and Float64x2List. These parameters are otherwise ignored.
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Obj> getObject(
String isolateId,
String objectId, {
int? offset,
int? count,
});
/// The `getPorts` RPC is used to retrieve the list of `ReceivePort` instances
/// for a given isolate.
///
/// See [PortList].
Future<PortList> getPorts(String isolateId);
/// The `getRetainingPath` RPC is used to lookup a path from an object
/// specified by `targetId` to a GC root (i.e., the object which is preventing
/// this object from being garbage collected).
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// If `targetId` refers to a heap object which has been collected by the VM's
/// garbage collector, then the `Collected` [Sentinel] is returned.
///
/// If `targetId` refers to a non-heap object which has been deleted, then the
/// `Collected` [Sentinel] is returned.
///
/// If the object handle has not expired and the object has not been
/// collected, then an [RetainingPath] will be returned.
///
/// The `limit` parameter specifies the maximum path length to be reported as
/// part of the retaining path. If a path is longer than `limit`, it will be
/// truncated at the root end of the path.
///
/// See [RetainingPath].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<RetainingPath> getRetainingPath(
String isolateId, String targetId, int limit);
/// Returns a description of major uses of memory known to the VM.
///
/// Adding or removing buckets is considered a backwards-compatible change for
/// the purposes of versioning. A client must gracefully handle the removal or
/// addition of any bucket.
Future<ProcessMemoryUsage> getProcessMemoryUsage();
/// The `getStack` RPC is used to retrieve the current execution stack and
/// message queue for an isolate. The isolate does not need to be paused.
///
/// If `limit` is provided, up to `limit` frames from the top of the stack
/// will be returned. If the stack depth is smaller than `limit` the entire
/// stack is returned. Note: this limit also applies to the
/// `asyncCausalFrames` and `awaiterFrames` stack representations in the
/// `Stack` response.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Stack].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Stack> getStack(String isolateId, {int? limit});
/// The `getSupportedProtocols` RPC is used to determine which protocols are
/// supported by the current server.
///
/// The result of this call should be intercepted by any middleware that
/// extends the core VM service protocol and should add its own protocol to
/// the list of protocols before forwarding the response to the client.
///
/// See [ProtocolList].
Future<ProtocolList> getSupportedProtocols();
/// The `getSourceReport` RPC is used to generate a set of reports tied to
/// source locations in an isolate.
///
/// The `reports` parameter is used to specify which reports should be
/// generated. The `reports` parameter is a list, which allows multiple
/// reports to be generated simultaneously from a consistent isolate state.
/// The `reports` parameter is allowed to be empty (this might be used to
/// force compilation of a particular subrange of some script).
///
/// The available report kinds are:
///
/// report kind | meaning
/// ----------- | -------
/// Coverage | Provide code coverage information
/// PossibleBreakpoints | Provide a list of token positions which correspond
/// to possible breakpoints.
///
/// The `scriptId` parameter is used to restrict the report to a particular
/// script. When analyzing a particular script, either or both of the
/// `tokenPos` and `endTokenPos` parameters may be provided to restrict the
/// analysis to a subrange of a script (for example, these can be used to
/// restrict the report to the range of a particular class or function).
///
/// If the `scriptId` parameter is not provided then the reports are generated
/// for all loaded scripts and the `tokenPos` and `endTokenPos` parameters are
/// disallowed.
///
/// The `forceCompilation` parameter can be used to force compilation of all
/// functions in the range of the report. Forcing compilation can cause a
/// compilation error, which could terminate the running Dart program. If this
/// parameter is not provided, it is considered to have the value `false`.
///
/// The `reportLines` parameter changes the token positions in
/// `SourceReportRange.possibleBreakpoints` and `SourceReportCoverage` to be
/// line numbers. This is designed to reduce the number of RPCs that need to
/// be performed in the case that the client is only interested in line
/// numbers. If this parameter is not provided, it is considered to have the
/// value `false`.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [SourceReport].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<SourceReport> getSourceReport(
String isolateId,
/*List<SourceReportKind>*/ List<String> reports, {
String? scriptId,
int? tokenPos,
int? endTokenPos,
bool? forceCompile,
bool? reportLines,
});
/// The `getVersion` RPC is used to determine what version of the Service
/// Protocol is served by a VM.
///
/// See [Version].
Future<Version> getVersion();
/// The `getVM` RPC returns global information about a Dart virtual machine.
///
/// See [VM].
Future<VM> getVM();
/// The `getVMTimeline` RPC is used to retrieve an object which contains VM
/// timeline events.
///
/// The `timeOriginMicros` parameter is the beginning of the time range used
/// to filter timeline events. It uses the same monotonic clock as
/// dart:developer's `Timeline.now` and the VM embedding API's
/// `Dart_TimelineGetMicros`. See [getVMTimelineMicros] for access to this
/// clock through the service protocol.
///
/// The `timeExtentMicros` parameter specifies how large the time range used
/// to filter timeline events should be.
///
/// For example, given `timeOriginMicros` and `timeExtentMicros`, only
/// timeline events from the following time range will be returned:
/// `(timeOriginMicros, timeOriginMicros + timeExtentMicros)`.
///
/// If `getVMTimeline` is invoked while the current recorder is one of Fuchsia
/// or Macos or Systrace, an [RPC error] with error code `114`, `invalid
/// timeline request`, will be returned as timeline events are handled by the
/// OS in these modes.
Future<Timeline> getVMTimeline(
{int? timeOriginMicros, int? timeExtentMicros});
/// The `getVMTimelineFlags` RPC returns information about the current VM
/// timeline configuration.
///
/// To change which timeline streams are currently enabled, see
/// [setVMTimelineFlags].
///
/// See [TimelineFlags].
Future<TimelineFlags> getVMTimelineFlags();
/// The `getVMTimelineMicros` RPC returns the current time stamp from the
/// clock used by the timeline, similar to `Timeline.now` in `dart:developer`
/// and `Dart_TimelineGetMicros` in the VM embedding API.
///
/// See [Timestamp] and [getVMTimeline].
Future<Timestamp> getVMTimelineMicros();
/// The `pause` RPC is used to interrupt a running isolate. The RPC enqueues
/// the interrupt request and potentially returns before the isolate is
/// paused.
///
/// When the isolate is paused an event will be sent on the `Debug` stream.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Success].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Success> pause(String isolateId);
/// The `kill` RPC is used to kill an isolate as if by dart:isolate's
/// `Isolate.kill(IMMEDIATE)`.
///
/// The isolate is killed regardless of whether it is paused or running.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Success].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Success> kill(String isolateId);
/// Registers a service that can be invoked by other VM service clients, where
/// `service` is the name of the service to advertise and `alias` is an
/// alternative name for the registered service.
///
/// Requests made to the new service will be forwarded to the client which
/// originally registered the service.
///
/// See [Success].
Future<Success> registerService(String service, String alias);
/// The `reloadSources` RPC is used to perform a hot reload of an Isolate's
/// sources.
///
/// if the `force` parameter is provided, it indicates that all of the
/// Isolate's sources should be reloaded regardless of modification time.
///
/// if the `pause` parameter is provided, the isolate will pause immediately
/// after the reload.
///
/// if the `rootLibUri` parameter is provided, it indicates the new uri to the
/// Isolate's root library.
///
/// if the `packagesUri` parameter is provided, it indicates the new uri to
/// the Isolate's package map (.packages) file.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<ReloadReport> reloadSources(
String isolateId, {
bool? force,
bool? pause,
String? rootLibUri,
String? packagesUri,
});
/// The `removeBreakpoint` RPC is used to remove a breakpoint by its `id`.
///
/// Note that breakpoints are added and removed on a per-isolate basis.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Success].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Success> removeBreakpoint(String isolateId, String breakpointId);
/// Requests a dump of the Dart heap of the given isolate.
///
/// This method immediately returns success. The VM will then begin delivering
/// binary events on the `HeapSnapshot` event stream. The binary data in these
/// events, when concatenated together, conforms to the [SnapshotGraph] type.
/// The splitting of the SnapshotGraph into events can happen at any byte
/// offset.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Success> requestHeapSnapshot(String isolateId);
/// The `resume` RPC is used to resume execution of a paused isolate.
///
/// If the `step` parameter is not provided, the program will resume regular
/// execution.
///
/// If the `step` parameter is provided, it indicates what form of
/// single-stepping to use.
///
/// step | meaning
/// ---- | -------
/// Into | Single step, entering function calls
/// Over | Single step, skipping over function calls
/// Out | Single step until the current function exits
/// Rewind | Immediately exit the top frame(s) without executing any code.
/// Isolate will be paused at the call of the last exited function.
///
/// The `frameIndex` parameter is only used when the `step` parameter is
/// Rewind. It specifies the stack frame to rewind to. Stack frame 0 is the
/// currently executing function, so `frameIndex` must be at least 1.
///
/// If the `frameIndex` parameter is not provided, it defaults to 1.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Success], [StepOption].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Success> resume(String isolateId,
{/*StepOption*/ String? step, int? frameIndex});
/// The `setBreakpointState` RPC allows for breakpoints to be enabled or
/// disabled, without requiring for the breakpoint to be completely removed.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// The returned [Breakpoint] is the updated breakpoint with its new values.
///
/// See [Breakpoint].
Future<Breakpoint> setBreakpointState(
String isolateId, String breakpointId, bool enable);
/// The `setExceptionPauseMode` RPC is used to control if an isolate pauses
/// when an exception is thrown.
///
/// mode | meaning
/// ---- | -------
/// None | Do not pause isolate on thrown exceptions
/// Unhandled | Pause isolate on unhandled exceptions
/// All | Pause isolate on all thrown exceptions
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Success> setExceptionPauseMode(
String isolateId, /*ExceptionPauseMode*/ String mode);
/// The `setFlag` RPC is used to set a VM flag at runtime. Returns an error if
/// the named flag does not exist, the flag may not be set at runtime, or the
/// value is of the wrong type for the flag.
///
/// The following flags may be set at runtime:
///
/// - pause_isolates_on_start
/// - pause_isolates_on_exit
/// - pause_isolates_on_unhandled_exceptions
/// - profile_period
/// - profiler
///
/// Notes:
///
/// - `profile_period` can be set to a minimum value of 50. Attempting to set
/// `profile_period` to a lower value will result in a value of 50 being set.
/// - Setting `profiler` will enable or disable the profiler depending on the
/// provided value. If set to false when the profiler is already running, the
/// profiler will be stopped but may not free its sample buffer depending on
/// platform limitations.
///
/// See [Success].
///
/// The return value can be one of [Success] or [Error].
Future<Response> setFlag(String name, String value);
/// The `setLibraryDebuggable` RPC is used to enable or disable whether
/// breakpoints and stepping work for a given library.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Success].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Success> setLibraryDebuggable(
String isolateId, String libraryId, bool isDebuggable);
/// The `setName` RPC is used to change the debugging name for an isolate.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Success].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Success> setName(String isolateId, String name);
/// The `setTraceClassAllocation` RPC allows for enabling or disabling
/// allocation tracing for a specific type of object. Allocation traces can be
/// retrieved with the `getAllocationTraces` RPC.
///
/// If `enable` is true, allocations of objects of the class represented by
/// `classId` will be traced.
///
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
/// [Sentinel] is returned.
///
/// See [Success].
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
Future<Success> setTraceClassAllocation(
String isolateId, String classId, bool enable);
/// The `setVMName` RPC is used to change the debugging name for the vm.
///
/// See [Success].
Future<Success> setVMName(String name);
/// The `setVMTimelineFlags` RPC is used to set which timeline streams are
/// enabled.
///
/// The `recordedStreams` parameter is the list of all timeline streams which
/// are to be enabled. Streams not explicitly specified will be disabled.
/// Invalid stream names are ignored.
///
/// A `TimelineStreamSubscriptionsUpdate` event is sent on the `Timeline`
/// stream as a result of invoking this RPC.
///
/// To get the list of currently enabled timeline streams, see
/// [getVMTimelineFlags].
///
/// See [Success].
Future<Success> setVMTimelineFlags(List<String> recordedStreams);
/// The `streamCancel` RPC cancels a stream subscription in the VM.
///
/// If the client is not subscribed to the stream, the `104` (Stream not
/// subscribed) [RPC error] code is returned.
///
/// See [Success].
Future<Success> streamCancel(String streamId);
/// The `streamListen` RPC subscribes to a stream in the VM. Once subscribed,
/// the client will begin receiving events from the stream.
///
/// If the client is already subscribed to the stream, the `103` (Stream
/// already subscribed) [RPC error] code is returned.
///
/// The `streamId` parameter may have the following published values:
///
/// streamId | event types provided
/// -------- | -----------
/// VM | VMUpdate, VMFlagUpdate
/// Isolate | IsolateStart, IsolateRunnable, IsolateExit, IsolateUpdate,
/// IsolateReload, ServiceExtensionAdded
/// Debug | PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted,
/// PauseException, PausePostRequest, Resume, BreakpointAdded,
/// BreakpointResolved, BreakpointRemoved, BreakpointUpdated, Inspect, None
/// Profiler | CpuSamples, UserTagChanged
/// GC | GC
/// Extension | Extension
/// Timeline | TimelineEvents, TimelineStreamsSubscriptionUpdate
/// Logging | Logging
/// Service | ServiceRegistered, ServiceUnregistered
/// HeapSnapshot | HeapSnapshot
///
/// Additionally, some embedders provide the `Stdout` and `Stderr` streams.
/// These streams allow the client to subscribe to writes to stdout and
/// stderr.
///
/// streamId | event types provided
/// -------- | -----------
/// Stdout | WriteEvent
/// Stderr | WriteEvent
///
/// It is considered a `backwards compatible` change to add a new type of
/// event to an existing stream. Clients should be written to handle this
/// gracefully, perhaps by warning and ignoring.
///
/// See [Success].
Future<Success> streamListen(String streamId);
}
class _PendingServiceRequest {
Future<Map<String, Object?>> get future => _completer.future;
final _completer = Completer<Map<String, Object?>>();
final dynamic originalId;
_PendingServiceRequest(this.originalId);
void complete(Map<String, Object?> response) {
response['id'] = originalId;
_completer.complete(response);
}
}
/// A Dart VM Service Protocol connection that delegates requests to a
/// [VmServiceInterface] implementation.
///
/// One of these should be created for each client, but they should generally
/// share the same [VmServiceInterface] and [ServiceExtensionRegistry]
/// instances.
class VmServerConnection {
final Stream<Map<String, Object>> _requestStream;
final StreamSink<Map<String, Object?>> _responseSink;
final ServiceExtensionRegistry _serviceExtensionRegistry;
final VmServiceInterface _serviceImplementation;
/// Used to create unique ids when acting as a proxy between clients.
int _nextServiceRequestId = 0;
/// Manages streams for `streamListen` and `streamCancel` requests.
final _streamSubscriptions = <String, StreamSubscription>{};
/// Completes when [_requestStream] is done.
Future<void> get done => _doneCompleter.future;
final _doneCompleter = Completer<void>();
/// Pending service extension requests to this client by id.
final _pendingServiceExtensionRequests = <dynamic, _PendingServiceRequest>{};
VmServerConnection(this._requestStream, this._responseSink,
this._serviceExtensionRegistry, this._serviceImplementation) {
_requestStream.listen(_delegateRequest, onDone: _doneCompleter.complete);
done.then(
(_) => _streamSubscriptions.values.forEach((sub) => sub.cancel()));
}
/// Invoked when the current client has registered some extension, and
/// another client sends an RPC request for that extension.
///
/// We don't attempt to do any serialization or deserialization of the
/// request or response in this case
Future<Map<String, Object?>> _forwardServiceExtensionRequest(
Map<String, Object?> request) {
final originalId = request['id'];
request = Map<String, Object?>.of(request);
// Modify the request ID to ensure we don't have conflicts between
// multiple clients ids.
final newId = '${_nextServiceRequestId++}:$originalId';
request['id'] = newId;
var pendingRequest = _PendingServiceRequest(originalId);
_pendingServiceExtensionRequests[newId] = pendingRequest;
_responseSink.add(request);
return pendingRequest.future;
}
void _delegateRequest(Map<String, Object?> request) async {
try {
var id = request['id'];
// Check if this is actually a response to a pending request.
if (_pendingServiceExtensionRequests.containsKey(id)) {
final pending = _pendingServiceExtensionRequests[id]!;
pending.complete(Map<String, Object?>.of(request));
return;
}
final method = request['method'] as String?;
if (method == null) {
throw RPCError(
null, RPCError.kInvalidRequest, 'Invalid Request', request);
}
final params = request['params'] as Map<String, dynamic>?;
late Response response;
switch (method) {
case 'registerService':
_serviceExtensionRegistry.registerExtension(params!['service'], this);
response = Success();
break;
case 'addBreakpoint':
response = await _serviceImplementation.addBreakpoint(
params!['isolateId'],
params['scriptId'],
params['line'],
column: params['column'],
);
break;
case 'addBreakpointWithScriptUri':
response = await _serviceImplementation.addBreakpointWithScriptUri(
params!['isolateId'],
params['scriptUri'],
params['line'],
column: params['column'],
);
break;
case 'addBreakpointAtEntry':
response = await _serviceImplementation.addBreakpointAtEntry(
params!['isolateId'],
params['functionId'],
);
break;
case 'clearCpuSamples':
response = await _serviceImplementation.clearCpuSamples(
params!['isolateId'],
);
break;
case 'clearVMTimeline':
response = await _serviceImplementation.clearVMTimeline();
break;
case 'invoke':
response = await _serviceImplementation.invoke(
params!['isolateId'],
params['targetId'],
params['selector'],
List<String>.from(params['argumentIds'] ?? []),
disableBreakpoints: params['disableBreakpoints'],
);
break;
case 'evaluate':
response = await _serviceImplementation.evaluate(
params!['isolateId'],
params['targetId'],
params['expression'],
scope: params['scope']?.cast<String, String>(),
disableBreakpoints: params['disableBreakpoints'],
);
break;
case 'evaluateInFrame':
response = await _serviceImplementation.evaluateInFrame(
params!['isolateId'],
params['frameIndex'],
params['expression'],
scope: params['scope']?.cast<String, String>(),
disableBreakpoints: params['disableBreakpoints'],
);
break;
case 'getAllocationProfile':
response = await _serviceImplementation.getAllocationProfile(
params!['isolateId'],
reset: params['reset'],
gc: params['gc'],
);
break;
case 'getAllocationTraces':
response = await _serviceImplementation.getAllocationTraces(
params!['isolateId'],
timeOriginMicros: params['timeOriginMicros'],
timeExtentMicros: params['timeExtentMicros'],
classId: params['classId'],
);
break;
case 'getClassList':
response = await _serviceImplementation.getClassList(
params!['isolateId'],
);
break;
case 'getCpuSamples':
response = await _serviceImplementation.getCpuSamples(
params!['isolateId'],
params['timeOriginMicros'],
params['timeExtentMicros'],
);
break;
case 'getFlagList':
response = await _serviceImplementation.getFlagList();
break;
case 'getInboundReferences':
response = await _serviceImplementation.getInboundReferences(
params!['isolateId'],
params['targetId'],
params['limit'],
);
break;
case 'getInstances':
response = await _serviceImplementation.getInstances(
params!['isolateId'],
params['objectId'],
params['limit'],
);
break;
case 'getIsolate':
response = await _serviceImplementation.getIsolate(
params!['isolateId'],
);
break;
case 'getIsolateGroup':
response = await _serviceImplementation.getIsolateGroup(
params!['isolateGroupId'],
);
break;
case 'getMemoryUsage':
response = await _serviceImplementation.getMemoryUsage(
params!['isolateId'],
);
break;
case 'getIsolateGroupMemoryUsage':
response = await _serviceImplementation.getIsolateGroupMemoryUsage(
params!['isolateGroupId'],
);
break;
case 'getScripts':
response = await _serviceImplementation.getScripts(
params!['isolateId'],
);
break;
case 'getObject':
response = await _serviceImplementation.getObject(
params!['isolateId'],
params['objectId'],
offset: params['offset'],
count: params['count'],
);
break;
case 'getPorts':
response = await _serviceImplementation.getPorts(
params!['isolateId'],
);
break;
case 'getRetainingPath':
response = await _serviceImplementation.getRetainingPath(
params!['isolateId'],
params['targetId'],
params['limit'],
);
break;
case 'getProcessMemoryUsage':
response = await _serviceImplementation.getProcessMemoryUsage();
break;
case 'getStack':
response = await _serviceImplementation.getStack(
params!['isolateId'],
limit: params['limit'],
);
break;
case 'getSupportedProtocols':
response = await _serviceImplementation.getSupportedProtocols();
break;
case 'getSourceReport':
response = await _serviceImplementation.getSourceReport(
params!['isolateId'],
List<String>.from(params['reports'] ?? []),
scriptId: params['scriptId'],
tokenPos: params['tokenPos'],
endTokenPos: params['endTokenPos'],
forceCompile: params['forceCompile'],
reportLines: params['reportLines'],
);
break;
case 'getVersion':
response = await _serviceImplementation.getVersion();
break;
case 'getVM':
response = await _serviceImplementation.getVM();
break;
case 'getVMTimeline':
response = await _serviceImplementation.getVMTimeline(
timeOriginMicros: params!['timeOriginMicros'],
timeExtentMicros: params['timeExtentMicros'],
);
break;
case 'getVMTimelineFlags':
response = await _serviceImplementation.getVMTimelineFlags();
break;
case 'getVMTimelineMicros':
response = await _serviceImplementation.getVMTimelineMicros();
break;
case 'pause':
response = await _serviceImplementation.pause(
params!['isolateId'],
);
break;
case 'kill':
response = await _serviceImplementation.kill(
params!['isolateId'],
);
break;
case 'reloadSources':
response = await _serviceImplementation.reloadSources(
params!['isolateId'],
force: params['force'],
pause: params['pause'],
rootLibUri: params['rootLibUri'],
packagesUri: params['packagesUri'],
);
break;
case 'removeBreakpoint':
response = await _serviceImplementation.removeBreakpoint(
params!['isolateId'],
params['breakpointId'],
);
break;
case 'requestHeapSnapshot':
response = await _serviceImplementation.requestHeapSnapshot(
params!['isolateId'],
);
break;
case 'resume':
response = await _serviceImplementation.resume(
params!['isolateId'],
step: params['step'],
frameIndex: params['frameIndex'],
);
break;
case 'setBreakpointState':
response = await _serviceImplementation.setBreakpointState(
params!['isolateId'],
params['breakpointId'],
params['enable'],
);
break;
case 'setExceptionPauseMode':
response = await _serviceImplementation.setExceptionPauseMode(
params!['isolateId'],
params['mode'],
);
break;
case 'setFlag':
response = await _serviceImplementation.setFlag(
params!['name'],
params['value'],
);
break;
case 'setLibraryDebuggable':
response = await _serviceImplementation.setLibraryDebuggable(
params!['isolateId'],
params['libraryId'],
params['isDebuggable'],
);
break;
case 'setName':
response = await _serviceImplementation.setName(
params!['isolateId'],
params['name'],
);
break;
case 'setTraceClassAllocation':
response = await _serviceImplementation.setTraceClassAllocation(
params!['isolateId'],
params['classId'],
params['enable'],
);
break;
case 'setVMName':
response = await _serviceImplementation.setVMName(
params!['name'],
);
break;
case 'setVMTimelineFlags':
response = await _serviceImplementation.setVMTimelineFlags(
List<String>.from(params!['recordedStreams'] ?? []),
);
break;
case 'streamCancel':
var id = params!['streamId'];
var existing = _streamSubscriptions.remove(id);
if (existing == null) {
throw RPCError.withDetails(
'streamCancel',
104,
'Stream not subscribed',
details: "The stream '$id' is not subscribed",
);
}
await existing.cancel();
response = Success();
break;
case 'streamListen':
var id = params!['streamId'];
if (_streamSubscriptions.containsKey(id)) {
throw RPCError.withDetails(
'streamListen',
103,
'Stream already subscribed',
details: "The stream '$id' is already subscribed",
);
}
var stream = id == 'Service'
? _serviceExtensionRegistry.onExtensionEvent
: _serviceImplementation.onEvent(id);
_streamSubscriptions[id] = stream.listen((e) {
_responseSink.add({
'jsonrpc': '2.0',
'method': 'streamNotify',
'params': {
'streamId': id,
'event': e.toJson(),
},
});
});
response = Success();
break;
default:
final registeredClient = _serviceExtensionRegistry.clientFor(method);
if (registeredClient != null) {
// Check for any client which has registered this extension, if we
// have one then delegate the request to that client.
_responseSink.add(await registeredClient
._forwardServiceExtensionRequest(request));
// Bail out early in this case, we are just acting as a proxy and
// never get a `Response` instance.
return;
} else if (method.startsWith('ext.')) {
// Remaining methods with `ext.` are assumed to be registered via
// dart:developer, which the service implementation handles.
final args =
params == null ? null : Map<String, dynamic>.of(params);
final isolateId = args?.remove('isolateId');
response = await _serviceImplementation.callServiceExtension(method,
isolateId: isolateId, args: args);
} else {
throw RPCError(
method, RPCError.kMethodNotFound, 'Method not found', request);
}
}
_responseSink.add({
'jsonrpc': '2.0',
'id': id,
'result': response.toJson(),
});
} catch (e, st) {
final error = e is RPCError
? e.toMap()
: {
'code': RPCError.kInternalError,
'message': '${request['method']}: $e',
'data': {'details': '$st'},
};
_responseSink.add({
'jsonrpc': '2.0',
'id': request['id'],
'error': error,
});
}
}
}
class _OutstandingRequest<T> {
_OutstandingRequest(this.method);
static int _idCounter = 0;
final String id = '${_idCounter++}';
final String method;
final StackTrace _stackTrace = StackTrace.current;
final Completer<T> _completer = Completer<T>();
Future<T> get future => _completer.future;
void complete(T value) => _completer.complete(value);
void completeError(Object error) =>
_completer.completeError(error, _stackTrace);
}
class VmService implements VmServiceInterface {
late final StreamSubscription _streamSub;
late final Function _writeMessage;
final Map<String, _OutstandingRequest> _outstandingRequests = {};
Map<String, ServiceCallback> _services = {};
late final Log _log;
StreamController<String> _onSend = StreamController.broadcast(sync: true);
StreamController<String> _onReceive = StreamController.broadcast(sync: true);
final Completer _onDoneCompleter = Completer();
Map<String, StreamController<Event>> _eventControllers = {};
StreamController<Event> _getEventController(String eventName) {
StreamController<Event>? controller = _eventControllers[eventName];
if (controller == null) {
controller = StreamController.broadcast();
_eventControllers[eventName] = controller;
}
return controller;
}
late final DisposeHandler? _disposeHandler;
VmService(
Stream<dynamic> /*String|List<int>*/ inStream,
void writeMessage(String message), {
Log? log,
DisposeHandler? disposeHandler,
Future? streamClosed,
}) {
_streamSub = inStream.listen(_processMessage,
onDone: () => _onDoneCompleter.complete());
_writeMessage = writeMessage;
_log = log == null ? _NullLog() : log;
_disposeHandler = disposeHandler;
streamClosed?.then((_) {
if (!_onDoneCompleter.isCompleted) {
_onDoneCompleter.complete();
}
});
}
@override
Stream<Event> onEvent(String streamId) =>
_getEventController(streamId).stream;
// VMUpdate, VMFlagUpdate
Stream<Event> get onVMEvent => _getEventController('VM').stream;
// IsolateStart, IsolateRunnable, IsolateExit, IsolateUpdate, IsolateReload, ServiceExtensionAdded
Stream<Event> get onIsolateEvent => _getEventController('Isolate').stream;
// PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted, PauseException, PausePostRequest, Resume, BreakpointAdded, BreakpointResolved, BreakpointRemoved, BreakpointUpdated, Inspect, None
Stream<Event> get onDebugEvent => _getEventController('Debug').stream;
// CpuSamples, UserTagChanged
Stream<Event> get onProfilerEvent => _getEventController('Profiler').stream;
// GC
Stream<Event> get onGCEvent => _getEventController('GC').stream;
// Extension
Stream<Event> get onExtensionEvent => _getEventController('Extension').stream;
// TimelineEvents, TimelineStreamsSubscriptionUpdate
Stream<Event> get onTimelineEvent => _getEventController('Timeline').stream;
// Logging
Stream<Event> get onLoggingEvent => _getEventController('Logging').stream;
// ServiceRegistered, ServiceUnregistered
Stream<Event> get onServiceEvent => _getEventController('Service').stream;
// HeapSnapshot
Stream<Event> get onHeapSnapshotEvent =>
_getEventController('HeapSnapshot').stream;
// WriteEvent
Stream<Event> get onStdoutEvent => _getEventController('Stdout').stream;
// WriteEvent
Stream<Event> get onStderrEvent => _getEventController('Stderr').stream;
@override
Future<Breakpoint> addBreakpoint(
String isolateId,
String scriptId,
int line, {
int? column,
}) =>
_call('addBreakpoint', {
'isolateId': isolateId,
'scriptId': scriptId,
'line': line,
if (column != null) 'column': column,
});
@override
Future<Breakpoint> addBreakpointWithScriptUri(
String isolateId,
String scriptUri,
int line, {
int? column,
}) =>
_call('addBreakpointWithScriptUri', {
'isolateId': isolateId,
'scriptUri': scriptUri,
'line': line,
if (column != null) 'column': column,
});
@override
Future<Breakpoint> addBreakpointAtEntry(
String isolateId, String functionId) =>
_call('addBreakpointAtEntry',
{'isolateId': isolateId, 'functionId': functionId});
@override
Future<Success> clearCpuSamples(String isolateId) =>
_call('clearCpuSamples', {'isolateId': isolateId});
@override
Future<Success> clearVMTimeline() => _call('clearVMTimeline');
@override
Future<Response> invoke(
String isolateId,
String targetId,
String selector,
List<String> argumentIds, {
bool? disableBreakpoints,
}) =>
_call('invoke', {
'isolateId': isolateId,
'targetId': targetId,
'selector': selector,
'argumentIds': argumentIds,
if (disableBreakpoints != null)
'disableBreakpoints': disableBreakpoints,
});
@override
Future<Response> evaluate(
String isolateId,
String targetId,
String expression, {
Map<String, String>? scope,
bool? disableBreakpoints,
}) =>
_call('evaluate', {
'isolateId': isolateId,
'targetId': targetId,
'expression': expression,
if (scope != null) 'scope': scope,
if (disableBreakpoints != null)
'disableBreakpoints': disableBreakpoints,
});
@override
Future<Response> evaluateInFrame(
String isolateId,
int frameIndex,
String expression, {
Map<String, String>? scope,
bool? disableBreakpoints,
}) =>
_call('evaluateInFrame', {
'isolateId': isolateId,
'frameIndex': frameIndex,
'expression': expression,
if (scope != null) 'scope': scope,
if (disableBreakpoints != null)
'disableBreakpoints': disableBreakpoints,
});
@override
Future<AllocationProfile> getAllocationProfile(String isolateId,
{bool? reset, bool? gc}) =>
_call('getAllocationProfile', {
'isolateId': isolateId,
if (reset != null && reset) 'reset': reset,
if (gc != null && gc) 'gc': gc,
});
@override
Future<CpuSamples> getAllocationTraces(
String isolateId, {
int? timeOriginMicros,
int? timeExtentMicros,
String? classId,
}) =>
_call('getAllocationTraces', {
'isolateId': isolateId,
if (timeOriginMicros != null) 'timeOriginMicros': timeOriginMicros,
if (timeExtentMicros != null) 'timeExtentMicros': timeExtentMicros,
if (classId != null) 'classId': classId,
});
@override
Future<ClassList> getClassList(String isolateId) =>
_call('getClassList', {'isolateId': isolateId});
@override
Future<CpuSamples> getCpuSamples(
String isolateId, int timeOriginMicros, int timeExtentMicros) =>
_call('getCpuSamples', {
'isolateId': isolateId,
'timeOriginMicros': timeOriginMicros,
'timeExtentMicros': timeExtentMicros
});
@override
Future<FlagList> getFlagList() => _call('getFlagList');
@override
Future<InboundReferences> getInboundReferences(
String isolateId, String targetId, int limit) =>
_call('getInboundReferences',
{'isolateId': isolateId, 'targetId': targetId, 'limit': limit});
@override
Future<InstanceSet> getInstances(
String isolateId, String objectId, int limit) =>
_call('getInstances',
{'isolateId': isolateId, 'objectId': objectId, 'limit': limit});
@override
Future<Isolate> getIsolate(String isolateId) =>
_call('getIsolate', {'isolateId': isolateId});
@override
Future<IsolateGroup> getIsolateGroup(String isolateGroupId) =>
_call('getIsolateGroup', {'isolateGroupId': isolateGroupId});
@override
Future<MemoryUsage> getMemoryUsage(String isolateId) =>
_call('getMemoryUsage', {'isolateId': isolateId});
@override
Future<MemoryUsage> getIsolateGroupMemoryUsage(String isolateGroupId) =>
_call('getIsolateGroupMemoryUsage', {'isolateGroupId': isolateGroupId});
@override
Future<ScriptList> getScripts(String isolateId) =>
_call('getScripts', {'isolateId': isolateId});
@override
Future<Obj> getObject(
String isolateId,
String objectId, {
int? offset,
int? count,
}) =>
_call('getObject', {
'isolateId': isolateId,
'objectId': objectId,
if (offset != null) 'offset': offset,
if (count != null) 'count': count,
});
@override
Future<PortList> getPorts(String isolateId) =>
_call('getPorts', {'isolateId': isolateId});
@override
Future<RetainingPath> getRetainingPath(
String isolateId, String targetId, int limit) =>
_call('getRetainingPath',
{'isolateId': isolateId, 'targetId': targetId, 'limit': limit});
@override
Future<ProcessMemoryUsage> getProcessMemoryUsage() =>
_call('getProcessMemoryUsage');
@override
Future<Stack> getStack(String isolateId, {int? limit}) => _call('getStack', {
'isolateId': isolateId,
if (limit != null) 'limit': limit,
});
@override
Future<ProtocolList> getSupportedProtocols() =>
_call('getSupportedProtocols');
@override
Future<SourceReport> getSourceReport(
String isolateId,
/*List<SourceReportKind>*/ List<String> reports, {
String? scriptId,
int? tokenPos,
int? endTokenPos,
bool? forceCompile,
bool? reportLines,
}) =>
_call('getSourceReport', {
'isolateId': isolateId,
'reports': reports,
if (scriptId != null) 'scriptId': scriptId,
if (tokenPos != null) 'tokenPos': tokenPos,
if (endTokenPos != null) 'endTokenPos': endTokenPos,
if (forceCompile != null) 'forceCompile': forceCompile,
if (reportLines != null) 'reportLines': reportLines,
});
@override
Future<Version> getVersion() => _call('getVersion');
@override
Future<VM> getVM() => _call('getVM');
@override
Future<Timeline> getVMTimeline(
{int? timeOriginMicros, int? timeExtentMicros}) =>
_call('getVMTimeline', {
if (timeOriginMicros != null) 'timeOriginMicros': timeOriginMicros,
if (timeExtentMicros != null) 'timeExtentMicros': timeExtentMicros,
});
@override
Future<TimelineFlags> getVMTimelineFlags() => _call('getVMTimelineFlags');
@override
Future<Timestamp> getVMTimelineMicros() => _call('getVMTimelineMicros');
@override
Future<Success> pause(String isolateId) =>
_call('pause', {'isolateId': isolateId});
@override
Future<Success> kill(String isolateId) =>
_call('kill', {'isolateId': isolateId});
@override
Future<Success> registerService(String service, String alias) =>
_call('registerService', {'service': service, 'alias': alias});
@override
Future<ReloadReport> reloadSources(
String isolateId, {
bool? force,
bool? pause,
String? rootLibUri,
String? packagesUri,
}) =>
_call('reloadSources', {
'isolateId': isolateId,
if (force != null) 'force': force,
if (pause != null) 'pause': pause,
if (rootLibUri != null) 'rootLibUri': rootLibUri,
if (packagesUri != null) 'packagesUri': packagesUri,
});
@override
Future<Success> removeBreakpoint(String isolateId, String breakpointId) =>
_call('removeBreakpoint',
{'isolateId': isolateId, 'breakpointId': breakpointId});
@override
Future<Success> requestHeapSnapshot(String isolateId) =>
_call('requestHeapSnapshot', {'isolateId': isolateId});
@override
Future<Success> resume(String isolateId,
{/*StepOption*/ String? step, int? frameIndex}) =>
_call('resume', {
'isolateId': isolateId,
if (step != null) 'step': step,
if (frameIndex != null) 'frameIndex': frameIndex,
});
@override
Future<Breakpoint> setBreakpointState(
String isolateId, String breakpointId, bool enable) =>
_call('setBreakpointState', {
'isolateId': isolateId,
'breakpointId': breakpointId,
'enable': enable
});
@override
Future<Success> setExceptionPauseMode(
String isolateId, /*ExceptionPauseMode*/ String mode) =>
_call('setExceptionPauseMode', {'isolateId': isolateId, 'mode': mode});
@override
Future<Response> setFlag(String name, String value) =>
_call('setFlag', {'name': name, 'value': value});
@override
Future<Success> setLibraryDebuggable(
String isolateId, String libraryId, bool isDebuggable) =>
_call('setLibraryDebuggable', {
'isolateId': isolateId,
'libraryId': libraryId,
'isDebuggable': isDebuggable
});
@override
Future<Success> setName(String isolateId, String name) =>
_call('setName', {'isolateId': isolateId, 'name': name});
@override
Future<Success> setTraceClassAllocation(
String isolateId, String classId, bool enable) =>
_call('setTraceClassAllocation',
{'isolateId': isolateId, 'classId': classId, 'enable': enable});
@override
Future<Success> setVMName(String name) => _call('setVMName', {'name': name});
@override
Future<Success> setVMTimelineFlags(List<String> recordedStreams) =>
_call('setVMTimelineFlags', {'recordedStreams': recordedStreams});
@override
Future<Success> streamCancel(String streamId) =>
_call('streamCancel', {'streamId': streamId});
@override
Future<Success> streamListen(String streamId) =>
_call('streamListen', {'streamId': streamId});
/// Call an arbitrary service protocol method. This allows clients to call
/// methods not explicitly exposed by this library.
Future<Response> callMethod(String method,
{String? isolateId, Map<String, dynamic>? args}) {
return callServiceExtension(method, isolateId: isolateId, args: args);
}
/// Invoke a specific service protocol extension method.
///
/// See https://api.dart.dev/stable/dart-developer/dart-developer-library.html.
@override
Future<Response> callServiceExtension(String method,
{String? isolateId, Map<String, dynamic>? args}) {
if (args == null && isolateId == null) {
return _call(method);
} else if (args == null) {
return _call(method, {'isolateId': isolateId!});
} else {
args = Map.from(args);
if (isolateId != null) {
args['isolateId'] = isolateId;
}
return _call(method, args);
}
}
Stream<String> get onSend => _onSend.stream;
Stream<String> get onReceive => _onReceive.stream;
Future<void> dispose() async {
await _streamSub.cancel();
_outstandingRequests.forEach((id, request) {
request._completer.completeError(RPCError(
request.method,
RPCError.kServerError,
'Service connection disposed',
));
});
_outstandingRequests.clear();
if (_disposeHandler != null) {
await _disposeHandler!();
}
if (!_onDoneCompleter.isCompleted) {
_onDoneCompleter.complete();
}
}
Future get onDone => _onDoneCompleter.future;
Future<T> _call<T>(String method, [Map args = const {}]) async {
final request = _OutstandingRequest(method);
_outstandingRequests[request.id] = request;
Map m = {
'jsonrpc': '2.0',
'id': request.id,
'method': method,
'params': args,
};
String message = jsonEncode(m);
_onSend.add(message);
_writeMessage(message);
return await request.future as T;
}
/// Register a service for invocation.
void registerServiceCallback(String service, ServiceCallback cb) {
if (_services.containsKey(service)) {
throw Exception('Service \'${service}\' already registered');
}
_services[service] = cb;
}
void _processMessage(dynamic message) {
// Expect a String, an int[], or a ByteData.
if (message is String) {
_processMessageStr(message);
} else if (message is List<int>) {
Uint8List list = Uint8List.fromList(message);
_processMessageByteData(ByteData.view(list.buffer));
} else if (message is ByteData) {
_processMessageByteData(message);
} else {
_log.warning('unknown message type: ${message.runtimeType}');
}
}
void _processMessageByteData(ByteData bytes) {
final int metaOffset = 4;
final int dataOffset = bytes.getUint32(0, Endian.little);
final metaLength = dataOffset - metaOffset;
final dataLength = bytes.lengthInBytes - dataOffset;
final meta = utf8.decode(Uint8List.view(
bytes.buffer, bytes.offsetInBytes + metaOffset, metaLength));
final data = ByteData.view(
bytes.buffer, bytes.offsetInBytes + dataOffset, dataLength);
dynamic map = jsonDecode(meta)!;
if (map['method'] == 'streamNotify') {
String streamId = map['params']['streamId'];
Map event = map['params']['event'];
event['data'] = data;
_getEventController(streamId)
.add(createServiceObject(event, const ['Event'])! as Event);
}
}
void _processMessageStr(String message) {
late Map<String, dynamic> json;
try {
_onReceive.add(message);
json = jsonDecode(message)!;
} catch (e, s) {
_log.severe('unable to decode message: ${message}, ${e}\n${s}');
return;
}
if (json.containsKey('method')) {
if (json.containsKey('id')) {
_processRequest(json);
} else {
_processNotification(json);
}
} else if (json.containsKey('id') &&
(json.containsKey('result') || json.containsKey('error'))) {
_processResponse(json);
} else {
_log.severe('unknown message type: ${message}');
}
}
void _processResponse(Map<String, dynamic> json) {
final request = _outstandingRequests.remove(json['id']);
if (request == null) {
_log.severe('unmatched request response: ${jsonEncode(json)}');
} else if (json['error'] != null) {
request.completeError(RPCError.parse(request.method, json['error']));
} else {
Map<String, dynamic> result = json['result'] as Map<String, dynamic>;
String? type = result['type'];
if (type == 'Sentinel') {
request.completeError(SentinelException.parse(request.method, result));
} else if (_typeFactories[type] == null) {
request.complete(Response.parse(result));
} else {
List<String> returnTypes = _methodReturnTypes[request.method] ?? [];
request.complete(createServiceObject(result, returnTypes));
}
}
}
Future _processRequest(Map<String, dynamic> json) async {
final Map m = await _routeRequest(
json['method'], json['params'] ?? <String, dynamic>{});
m['id'] = json['id'];
m['jsonrpc'] = '2.0';
String message = jsonEncode(m);
_onSend.add(message);
_writeMessage(message);
}
Future _processNotification(Map<String, dynamic> json) async {
final String method = json['method'];
final Map<String, dynamic> params = json['params'] ?? <String, dynamic>{};
if (method == 'streamNotify') {
String streamId = params['streamId'];
_getEventController(streamId)
.add(createServiceObject(params['event'], const ['Event'])! as Event);
} else {
await _routeRequest(method, params);
}
}
Future<Map> _routeRequest(String method, Map<String, dynamic> params) async {
final service = _services[method];
if (service == null) {
RPCError error = RPCError(
method, RPCError.kMethodNotFound, 'method not found \'$method\'');
return {'error': error.toMap()};
}
try {
return await service(params);
} catch (e, st) {
RPCError error = RPCError.withDetails(
method,
RPCError.kServerError,
'$e',
details: '$st',
);
return {'error': error.toMap()};
}
}
}
typedef DisposeHandler = Future Function();
class RPCError implements Exception {
/// Application specific error codes.
static const int kServerError = -32000;
/// The JSON sent is not a valid Request object.
static const int kInvalidRequest = -32600;
/// The method does not exist or is not available.
static const int kMethodNotFound = -32601;
/// Invalid method parameter(s), such as a mismatched type.
static const int kInvalidParams = -32602;
/// Internal JSON-RPC error.
static const int kInternalError = -32603;
static RPCError parse(String callingMethod, dynamic json) {
return RPCError(callingMethod, json['code'], json['message'], json['data']);
}
final String? callingMethod;
final int code;
final String message;
final Map? data;
RPCError(this.callingMethod, this.code, this.message, [this.data]);
RPCError.withDetails(this.callingMethod, this.code, this.message,
{Object? details})
: data = details == null ? null : <String, dynamic>{} {
if (details != null) {
data!['details'] = details;
}
}
String? get details => data == null ? null : data!['details'];
/// Return a map representation of this error suitable for converstion to
/// json.
Map<String, dynamic> toMap() {
Map<String, dynamic> map = {
'code': code,
'message': message,
};
if (data != null) {
map['data'] = data;
}
return map;
}
String toString() {
if (details == null) {
return '$callingMethod: ($code) $message';
} else {
return '$callingMethod: ($code) $message\n$details';
}
}
}
/// Thrown when an RPC response is a [Sentinel].
class SentinelException implements Exception {
final String callingMethod;
final Sentinel sentinel;
SentinelException.parse(this.callingMethod, Map<String, dynamic> data)
: sentinel = Sentinel.parse(data)!;
String toString() => '$sentinel from ${callingMethod}()';
}
/// An `ExtensionData` is an arbitrary map that can have any contents.
class ExtensionData {
static ExtensionData? parse(Map<String, dynamic>? json) =>
json == null ? null : ExtensionData._fromJson(json);
final Map<String, dynamic> data;
ExtensionData() : data = {};
ExtensionData._fromJson(this.data);
String toString() => '[ExtensionData ${data}]';
}
/// A logging handler you can pass to a [VmService] instance in order to get
/// notifications of non-fatal service protocol warnings and errors.
abstract class Log {
/// Log a warning level message.
void warning(String message);
/// Log an error level message.
void severe(String message);
}
class _NullLog implements Log {
void warning(String message) {}
void severe(String message) {}
}
// enums
class CodeKind {
CodeKind._();
static const String kDart = 'Dart';
static const String kNative = 'Native';
static const String kStub = 'Stub';
static const String kTag = 'Tag';
static const String kCollected = 'Collected';
}
class ErrorKind {
ErrorKind._();
/// The isolate has encountered an unhandled Dart exception.
static const String kUnhandledException = 'UnhandledException';
/// The isolate has encountered a Dart language error in the program.
static const String kLanguageError = 'LanguageError';
/// The isolate has encountered an internal error. These errors should be
/// reported as bugs.
static const String kInternalError = 'InternalError';
/// The isolate has been terminated by an external source.
static const String kTerminationError = 'TerminationError';
}
/// An enum of available event streams.
class EventStreams {
EventStreams._();
static const String kVM = 'VM';
static const String kIsolate = 'Isolate';
static const String kDebug = 'Debug';
static const String kProfiler = 'Profiler';
static const String kGC = 'GC';
static const String kExtension = 'Extension';
static const String kTimeline = 'Timeline';
static const String kLogging = 'Logging';
static const String kService = 'Service';
static const String kHeapSnapshot = 'HeapSnapshot';
static const String kStdout = 'Stdout';
static const String kStderr = 'Stderr';
}
/// Adding new values to `EventKind` is considered a backwards compatible
/// change. Clients should ignore unrecognized events.
class EventKind {
EventKind._();
/// Notification that VM identifying information has changed. Currently used
/// to notify of changes to the VM debugging name via setVMName.
static const String kVMUpdate = 'VMUpdate';
/// Notification that a VM flag has been changed via the service protocol.
static const String kVMFlagUpdate = 'VMFlagUpdate';
/// Notification that a new isolate has started.
static const String kIsolateStart = 'IsolateStart';
/// Notification that an isolate is ready to run.
static const String kIsolateRunnable = 'IsolateRunnable';
/// Notification that an isolate has exited.
static const String kIsolateExit = 'IsolateExit';
/// Notification that isolate identifying information has changed. Currently
/// used to notify of changes to the isolate debugging name via setName.
static const String kIsolateUpdate = 'IsolateUpdate';
/// Notification that an isolate has been reloaded.
static const String kIsolateReload = 'IsolateReload';
/// Notification that an extension RPC was registered on an isolate.
static const String kServiceExtensionAdded = 'ServiceExtensionAdded';
/// An isolate has paused at start, before executing code.
static const String kPauseStart = 'PauseStart';
/// An isolate has paused at exit, before terminating.
static const String kPauseExit = 'PauseExit';
/// An isolate has paused at a breakpoint or due to stepping.
static const String kPauseBreakpoint = 'PauseBreakpoint';
/// An isolate has paused due to interruption via pause.
static const String kPauseInterrupted = 'PauseInterrupted';
/// An isolate has paused due to an exception.
static const String kPauseException = 'PauseException';
/// An isolate has paused after a service request.
static const String kPausePostRequest = 'PausePostRequest';
/// An isolate has started or resumed execution.
static const String kResume = 'Resume';
/// Indicates an isolate is not yet runnable. Only appears in an Isolate's
/// pauseEvent. Never sent over a stream.
static const String kNone = 'None';
/// A breakpoint has been added for an isolate.
static const String kBreakpointAdded = 'BreakpointAdded';
/// An unresolved breakpoint has been resolved for an isolate.
static const String kBreakpointResolved = 'BreakpointResolved';
/// A breakpoint has been removed.
static const String kBreakpointRemoved = 'BreakpointRemoved';
/// A breakpoint has been updated.
static const String kBreakpointUpdated = 'BreakpointUpdated';
/// A garbage collection event.
static const String kGC = 'GC';
/// Notification of bytes written, for example, to stdout/stderr.
static const String kWriteEvent = 'WriteEvent';
/// Notification from dart:developer.inspect.
static const String kInspect = 'Inspect';
/// Event from dart:developer.postEvent.
static const String kExtension = 'Extension';
/// Event from dart:developer.log.
static const String kLogging = 'Logging';
/// A block of timeline events has been completed.
///
/// This service event is not sent for individual timeline events. It is
/// subject to buffering, so the most recent timeline events may never be
/// included in any TimelineEvents event if no timeline events occur later to
/// complete the block.
static const String kTimelineEvents = 'TimelineEvents';
/// The set of active timeline streams was changed via `setVMTimelineFlags`.
static const String kTimelineStreamSubscriptionsUpdate =
'TimelineStreamSubscriptionsUpdate';
/// Notification that a Service has been registered into the Service Protocol
/// from another client.
static const String kServiceRegistered = 'ServiceRegistered';
/// Notification that a Service has been removed from the Service Protocol
/// from another client.
static const String kServiceUnregistered = 'ServiceUnregistered';
/// Notification that the UserTag for an isolate has been changed.
static const String kUserTagChanged = 'UserTagChanged';
/// A block of recently collected CPU samples.
static const String kCpuSamples = 'CpuSamples';
}
/// Adding new values to `InstanceKind` is considered a backwards compatible
/// change. Clients should treat unrecognized instance kinds as `PlainInstance`.
class InstanceKind {
InstanceKind._();
/// A general instance of the Dart class Object.
static const String kPlainInstance = 'PlainInstance';
/// null instance.
static const String kNull = 'Null';
/// true or false.
static const String kBool = 'Bool';
/// An instance of the Dart class double.
static const String kDouble = 'Double';
/// An instance of the Dart class int.
static const String kInt = 'Int';
/// An instance of the Dart class String.
static const String kString = 'String';
/// An instance of the built-in VM List implementation. User-defined Lists
/// will be PlainInstance.
static const String kList = 'List';
/// An instance of the built-in VM Map implementation. User-defined Maps will
/// be PlainInstance.
static const String kMap = 'Map';
/// Vector instance kinds.
static const String kFloat32x4 = 'Float32x4';
static const String kFloat64x2 = 'Float64x2';
static const String kInt32x4 = 'Int32x4';
/// An instance of the built-in VM TypedData implementations. User-defined
/// TypedDatas will be PlainInstance.
static const String kUint8ClampedList = 'Uint8ClampedList';
static const String kUint8List = 'Uint8List';
static const String kUint16List = 'Uint16List';
static const String kUint32List = 'Uint32List';
static const String kUint64List = 'Uint64List';
static const String kInt8List = 'Int8List';
static const String kInt16List = 'Int16List';
static const String kInt32List = 'Int32List';
static const String kInt64List = 'Int64List';
static const String kFloat32List = 'Float32List';
static const String kFloat64List = 'Float64List';
static const String kInt32x4List = 'Int32x4List';
static const String kFloat32x4List = 'Float32x4List';
static const String kFloat64x2List = 'Float64x2List';
/// An instance of the Dart class StackTrace.
static const String kStackTrace = 'StackTrace';
/// An instance of the built-in VM Closure implementation. User-defined
/// Closures will be PlainInstance.
static const String kClosure = 'Closure';
/// An instance of the Dart class MirrorReference.
static const String kMirrorReference = 'MirrorReference';
/// An instance of the Dart class RegExp.
static const String kRegExp = 'RegExp';
/// An instance of the Dart class WeakProperty.
static const String kWeakProperty = 'WeakProperty';
/// An instance of the Dart class Type.
static const String kType = 'Type';
/// An instance of the Dart class TypeParameter.
static const String kTypeParameter = 'TypeParameter';
/// An instance of the Dart class TypeRef.
static const String kTypeRef = 'TypeRef';
/// An instance of the Dart class FunctionType.
static const String kFunctionType = 'FunctionType';
/// An instance of the Dart class BoundedType.
static const String kBoundedType = 'BoundedType';
/// An instance of the Dart class ReceivePort.
static const String kReceivePort = 'ReceivePort';
}
/// A `SentinelKind` is used to distinguish different kinds of `Sentinel`
/// objects.
///
/// Adding new values to `SentinelKind` is considered a backwards compatible
/// change. Clients must handle this gracefully.
class SentinelKind {
SentinelKind._();
/// Indicates that the object referred to has been collected by the GC.
static const String kCollected = 'Collected';
/// Indicates that an object id has expired.
static const String kExpired = 'Expired';
/// Indicates that a variable or field has not been initialized.
static const String kNotInitialized = 'NotInitialized';
/// Indicates that a variable or field is in the process of being initialized.
static const String kBeingInitialized = 'BeingInitialized';
/// Indicates that a variable has been eliminated by the optimizing compiler.
static const String kOptimizedOut = 'OptimizedOut';
/// Reserved for future use.
static const String kFree = 'Free';
}
/// A `FrameKind` is used to distinguish different kinds of `Frame` objects.
class FrameKind {
FrameKind._();
static const String kRegular = 'Regular';
static const String kAsyncCausal = 'AsyncCausal';
static const String kAsyncSuspensionMarker = 'AsyncSuspensionMarker';
static const String kAsyncActivation = 'AsyncActivation';
}
class SourceReportKind {
SourceReportKind._();
/// Used to request a code coverage information.
static const String kCoverage = 'Coverage';
/// Used to request a list of token positions of possible breakpoints.
static const String kPossibleBreakpoints = 'PossibleBreakpoints';
}
/// An `ExceptionPauseMode` indicates how the isolate pauses when an exception
/// is thrown.
class ExceptionPauseMode {
ExceptionPauseMode._();
static const String kNone = 'None';
static const String kUnhandled = 'Unhandled';
static const String kAll = 'All';
}
/// A `StepOption` indicates which form of stepping is requested in a [resume]
/// RPC.
class StepOption {
StepOption._();
static const String kInto = 'Into';
static const String kOver = 'Over';
static const String kOverAsyncSuspension = 'OverAsyncSuspension';
static const String kOut = 'Out';
static const String kRewind = 'Rewind';
}
// types
class AllocationProfile extends Response {
static AllocationProfile? parse(Map<String, dynamic>? json) =>
json == null ? null : AllocationProfile._fromJson(json);
/// Allocation information for all class types.
List<ClassHeapStats>? members;
/// Information about memory usage for the isolate.
MemoryUsage? memoryUsage;
/// The timestamp of the last accumulator reset.
///
/// If the accumulators have not been reset, this field is not present.
@optional
int? dateLastAccumulatorReset;
/// The timestamp of the last manually triggered GC.
///
/// If a GC has not been triggered manually, this field is not present.
@optional
int? dateLastServiceGC;
AllocationProfile({
required this.members,
required this.memoryUsage,
this.dateLastAccumulatorReset,
this.dateLastServiceGC,
});
AllocationProfile._fromJson(Map<String, dynamic> json)
: super._fromJson(json) {
members = List<ClassHeapStats>.from(
createServiceObject(json['members'], const ['ClassHeapStats'])
as List? ??
[]);
memoryUsage =
createServiceObject(json['memoryUsage'], const ['MemoryUsage'])
as MemoryUsage?;
dateLastAccumulatorReset = json['dateLastAccumulatorReset'] is String
? int.parse(json['dateLastAccumulatorReset'])
: json['dateLastAccumulatorReset'];
dateLastServiceGC = json['dateLastServiceGC'] is String
? int.parse(json['dateLastServiceGC'])
: json['dateLastServiceGC'];
}
@override
String get type => 'AllocationProfile';
@override
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json['type'] = type;
json.addAll({
'members': members?.map((f) => f.toJson()).toList(),
'memoryUsage': memoryUsage?.toJson(),
});
_setIfNotNull(json, 'dateLastAccumulatorReset', dateLastAccumulatorReset);
_setIfNotNull(json, 'dateLastServiceGC', dateLastServiceGC);
return json;
}
String toString() =>
'[AllocationProfile members: ${members}, memoryUsage: ${memoryUsage}]';
}
/// A `BoundField` represents a field bound to a particular value in an
/// `Instance`.
///
/// If the field is uninitialized, the `value` will be the `NotInitialized`
/// [Sentinel].
///
/// If the field is being initialized, the `value` will be the
/// `BeingInitialized` [Sentinel].
class BoundField {
static BoundField? parse(Map<String, dynamic>? json) =>
json == null ? null : BoundField._fromJson(json);
FieldRef? decl;
/// [value] can be one of [InstanceRef] or [Sentinel].
dynamic value;
BoundField({
required this.decl,
required this.value,
});
BoundField._fromJson(Map<String, dynamic> json) {
decl = createServiceObject(json['decl'], const ['FieldRef']) as FieldRef?;
value =
createServiceObject(json['value'], const ['InstanceRef', 'Sentinel'])
as dynamic;
}
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json.addAll({
'decl': decl?.toJson(),
'value': value?.toJson(),
});
return json;
}
String toString() => '[BoundField decl: ${decl}, value: ${value}]';
}
/// A `BoundVariable` represents a local variable bound to a particular value in
/// a `Frame`.
///
/// If the variable is uninitialized, the `value` will be the `NotInitialized`
/// [Sentinel].
///
/// If the variable is being initialized, the `value` will be the
/// `BeingInitialized` [Sentinel].
///
/// If the variable has been optimized out by the compiler, the `value` will be
/// the `OptimizedOut` [Sentinel].
class BoundVariable extends Response {
static BoundVariable? parse(Map<String, dynamic>? json) =>
json == null ? null : BoundVariable._fromJson(json);
String? name;
/// [value] can be one of [InstanceRef], [TypeArgumentsRef] or [Sentinel].
dynamic value;
/// The token position where this variable was declared.
int? declarationTokenPos;
/// The first token position where this variable is visible to the scope.
int? scopeStartTokenPos;
/// The last token position where this variable is visible to the scope.
int? scopeEndTokenPos;
BoundVariable({
required this.name,
required this.value,
required this.declarationTokenPos,
required this.scopeStartTokenPos,
required this.scopeEndTokenPos,
});
BoundVariable._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
name = json['name'] ?? '';
value = createServiceObject(json['value'],
const ['InstanceRef', 'TypeArgumentsRef', 'Sentinel']) as dynamic;
declarationTokenPos = json['declarationTokenPos'] ?? -1;
scopeStartTokenPos = json['scopeStartTokenPos'] ?? -1;
scopeEndTokenPos = json['scopeEndTokenPos'] ?? -1;
}
@override
String get type => 'BoundVariable';
@override
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json['type'] = type;
json.addAll({
'name': name,
'value': value?.toJson(),
'declarationTokenPos': declarationTokenPos,
'scopeStartTokenPos': scopeStartTokenPos,
'scopeEndTokenPos': scopeEndTokenPos,
});
return json;
}
String toString() => '[BoundVariable ' //
'name: ${name}, value: ${value}, declarationTokenPos: ${declarationTokenPos}, ' //
'scopeStartTokenPos: ${scopeStartTokenPos}, scopeEndTokenPos: ${scopeEndTokenPos}]';
}
/// A `Breakpoint` describes a debugger breakpoint.
///
/// A breakpoint is `resolved` when it has been assigned to a specific program
/// location. A breakpoint my remain unresolved when it is in code which has not
/// yet been compiled or in a library which has not been loaded (i.e. a deferred
/// library).
class Breakpoint extends Obj {
static Breakpoint? parse(Map<String, dynamic>? json) =>
json == null ? null : Breakpoint._fromJson(json);
/// A number identifying this breakpoint to the user.
int? breakpointNumber;
/// Is this breakpoint enabled?
bool? enabled;
/// Has this breakpoint been assigned to a specific program location?
bool? resolved;
/// Is this a breakpoint that was added synthetically as part of a step
/// OverAsyncSuspension resume command?
@optional
bool? isSyntheticAsyncContinuation;
/// SourceLocation when breakpoint is resolved, UnresolvedSourceLocation when
/// a breakpoint is not resolved.
///
/// [location] can be one of [SourceLocation] or [UnresolvedSourceLocation].
dynamic location;
Breakpoint({
required this.breakpointNumber,
required this.enabled,
required this.resolved,
required this.location,
required String id,
this.isSyntheticAsyncContinuation,
}) : super(
id: id,
);
Breakpoint._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
breakpointNumber = json['breakpointNumber'] ?? -1;
enabled = json['enabled'] ?? false;
resolved = json['resolved'] ?? false;
isSyntheticAsyncContinuation = json['isSyntheticAsyncContinuation'];
location = createServiceObject(json['location'],
const ['SourceLocation', 'UnresolvedSourceLocation']) as dynamic;
}
@override
String get type => 'Breakpoint';
@override
Map<String, dynamic> toJson() {
final json = super.toJson();
json['type'] = type;
json.addAll({
'breakpointNumber': breakpointNumber,
'enabled': enabled,
'resolved': resolved,
'location': location?.toJson(),
});
_setIfNotNull(
json, 'isSyntheticAsyncContinuation', isSyntheticAsyncContinuation);
return json;
}
int get hashCode => id.hashCode;
bool operator ==(Object other) => other is Breakpoint && id == other.id;
String toString() => '[Breakpoint ' //
'id: ${id}, breakpointNumber: ${breakpointNumber}, enabled: ${enabled}, ' //
'resolved: ${resolved}, location: ${location}]';
}
/// `ClassRef` is a reference to a `Class`.
class ClassRef extends ObjRef {
static ClassRef? parse(Map<String, dynamic>? json) =>
json == null ? null : ClassRef._fromJson(json);
/// The name of this class.
String? name;
/// The location of this class in the source code.
@optional
SourceLocation? location;
/// The library which contains this class.
LibraryRef? library;
/// The type parameters for the class.
///
/// Provided if the class is generic.
@optional
List<InstanceRef>? typeParameters;
ClassRef({
required this.name,
required this.library,
required String id,
this.location,
this.typeParameters,
}) : super(
id: id,
);
ClassRef._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
name = json['name'] ?? '';
location = createServiceObject(json['location'], const ['SourceLocation'])
as SourceLocation?;
library = createServiceObject(json['library'], const ['LibraryRef'])
as LibraryRef?;
typeParameters = json['typeParameters'] == null
? null
: List<InstanceRef>.from(
createServiceObject(json['typeParameters'], const ['InstanceRef'])!
as List);
}
@override
String get type => '@Class';
@override
Map<String, dynamic> toJson() {
final json = super.toJson();
json['type'] = type;
json.addAll({
'name': name,
'library': library?.toJson(),
});
_setIfNotNull(json, 'location', location?.toJson());
_setIfNotNull(json, 'typeParameters',
typeParameters?.map((f) => f.toJson()).toList());
return json;
}
int get hashCode => id.hashCode;
bool operator ==(Object other) => other is ClassRef && id == other.id;
String toString() =>
'[ClassRef id: ${id}, name: ${name}, library: ${library}]';
}
/// A `Class` provides information about a Dart language class.
class Class extends Obj implements ClassRef {
static Class? parse(Map<String, dynamic>? json) =>
json == null ? null : Class._fromJson(json);
/// The name of this class.
String? name;
/// The location of this class in the source code.
@optional
SourceLocation? location;
/// The library which contains this class.
LibraryRef? library;
/// The type parameters for the class.
///
/// Provided if the class is generic.
@optional
List<InstanceRef>? typeParameters;
/// The error which occurred during class finalization, if it exists.
@optional
ErrorRef? error;
/// Is this an abstract class?
bool? isAbstract;
/// Is this a const class?
bool? isConst;
/// Are allocations of this class being traced?
bool? traceAllocations;
/// The superclass of this class, if any.
@optional
ClassRef? superClass;
/// The supertype for this class, if any.
///
/// The value will be of the kind: Type.
@optional
InstanceRef? superType;
/// A list of interface types for this class.
///
/// The values will be of the kind: Type.
List<InstanceRef>? interfaces;
/// The mixin type for this class, if any.
///
/// The value will be of the kind: Type.
@optional
InstanceRef? mixin;
/// A list of fields in this class. Does not include fields from superclasses.
List<FieldRef>? fields;
/// A list of functions in this class. Does not include functions from
/// superclasses.
List<FuncRef>? functions;
/// A list of subclasses of this class.
List<ClassRef>? subclasses;
Class({
required this.name,
required this.library,
required this.isAbstract,
required this.isConst,
required this.traceAllocations,
required this.interfaces,
required this.fields,
required this.functions,
required this.subclasses,
required String id,
this.location,
this.typeParameters,
this.error,
this.superClass,
this.superType,
this.mixin,
}) : super(
id: id,
);
Class._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
name = json['name'] ?? '';
location = createServiceObject(json['location'], const ['SourceLocation'])
as SourceLocation?;
library = createServiceObject(json['library'], const ['LibraryRef'])
as LibraryRef?;
typeParameters = json['typeParameters'] == null
? null
: List<InstanceRef>.from(
createServiceObject(json['typeParameters'], const ['InstanceRef'])!
as List);
error = createServiceObject(json['error'], const ['ErrorRef']) as ErrorRef?;
isAbstract = json['abstract'] ?? false;
isConst = json['const'] ?? false;
traceAllocations = json['traceAllocations'] ?? false;
superClass =
createServiceObject(json['super'], const<