| // 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<
|