[ package:vm_service ] 4.0.0 release, Sentinels are now thrown, Future<dynamic> returns are now Future<Response> Change-Id: Ifd1bf62e3bc33bf14359802086c87b2fd19f3ba9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/138561 Reviewed-by: Jacob Richman <jacobr@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com>
diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md index 2041a96..6758942 100644 --- a/pkg/vm_service/CHANGELOG.md +++ b/pkg/vm_service/CHANGELOG.md
@@ -1,7 +1,14 @@ # Changelog +## 4.0.0 +- **breaking**: RPCs which can return a `Sentinel` will now throw the `Sentinel` + it is received as a response. +- **breaking**: RPCs which can return multiple values now return + `Future<Response>` rather than `Future<dynamic>`. +- `RPCError` now implements `Exception`. + ## 3.0.0 -**breaking**: RPCs which have an isolateId parameter now return +- **breaking**: RPCs which have an isolateId parameter now return `Future<dynamic>` as a `Sentinel` can be returned if the target isolate no longer exists.
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart index 0a689b9..7fc8e94 100644 --- a/pkg/vm_service/lib/src/vm_service.dart +++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -185,47 +185,47 @@ }; Map<String, List<String>> _methodReturnTypes = { - 'addBreakpoint': const ['Breakpoint', 'Sentinel'], - 'addBreakpointWithScriptUri': const ['Breakpoint', 'Sentinel'], - 'addBreakpointAtEntry': const ['Breakpoint', 'Sentinel'], - 'clearCpuSamples': const ['Success', 'Sentinel'], + 'addBreakpoint': const ['Breakpoint'], + 'addBreakpointWithScriptUri': const ['Breakpoint'], + 'addBreakpointAtEntry': const ['Breakpoint'], + 'clearCpuSamples': const ['Success'], 'clearVMTimeline': const ['Success'], - 'invoke': const ['InstanceRef', 'ErrorRef', 'Sentinel'], - 'evaluate': const ['InstanceRef', 'ErrorRef', 'Sentinel'], - 'evaluateInFrame': const ['InstanceRef', 'ErrorRef', 'Sentinel'], - 'getAllocationProfile': const ['AllocationProfile', 'Sentinel'], + 'invoke': const ['InstanceRef', 'ErrorRef'], + 'evaluate': const ['InstanceRef', 'ErrorRef'], + 'evaluateInFrame': const ['InstanceRef', 'ErrorRef'], + 'getAllocationProfile': const ['AllocationProfile'], 'getClientName': const ['ClientName'], - 'getCpuSamples': const ['CpuSamples', 'Sentinel'], + 'getCpuSamples': const ['CpuSamples'], 'getFlagList': const ['FlagList'], - 'getInboundReferences': const ['InboundReferences', 'Sentinel'], - 'getInstances': const ['InstanceSet', 'Sentinel'], - 'getIsolate': const ['Isolate', 'Sentinel'], - 'getIsolateGroup': const ['IsolateGroup', 'Sentinel'], - 'getMemoryUsage': const ['MemoryUsage', 'Sentinel'], - 'getIsolateGroupMemoryUsage': const ['MemoryUsage', 'Sentinel'], - 'getScripts': const ['ScriptList', 'Sentinel'], - 'getObject': const ['Obj', 'Sentinel'], - 'getRetainingPath': const ['RetainingPath', 'Sentinel'], - 'getStack': const ['Stack', 'Sentinel'], - 'getSourceReport': const ['SourceReport', 'Sentinel'], + 'getInboundReferences': const ['InboundReferences'], + 'getInstances': const ['InstanceSet'], + 'getIsolate': const ['Isolate'], + 'getIsolateGroup': const ['IsolateGroup'], + 'getMemoryUsage': const ['MemoryUsage'], + 'getIsolateGroupMemoryUsage': const ['MemoryUsage'], + 'getScripts': const ['ScriptList'], + 'getObject': const ['Obj'], + 'getRetainingPath': const ['RetainingPath'], + 'getStack': const ['Stack'], + 'getSourceReport': const ['SourceReport'], 'getVersion': const ['Version'], 'getVM': const ['VM'], 'getVMTimeline': const ['Timeline'], 'getVMTimelineFlags': const ['TimelineFlags'], 'getVMTimelineMicros': const ['Timestamp'], - 'pause': const ['Success', 'Sentinel'], - 'kill': const ['Success', 'Sentinel'], + 'pause': const ['Success'], + 'kill': const ['Success'], 'registerService': const ['Success'], - 'reloadSources': const ['ReloadReport', 'Sentinel'], - 'removeBreakpoint': const ['Success', 'Sentinel'], - 'requestHeapSnapshot': const ['Success', 'Sentinel'], + 'reloadSources': const ['ReloadReport'], + 'removeBreakpoint': const ['Success'], + 'requestHeapSnapshot': const ['Success'], 'requirePermissionToResume': const ['Success'], - 'resume': const ['Success', 'Sentinel'], + 'resume': const ['Success'], 'setClientName': const ['Success'], - 'setExceptionPauseMode': const ['Success', 'Sentinel'], + 'setExceptionPauseMode': const ['Success'], 'setFlag': const ['Success', 'Error'], - 'setLibraryDebuggable': const ['Success', 'Sentinel'], - 'setName': const ['Success', 'Sentinel'], + 'setLibraryDebuggable': const ['Success'], + 'setName': const ['Success'], 'setVMName': const ['Success'], 'setVMTimelineFlags': const ['Success'], 'streamCancel': const ['Success'], @@ -272,8 +272,9 @@ /// /// See [Breakpoint]. /// - /// The return value can be one of [Breakpoint] or [Sentinel]. - Future<dynamic> addBreakpoint( + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<Breakpoint> addBreakpoint( String isolateId, String scriptId, int line, { @@ -308,8 +309,9 @@ /// /// See [Breakpoint]. /// - /// The return value can be one of [Breakpoint] or [Sentinel]. - Future<dynamic> addBreakpointWithScriptUri( + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<Breakpoint> addBreakpointWithScriptUri( String isolateId, String scriptUri, int line, { @@ -329,8 +331,9 @@ /// /// Note that breakpoints are added and removed on a per-isolate basis. /// - /// The return value can be one of [Breakpoint] or [Sentinel]. - Future<dynamic> addBreakpointAtEntry(String isolateId, String functionId); + /// 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. /// @@ -339,8 +342,9 @@ /// /// See [Success]. /// - /// The return value can be one of [Success] or [Sentinel]. - Future<dynamic> clearCpuSamples(String isolateId); + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<Success> clearCpuSamples(String isolateId); /// Clears all VM timeline events. /// @@ -379,8 +383,11 @@ /// If the invocation is evaluated successfully, an [InstanceRef] reference /// will be returned. /// - /// The return value can be one of [InstanceRef], [ErrorRef] or [Sentinel]. - Future<dynamic> invoke( + /// 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, @@ -422,8 +429,11 @@ /// If the expression is evaluated successfully, an [InstanceRef] reference /// will be returned. /// - /// The return value can be one of [InstanceRef], [ErrorRef] or [Sentinel]. - Future<dynamic> evaluate( + /// 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, { @@ -457,8 +467,11 @@ /// If `isolateId` refers to an isolate which has exited, then the `Collected` /// [Sentinel] is returned. /// - /// The return value can be one of [InstanceRef], [ErrorRef] or [Sentinel]. - Future<dynamic> evaluateInFrame( + /// 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, { @@ -479,8 +492,10 @@ /// If `isolateId` refers to an isolate which has exited, then the `Collected` /// [Sentinel] is returned. /// - /// The return value can be one of [AllocationProfile] or [Sentinel]. - Future<dynamic> getAllocationProfile(String isolateId, {bool reset, bool gc}); + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<AllocationProfile> getAllocationProfile(String isolateId, + {bool reset, bool gc}); /// The `getClientName` RPC is used to retrieve the name associated with the /// currently connected VM service client. If no name was previously set @@ -500,8 +515,9 @@ /// /// See [CpuSamples]. /// - /// The return value can be one of [CpuSamples] or [Sentinel]. - Future<dynamic> getCpuSamples( + /// 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 @@ -535,8 +551,9 @@ /// /// See [InboundReferences]. /// - /// The return value can be one of [InboundReferences] or [Sentinel]. - Future<dynamic> getInboundReferences( + /// 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 @@ -561,8 +578,10 @@ /// /// See [InstanceSet]. /// - /// The return value can be one of [InstanceSet] or [Sentinel]. - Future<dynamic> getInstances(String isolateId, String objectId, int limit); + /// 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`. /// @@ -571,8 +590,9 @@ /// /// See [Isolate]. /// - /// The return value can be one of [Isolate] or [Sentinel]. - Future<dynamic> getIsolate(String isolateId); + /// 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`. @@ -586,8 +606,9 @@ /// /// See [IsolateGroup], [VM]. /// - /// The return value can be one of [IsolateGroup] or [Sentinel]. - Future<dynamic> getIsolateGroup(String isolateGroupId); + /// 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`. @@ -597,8 +618,9 @@ /// /// See [Isolate]. /// - /// The return value can be one of [MemoryUsage] or [Sentinel]. - Future<dynamic> getMemoryUsage(String isolateId); + /// 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`. @@ -608,8 +630,9 @@ /// /// See [IsolateGroup]. /// - /// The return value can be one of [MemoryUsage] or [Sentinel]. - Future<dynamic> getIsolateGroupMemoryUsage(String isolateGroupId); + /// 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`. @@ -619,8 +642,9 @@ /// /// See [ScriptList]. /// - /// The return value can be one of [ScriptList] or [Sentinel]. - Future<dynamic> getScripts(String isolateId); + /// 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`. @@ -646,8 +670,9 @@ /// Int32List, Int64List, Flooat32List, Float64List, Inst32x3List, /// Float32x4List, and Float64x2List. These parameters are otherwise ignored. /// - /// The return value can be one of [Obj] or [Sentinel]. - Future<dynamic> getObject( + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<Obj> getObject( String isolateId, String objectId, { int offset, @@ -676,8 +701,9 @@ /// /// See [RetainingPath]. /// - /// The return value can be one of [RetainingPath] or [Sentinel]. - Future<dynamic> getRetainingPath( + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<RetainingPath> getRetainingPath( String isolateId, String targetId, int limit); /// The `getStack` RPC is used to retrieve the current execution stack and @@ -688,8 +714,9 @@ /// /// See [Stack]. /// - /// The return value can be one of [Stack] or [Sentinel]. - Future<dynamic> getStack(String isolateId); + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<Stack> getStack(String isolateId); /// The `getSourceReport` RPC is used to generate a set of reports tied to /// source locations in an isolate. @@ -728,8 +755,9 @@ /// /// See [SourceReport]. /// - /// The return value can be one of [SourceReport] or [Sentinel]. - Future<dynamic> getSourceReport( + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<SourceReport> getSourceReport( String isolateId, /*List<SourceReportKind>*/ List<String> reports, { @@ -799,8 +827,9 @@ /// /// See [Success]. /// - /// The return value can be one of [Success] or [Sentinel]. - Future<dynamic> pause(String isolateId); + /// 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)`. @@ -812,8 +841,9 @@ /// /// See [Success]. /// - /// The return value can be one of [Success] or [Sentinel]. - Future<dynamic> kill(String isolateId); + /// 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 @@ -843,8 +873,9 @@ /// If `isolateId` refers to an isolate which has exited, then the `Collected` /// [Sentinel] is returned. /// - /// The return value can be one of [ReloadReport] or [Sentinel]. - Future<dynamic> reloadSources( + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<ReloadReport> reloadSources( String isolateId, { bool force, bool pause, @@ -861,8 +892,9 @@ /// /// See [Success]. /// - /// The return value can be one of [Success] or [Sentinel]. - Future<dynamic> removeBreakpoint(String isolateId, String breakpointId); + /// 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. /// @@ -875,8 +907,9 @@ /// If `isolateId` refers to an isolate which has exited, then the `Collected` /// [Sentinel] is returned. /// - /// The return value can be one of [Success] or [Sentinel]. - Future<dynamic> requestHeapSnapshot(String isolateId); + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<Success> requestHeapSnapshot(String isolateId); /// The `requirePermissionToResume` RPC is used to change the pause/resume /// behavior of isolates by providing a way for the VM service to wait for @@ -930,8 +963,9 @@ /// /// See [Success], [StepOption]. /// - /// The return value can be one of [Success] or [Sentinel]. - Future<dynamic> resume(String isolateId, + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<Success> resume(String isolateId, {/*StepOption*/ String step, int frameIndex}); /// The `setClientName` RPC is used to set a name to be associated with the @@ -955,8 +989,9 @@ /// If `isolateId` refers to an isolate which has exited, then the `Collected` /// [Sentinel] is returned. /// - /// The return value can be one of [Success] or [Sentinel]. - Future<dynamic> setExceptionPauseMode( + /// 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 @@ -966,7 +1001,7 @@ /// The following flags may be set at runtime: /// /// The return value can be one of [Success] or [Error]. - Future<dynamic> setFlag(String name, String value); + 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. @@ -976,8 +1011,9 @@ /// /// See [Success]. /// - /// The return value can be one of [Success] or [Sentinel]. - Future<dynamic> setLibraryDebuggable( + /// 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. @@ -987,8 +1023,9 @@ /// /// See [Success]. /// - /// The return value can be one of [Success] or [Sentinel]. - Future<dynamic> setName(String isolateId, String name); + /// This method will throw a [SentinelException] in the case a [Sentinel] is + /// returned. + Future<Success> setName(String isolateId, String name); /// The `setVMName` RPC is used to change the debugging name for the vm. /// @@ -1540,7 +1577,7 @@ Stream<Event> get onStderrEvent => _getEventController('Stderr').stream; @override - Future<dynamic> addBreakpoint( + Future<Breakpoint> addBreakpoint( String isolateId, String scriptId, int line, { @@ -1554,7 +1591,7 @@ }); @override - Future<dynamic> addBreakpointWithScriptUri( + Future<Breakpoint> addBreakpointWithScriptUri( String isolateId, String scriptUri, int line, { @@ -1581,7 +1618,7 @@ Future<Success> clearVMTimeline() => _call('clearVMTimeline'); @override - Future<dynamic> invoke( + Future<Response> invoke( String isolateId, String targetId, String selector, @@ -1598,7 +1635,7 @@ }); @override - Future<dynamic> evaluate( + Future<Response> evaluate( String isolateId, String targetId, String expression, { @@ -1615,7 +1652,7 @@ }); @override - Future<dynamic> evaluateInFrame( + Future<Response> evaluateInFrame( String isolateId, int frameIndex, String expression, { @@ -1656,7 +1693,7 @@ Future<FlagList> getFlagList() => _call('getFlagList'); @override - Future<dynamic> getInboundReferences( + Future<InboundReferences> getInboundReferences( String isolateId, String targetId, int limit) => _call('getInboundReferences', {'isolateId': isolateId, 'targetId': targetId, 'limit': limit}); @@ -1668,19 +1705,19 @@ {'isolateId': isolateId, 'objectId': objectId, 'limit': limit}); @override - Future<dynamic> getIsolate(String isolateId) => + Future<Isolate> getIsolate(String isolateId) => _call('getIsolate', {'isolateId': isolateId}); @override - Future<dynamic> getIsolateGroup(String isolateGroupId) => + Future<IsolateGroup> getIsolateGroup(String isolateGroupId) => _call('getIsolateGroup', {'isolateGroupId': isolateGroupId}); @override - Future<dynamic> getMemoryUsage(String isolateId) => + Future<MemoryUsage> getMemoryUsage(String isolateId) => _call('getMemoryUsage', {'isolateId': isolateId}); @override - Future<dynamic> getIsolateGroupMemoryUsage(String isolateGroupId) => + Future<MemoryUsage> getIsolateGroupMemoryUsage(String isolateGroupId) => _call('getIsolateGroupMemoryUsage', {'isolateGroupId': isolateGroupId}); @override @@ -1688,7 +1725,7 @@ _call('getScripts', {'isolateId': isolateId}); @override - Future<dynamic> getObject( + Future<Obj> getObject( String isolateId, String objectId, { int offset, @@ -1712,7 +1749,7 @@ _call('getStack', {'isolateId': isolateId}); @override - Future<dynamic> getSourceReport( + Future<SourceReport> getSourceReport( String isolateId, /*List<SourceReportKind>*/ List<String> reports, { @@ -1763,7 +1800,7 @@ _call('registerService', {'service': service, 'alias': alias}); @override - Future<dynamic> reloadSources( + Future<ReloadReport> reloadSources( String isolateId, { bool force, bool pause, @@ -1815,7 +1852,7 @@ _call('setExceptionPauseMode', {'isolateId': isolateId, 'mode': mode}); @override - Future<dynamic> setFlag(String name, String value) => + Future<Response> setFlag(String name, String value) => _call('setFlag', {'name': name, 'value': value}); @override @@ -1984,7 +2021,9 @@ } else { Map<String, dynamic> result = json['result'] as Map<String, dynamic>; String type = result['type']; - if (_typeFactories[type] == null) { + if (type == 'Sentinel') { + completer.completeError(SentinelException.parse(methodName, result)); + } else if (_typeFactories[type] == null) { completer.complete(Response.parse(result)); } else { completer.complete(createServiceObject(result, returnTypes)); @@ -2037,7 +2076,7 @@ typedef DisposeHandler = Future Function(); -class RPCError { +class RPCError implements Exception { static RPCError parse(String callingMethod, dynamic json) { return RPCError(callingMethod, json['code'], json['message'], json['data']); } @@ -2060,6 +2099,17 @@ } } +/// 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 json) =>
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml index 99a3026..366bd74 100644 --- a/pkg/vm_service/pubspec.yaml +++ b/pkg/vm_service/pubspec.yaml
@@ -2,7 +2,7 @@ description: >- A library to communicate with a service implementing the Dart VM service protocol. -version: 3.0.0+1 +version: 4.0.0 homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
diff --git a/pkg/vm_service/test/async_generator_breakpoint_test.dart b/pkg/vm_service/test/async_generator_breakpoint_test.dart index 4d52ce4..3cf2655 100644 --- a/pkg/vm_service/test/async_generator_breakpoint_test.dart +++ b/pkg/vm_service/test/async_generator_breakpoint_test.dart
@@ -48,7 +48,7 @@ Future testAsync(VmService service, IsolateRef isolateRef) async { final isolate = await service.getIsolate(isolateRef.id); - final lib = await service.getObject(isolate.id, isolate.rootLib.id); + final Library lib = await service.getObject(isolate.id, isolate.rootLib.id); final script = lib.scripts[0]; final bp1 = await service.addBreakpoint(isolate.id, script.id, 11); @@ -77,10 +77,10 @@ // ignore: unawaited_futures service .evaluate(isolate.id, lib.id, 'testerReady = true') - .then((result) async { - result = await service.getObject(isolate.id, result.id); - print(result); - expect((result as Instance).valueAsString, equals('true')); + .then((Response result) async { + Obj res = await service.getObject(isolate.id, (result as InstanceRef).id); + print(res); + expect((res as Instance).valueAsString, equals('true')); }); final stream = service.onDebugEvent;
diff --git a/pkg/vm_service/test/common/service_test_common.dart b/pkg/vm_service/test/common/service_test_common.dart index e5dea67..55edf13 100644 --- a/pkg/vm_service/test/common/service_test_common.dart +++ b/pkg/vm_service/test/common/service_test_common.dart
@@ -117,7 +117,7 @@ return (VmService service, IsolateRef isolateRef) async { print("Setting breakpoint for line $line"); final isolate = await service.getIsolate(isolateRef.id); - final lib = await service.getObject(isolate.id, isolate.rootLib.id); + final Library lib = await service.getObject(isolate.id, isolate.rootLib.id); final script = lib.scripts.first; Breakpoint bpt = await service.addBreakpoint(isolate.id, script.id, line); @@ -139,7 +139,8 @@ expect(frames.length, greaterThanOrEqualTo(1)); final top = frames[0]; - final script = await service.getObject(isolate.id, top.location.script.id); + final Script script = + await service.getObject(isolate.id, top.location.script.id); int actualLine = script.getLineNumberFromTokenPos(top.location.tokenPos); if (actualLine != line) { print("Actual: $actualLine Line: $line");
diff --git a/pkg/vm_service/test/coverage_leaf_function_test.dart b/pkg/vm_service/test/coverage_leaf_function_test.dart index fe28924..eac959f 100644 --- a/pkg/vm_service/test/coverage_leaf_function_test.dart +++ b/pkg/vm_service/test/coverage_leaf_function_test.dart
@@ -37,9 +37,11 @@ expect(stack.frames.length, greaterThanOrEqualTo(1)); expect(stack.frames[0].function.name, 'testFunction'); - final root = await service.getObject(isolate.id, isolate.rootLib.id); - var func = root.functions.singleWhere((f) => f.name == 'leafFunction'); - func = await service.getObject(isolate.id, func.id); + final Library root = + await service.getObject(isolate.id, isolate.rootLib.id); + FuncRef funcRef = + root.functions.singleWhere((f) => f.name == 'leafFunction'); + Func func = await service.getObject(isolate.id, funcRef.id) as Func; final expectedRange = { 'scriptIndex': 0, @@ -73,9 +75,11 @@ expect(stack.frames.length, greaterThanOrEqualTo(1)); expect(stack.frames[0].function.name, 'testFunction'); - final root = await service.getObject(isolate.id, isolate.rootLib.id); - var func = root.functions.singleWhere((f) => f.name == 'leafFunction'); - func = await service.getObject(isolate.id, func.id); + final Library root = + await service.getObject(isolate.id, isolate.rootLib.id); + FuncRef funcRef = + root.functions.singleWhere((f) => f.name == 'leafFunction'); + Func func = await service.getObject(isolate.id, funcRef.id) as Func; var expectedRange = { 'scriptIndex': 0,
diff --git a/pkg/vm_service/test/debugging_test.dart b/pkg/vm_service/test/debugging_test.dart index 8bfefd6..df3a1ed 100644 --- a/pkg/vm_service/test/debugging_test.dart +++ b/pkg/vm_service/test/debugging_test.dart
@@ -80,16 +80,14 @@ } }); await service.streamListen(EventStreams.kDebug); - final script = + final Script script = await service.getObject(isolate.id, rootLib.scripts.first.id); // Add the breakpoint. - final Breakpoint result = + final Breakpoint bpt = await service.addBreakpoint(isolate.id, script.id, 16); - print(result); - expect(result is Breakpoint, isTrue); - Breakpoint bpt = result; - expect(bpt.location.script.id, script.id); - expect(script.getLineNumberFromTokenPos(bpt.location.tokenPos), 16); + final SourceLocation location = bpt.location; + expect(location.script.id, script.id); + expect(script.getLineNumberFromTokenPos(location.tokenPos), 16); isolate = await service.getIsolate(isolate.id); expect(isolate.breakpoints.length, 1);
diff --git a/pkg/vm_service/test/eval_test.dart b/pkg/vm_service/test/eval_test.dart index b751001..4eb8bb5 100644 --- a/pkg/vm_service/test/eval_test.dart +++ b/pkg/vm_service/test/eval_test.dart
@@ -45,7 +45,7 @@ // Make sure we are in the right place. expect(stack.frames.length, greaterThanOrEqualTo(2)); expect(stack.frames[0].function.name, 'method'); - expect(stack.frames[0].function.owner.name, 'MyClass'); + expect((stack.frames[0].function.owner as ClassRef).name, 'MyClass'); final LibraryRef lib = isolate.rootLib; final ClassRef cls = stack.frames[0].function.owner; @@ -81,11 +81,12 @@ // Make sure we are in the right place. expect(stack.frames.length, greaterThanOrEqualTo(2)); expect(stack.frames[0].function.name, 'foo'); - expect(stack.frames[0].function.owner.name, '_MyClass'); + expect((stack.frames[0].function.owner as ClassRef).name, '_MyClass'); final ClassRef cls = stack.frames[0].function.owner; - final result = await service.evaluate(isolate.id, cls.id, "1+1"); + final InstanceRef result = + await service.evaluate(isolate.id, cls.id, "1+1"); print(result); expect(result.valueAsString, "2"); }
diff --git a/pkg/vm_service/test/evaluate_with_scope_test.dart b/pkg/vm_service/test/evaluate_with_scope_test.dart index ad4a106..38588d8 100644 --- a/pkg/vm_service/test/evaluate_with_scope_test.dart +++ b/pkg/vm_service/test/evaluate_with_scope_test.dart
@@ -22,13 +22,13 @@ final tests = <IsolateTest>[ (VmService service, IsolateRef isolateRef) async { final isolate = await service.getIsolate(isolateRef.id); - final lib = await service.getObject(isolate.id, isolate.rootLib.id); + final Library lib = await service.getObject(isolate.id, isolate.rootLib.id); - final field1 = await service.getObject( + final Field field1 = await service.getObject( isolate.id, lib.variables.singleWhere((v) => v.name == 'thing1').id); final thing1 = (await service.getObject(isolate.id, field1.staticValue.id)); - final field2 = await service.getObject( + final Field field2 = await service.getObject( isolate.id, lib.variables.singleWhere((v) => v.name == 'thing2').id); final thing2 = (await service.getObject(isolate.id, field2.staticValue.id));
diff --git a/pkg/vm_service/test/get_flag_list_rpc_test.dart b/pkg/vm_service/test/get_flag_list_rpc_test.dart index f7a8b70..8b6c337 100644 --- a/pkg/vm_service/test/get_flag_list_rpc_test.dart +++ b/pkg/vm_service/test/get_flag_list_rpc_test.dart
@@ -22,16 +22,14 @@ var tests = <VMTest>[ // Modify a flag which does not exist. (VmService service) async { - final result = await service.setFlag('does_not_exist', 'true'); - expect(result, TypeMatcher<Error>()); + final Error result = await service.setFlag('does_not_exist', 'true'); expect(result.message, 'Cannot set flag: flag not found'); }, // Modify a flag with the wrong value type. (VmService service) async { - final result = + final Error result = await service.setFlag('pause_isolates_on_start', 'not-boolean'); - expect(result, TypeMatcher<Error>()); expect(result.message, equals('Cannot set flag: invalid value')); }, @@ -43,8 +41,7 @@ // Modify a flag which cannot be set at runtime. (VmService service) async { - final result = await service.setFlag('random_seed', '42'); - expect(result, TypeMatcher<Error>()); + final Error result = await service.setFlag('random_seed', '42'); expect(result.message, 'Cannot set flag: cannot change at runtime'); },
diff --git a/pkg/vm_service/test/invoke_test.dart b/pkg/vm_service/test/invoke_test.dart index dcbb16e..651e39e 100644 --- a/pkg/vm_service/test/invoke_test.dart +++ b/pkg/vm_service/test/invoke_test.dart
@@ -31,7 +31,7 @@ hasStoppedAtBreakpoint, (VmService service, IsolateRef isolateRef) async { final isolate = await service.getIsolate(isolateRef.id); - final lib = await service.getObject(isolate.id, isolate.rootLib.id); + final Library lib = await service.getObject(isolate.id, isolate.rootLib.id); final cls = lib.classes.singleWhere((cls) => cls.name == "Klass"); FieldRef fieldRef = lib.variables.singleWhere((field) => field.name == "instance");
diff --git a/pkg/vm_service/test/throws_sentinel_test.dart b/pkg/vm_service/test/throws_sentinel_test.dart new file mode 100644 index 0000000..b399f06 --- /dev/null +++ b/pkg/vm_service/test/throws_sentinel_test.dart
@@ -0,0 +1,23 @@ +// Copyright (c) 2020, 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. + +import 'package:vm_service/vm_service.dart'; +import 'package:test/test.dart'; + +import 'common/test_helper.dart'; + +var tests = <VMTest>[ + (VmService vm) async { + try { + final res = await vm.getIsolate('isolates/12321'); + fail('Expected SentinelException, got $res'); + } on SentinelException catch (e) { + // Expected. + } catch (e) { + fail('Expected SentinelException, got $e'); + } + }, +]; + +main([args = const <String>[]]) async => await runVMTests(args, tests);
diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart index 0f62ac2..eb2e6e8 100644 --- a/pkg/vm_service/tool/dart/generate_dart.dart +++ b/pkg/vm_service/tool/dart/generate_dart.dart
@@ -210,7 +210,9 @@ } else { Map<String, dynamic> result = json['result'] as Map<String, dynamic>; String type = result['type']; - if (_typeFactories[type] == null) { + if (type == 'Sentinel') { + completer.completeError(SentinelException.parse(methodName, result)); + } else if (_typeFactories[type] == null) { completer.complete(Response.parse(result)); } else { completer.complete(createServiceObject(result, returnTypes)); @@ -265,7 +267,7 @@ typedef DisposeHandler = Future Function(); -class RPCError { +class RPCError implements Exception { static RPCError parse(String callingMethod, dynamic json) { return RPCError(callingMethod, json['code'], json['message'], json['data']); } @@ -288,6 +290,17 @@ } } +/// 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 json) => @@ -1110,6 +1123,11 @@ '${joinLast(returnType.types.map((t) => '[${t}]'), ', ', ' or ')}.'; _docs = _docs.trim(); } + if (returnType.canReturnSentinel) { + _docs += + '\n\nThis method will throw a [SentinelException] in the case a [Sentinel] is returned.'; + _docs = _docs.trim(); + } if (_docs.isNotEmpty) gen.writeDocs(_docs); } if (withOverrides) gen.writeln('@override'); @@ -1147,10 +1165,11 @@ MemberType(); - void parse(Parser parser) { + void parse(Parser parser, {bool isReturnType = false}) { // foo|bar[]|baz // (@Instance|Sentinel)[] bool loop = true; + this.isReturnType = isReturnType; while (loop) { if (parser.consume('(')) { @@ -1172,7 +1191,11 @@ parser.expect(']'); ref.arrayDepth++; } - types.add(ref); + if (isReturnType && ref.name == 'Sentinel') { + canReturnSentinel = true; + } else { + types.add(ref); + } } loop = parser.consume('|'); @@ -1182,9 +1205,13 @@ String get name { if (types.isEmpty) return ''; if (types.length == 1) return types.first.ref; + if (isReturnType) return 'Response'; return 'dynamic'; } + bool isReturnType = false; + bool canReturnSentinel = false; + bool get isMultipleReturns => types.length > 1; bool get isSimple => types.length == 1 && types.first.isSimple; @@ -1923,7 +1950,7 @@ // method is return type, name, (, args ) // args is type name, [optional], comma - method.returnType.parse(this); + method.returnType.parse(this, isReturnType: true); Token t = expectName(); validate(