diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md index 38add630fae..e00354bf030 100644 --- a/pkg/vm_service/CHANGELOG.md +++ b/pkg/vm_service/CHANGELOG.md @@ -2,6 +2,7 @@ - Update to version `4.7` of the spec. - Add deprecation notice to `Stack.awaiterFrames`. - Add deprecation notice to `FrameKind.kAsyncActivation`. +- Expose RPC error codes that were defined in `package:dds`. ## 11.5.0 - Update to version `4.6` of the spec. diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart index d935cc12e7c..15359c239c1 100644 --- a/pkg/vm_service/lib/src/vm_service.dart +++ b/pkg/vm_service/lib/src/vm_service.dart @@ -1490,8 +1490,8 @@ class VmServerConnection { } final method = request['method'] as String?; if (method == null) { - throw RPCError( - null, RPCError.kInvalidRequest, 'Invalid Request', request); + throw RPCError(null, RPCErrorKind.kInvalidRequest.code, + 'Invalid Request', request); } final params = request['params'] as Map?; late Response response; @@ -1888,8 +1888,8 @@ class VmServerConnection { response = await _serviceImplementation.callServiceExtension(method, isolateId: isolateId, args: args); } else { - throw RPCError( - method, RPCError.kMethodNotFound, 'Method not found', request); + throw RPCError(method, RPCErrorKind.kMethodNotFound.code, + 'Method not found', request); } } _responseSink.add({ @@ -1907,7 +1907,7 @@ class VmServerConnection { final error = e is RPCError ? e.toMap() : { - 'code': RPCError.kInternalError, + 'code': RPCErrorKind.kInternalError.code, 'message': '${request['method']}: $e', 'data': {'details': '$st'}, }; @@ -2474,7 +2474,7 @@ class VmService implements VmServiceInterface { _outstandingRequests.forEach((id, request) { request._completer.completeError(RPCError( request.method, - RPCError.kServerError, + RPCErrorKind.kServerError.code, 'Service connection disposed', )); }); @@ -2615,8 +2615,8 @@ class VmService implements VmServiceInterface { Future _routeRequest(String method, Map params) async { final service = _services[method]; if (service == null) { - RPCError error = RPCError( - method, RPCError.kMethodNotFound, 'method not found \'$method\''); + RPCError error = RPCError(method, RPCErrorKind.kMethodNotFound.code, + 'method not found \'$method\''); return {'error': error.toMap()}; } @@ -2625,7 +2625,7 @@ class VmService implements VmServiceInterface { } catch (e, st) { RPCError error = RPCError.withDetails( method, - RPCError.kServerError, + RPCErrorKind.kServerError.code, '$e', details: '$st', ); @@ -2636,21 +2636,143 @@ class VmService implements VmServiceInterface { typedef DisposeHandler = Future Function(); -class RPCError implements Exception { - /// Application specific error codes. - static const int kServerError = -32000; +// These error codes must be kept in sync with those in vm/json_stream.h and +// vmservice.dart. +enum RPCErrorKind { + /// Application specific error code. + kServerError, /// The JSON sent is not a valid Request object. - static const int kInvalidRequest = -32600; + kInvalidRequest, /// The method does not exist or is not available. - static const int kMethodNotFound = -32601; + kMethodNotFound, /// Invalid method parameter(s), such as a mismatched type. - static const int kInvalidParams = -32602; + kInvalidParams, /// Internal JSON-RPC error. - static const int kInternalError = -32603; + kInternalError, + + /// The requested feature is disabled. + kFeatureDisabled, + + /// The stream has already been subscribed to. + kStreamAlreadySubscribed, + + /// The stream has not been subscribed to. + kStreamNotSubscribed, + + /// Isolate must first be paused. + kIsolateMustBePaused, + + /// The service has already been registered. + kServiceAlreadyRegistered, + + /// The service no longer exists. + kServiceDisappeared, + + /// There was an error in the expression compiler. + kExpressionCompilationError, + + /// The custom stream does not exist. + kCustomStreamDoesNotExist, + + /// The core stream is not allowed. + kCoreStreamNotAllowed; + + static final _codeToErrorMap = + RPCErrorKind.values.fold({}, (map, error) { + map[error.code] = error; + return map; + }); + + static RPCErrorKind? fromCode(int code) { + return _codeToErrorMap[code]; + } + + String get message { + switch (this) { + case kServerError: + return 'Application error'; + case kInvalidRequest: + return 'Invalid request object'; + case kMethodNotFound: + return 'Method not found'; + case kInvalidParams: + return 'Invalid method parameters'; + case kInternalError: + return 'Internal JSON-RPC error'; + case kFeatureDisabled: + return 'Feature is disabled'; + case kStreamAlreadySubscribed: + return 'Stream already subscribed'; + case kStreamNotSubscribed: + return 'Stream not subscribed'; + case kIsolateMustBePaused: + return 'Isolate must be paused'; + case kServiceAlreadyRegistered: + return 'Service already registered'; + case kServiceDisappeared: + return 'Service has disappeared'; + case kExpressionCompilationError: + return 'Expression compilation error'; + case kCustomStreamDoesNotExist: + return 'Custom stream does not exist'; + case kCoreStreamNotAllowed: + return 'Core streams are not allowed'; + } + } + + int get code { + switch (this) { + case kServerError: + return -32000; + case kInvalidRequest: + return -32600; + case kMethodNotFound: + return -32601; + case kInvalidParams: + return -32602; + case kInternalError: + return -32603; + case kFeatureDisabled: + return 100; + case kStreamAlreadySubscribed: + return 103; + case kStreamNotSubscribed: + return 104; + case kIsolateMustBePaused: + return 106; + case kServiceAlreadyRegistered: + return 111; + case kServiceDisappeared: + return 112; + case kExpressionCompilationError: + return 113; + case kCustomStreamDoesNotExist: + return 130; + case kCoreStreamNotAllowed: + return 131; + } + } +} + +class RPCError implements Exception { + @Deprecated('Use RPCErrorKind.kServerError.code instead.') + static int get kServerError => RPCErrorKind.kServerError.code; + + @Deprecated('Use RPCErrorKind.kInvalidRequest.code instead.') + static int get kInvalidRequest => RPCErrorKind.kInvalidRequest.code; + + @Deprecated('Use RPCErrorKind.kMethodNotFound.code instead.') + static int get kMethodNotFound => RPCErrorKind.kMethodNotFound.code; + + @Deprecated('Use RPCErrorKind.kInvalidParams.code instead.') + static int get kInvalidParams => RPCErrorKind.kInvalidParams.code; + + @Deprecated('Use RPCErrorKind.kInternalError.code instead.') + static int get kInternalError => RPCErrorKind.kInternalError.code; static RPCError parse(String callingMethod, dynamic json) { return RPCError(callingMethod, json['code'], json['message'], json['data']); @@ -2661,7 +2783,9 @@ class RPCError implements Exception { final String message; final Map? data; - RPCError(this.callingMethod, this.code, this.message, [this.data]); + RPCError(this.callingMethod, this.code, [message, this.data]) + : message = + message ?? RPCErrorKind.fromCode(code)?.message ?? 'Unknown error'; RPCError.withDetails(this.callingMethod, this.code, this.message, {Object? details}) diff --git a/pkg/vm_service/test/add_breakpoint_rpc_kernel_test.dart b/pkg/vm_service/test/add_breakpoint_rpc_kernel_test.dart index 0fa939b5520..ac296e6a4a3 100644 --- a/pkg/vm_service/test/add_breakpoint_rpc_kernel_test.dart +++ b/pkg/vm_service/test/add_breakpoint_rpc_kernel_test.dart @@ -150,7 +150,7 @@ var tests = [ expect(false, isTrue, reason: 'Unreachable'); } on RPCError catch (e) { caughtException = true; - expect(e.code, RPCError.kInvalidParams); + expect(e.code, RPCErrorKind.kInvalidParams.code); expect(e.details, "addBreakpoint: invalid 'column' parameter: 0"); } expect(caughtException, isTrue); diff --git a/pkg/vm_service/test/get_isolate_rpc_test.dart b/pkg/vm_service/test/get_isolate_rpc_test.dart index af7232de91a..95f82dfaaf0 100644 --- a/pkg/vm_service/test/get_isolate_rpc_test.dart +++ b/pkg/vm_service/test/get_isolate_rpc_test.dart @@ -37,7 +37,7 @@ var tests = [ expect(false, isTrue, reason: 'Unreachable'); } on RPCError catch (e) { caughtException = true; - expect(e.code, equals(RPCError.kInvalidParams)); + expect(e.code, equals(RPCErrorKind.kInvalidParams.code)); expect(e.details, "getIsolate: invalid 'isolateId' parameter: badid"); } expect(caughtException, isTrue); diff --git a/pkg/vm_service/test/get_object_rpc_test.dart b/pkg/vm_service/test/get_object_rpc_test.dart index c8f36de66ba..eccc59a0a61 100644 --- a/pkg/vm_service/test/get_object_rpc_test.dart +++ b/pkg/vm_service/test/get_object_rpc_test.dart @@ -766,7 +766,7 @@ var tests = [ await service.getObject(isolateId, objectId); fail('successfully got library with bad ID'); } on RPCError catch (e) { - expect(e.code, equals(RPCError.kInvalidParams)); + expect(e.code, equals(RPCErrorKind.kInvalidParams.code)); expect(e.message, "Invalid params"); } }, @@ -802,7 +802,7 @@ var tests = [ await service.getObject(isolateId, objectId); fail('successfully got script with bad ID'); } on RPCError catch (e) { - expect(e.code, equals(RPCError.kInvalidParams)); + expect(e.code, equals(RPCErrorKind.kInvalidParams.code)); expect(e.message, "Invalid params"); } }, @@ -1220,7 +1220,7 @@ var tests = [ await service.getObject(isolateId, objectId); fail('successfully got class with bad ID'); } on RPCError catch (e) { - expect(e.code, equals(RPCError.kInvalidParams)); + expect(e.code, equals(RPCErrorKind.kInvalidParams.code)); expect(e.message, "Invalid params"); } }, @@ -1253,7 +1253,7 @@ var tests = [ await service.getObject(isolateId, objectId); fail('successfully got type with bad ID'); } on RPCError catch (e) { - expect(e.code, equals(RPCError.kInvalidParams)); + expect(e.code, equals(RPCErrorKind.kInvalidParams.code)); expect(e.message, "Invalid params"); } }, @@ -1394,7 +1394,7 @@ var tests = [ await service.getObject(isolateId, objectId); fail('successfully got function with bad ID'); } on RPCError catch (e) { - expect(e.code, equals(RPCError.kInvalidParams)); + expect(e.code, equals(RPCErrorKind.kInvalidParams.code)); expect(e.message, "Invalid params"); } }, @@ -1494,7 +1494,7 @@ var tests = [ await service.getObject(isolateId, objectId); fail('successfully got field initializer with bad ID'); } on RPCError catch (e) { - expect(e.code, equals(RPCError.kInvalidParams)); + expect(e.code, equals(RPCErrorKind.kInvalidParams.code)); } }, @@ -1538,7 +1538,7 @@ var tests = [ await service.getObject(isolateId, objectId); fail('successfully got field with bad ID'); } on RPCError catch (e) { - expect(e.code, equals(RPCError.kInvalidParams)); + expect(e.code, equals(RPCErrorKind.kInvalidParams.code)); } }, @@ -1587,7 +1587,7 @@ var tests = [ await service.getObject(isolateId, objectId); fail('successfully got code with bad ID'); } on RPCError catch (e) { - expect(e.code, equals(RPCError.kInvalidParams)); + expect(e.code, equals(RPCErrorKind.kInvalidParams.code)); expect(e.message, "Invalid params"); } }, diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart index c96eac0f675..26de1337d46 100644 --- a/pkg/vm_service/tool/dart/generate_dart.dart +++ b/pkg/vm_service/tool/dart/generate_dart.dart @@ -104,7 +104,10 @@ final String _implCode = r''' await _streamSub.cancel(); _outstandingRequests.forEach((id, request) { request._completer.completeError(RPCError( - request.method, RPCError.kServerError, 'Service connection disposed',)); + request.method, + RPCErrorKind.kServerError.code, + 'Service connection disposed', + )); }); _outstandingRequests.clear(); if (_disposeHandler != null) { @@ -237,8 +240,8 @@ final String _implCode = r''' Future _routeRequest(String method, Map params) async{ final service = _services[method]; if (service == null) { - RPCError error = RPCError( - method, RPCError.kMethodNotFound, 'method not found \'$method\''); + RPCError error = RPCError(method, RPCErrorKind.kMethodNotFound.code, + 'method not found \'$method\''); return {'error': error.toMap()}; } @@ -246,7 +249,11 @@ final String _implCode = r''' return await service(params); } catch (e, st) { RPCError error = RPCError.withDetails( - method, RPCError.kServerError, '$e', details: '$st',); + method, + RPCErrorKind.kServerError.code, + '$e', + details: '$st', + ); return {'error': error.toMap()}; } } @@ -257,21 +264,143 @@ final String _rpcError = r''' typedef DisposeHandler = Future Function(); -class RPCError implements Exception { - /// Application specific error codes. - static const int kServerError = -32000; +// These error codes must be kept in sync with those in vm/json_stream.h and +// vmservice.dart. +enum RPCErrorKind { + /// Application specific error code. + kServerError, /// The JSON sent is not a valid Request object. - static const int kInvalidRequest = -32600; + kInvalidRequest, /// The method does not exist or is not available. - static const int kMethodNotFound = -32601; + kMethodNotFound, /// Invalid method parameter(s), such as a mismatched type. - static const int kInvalidParams = -32602; + kInvalidParams, /// Internal JSON-RPC error. - static const int kInternalError = -32603; + kInternalError, + + /// The requested feature is disabled. + kFeatureDisabled, + + /// The stream has already been subscribed to. + kStreamAlreadySubscribed, + + /// The stream has not been subscribed to. + kStreamNotSubscribed, + + /// Isolate must first be paused. + kIsolateMustBePaused, + + /// The service has already been registered. + kServiceAlreadyRegistered, + + /// The service no longer exists. + kServiceDisappeared, + + /// There was an error in the expression compiler. + kExpressionCompilationError, + + /// The custom stream does not exist. + kCustomStreamDoesNotExist, + + /// The core stream is not allowed. + kCoreStreamNotAllowed; + + static final _codeToErrorMap = + RPCErrorKind.values.fold({}, (map, error) { + map[error.code] = error; + return map; + }); + + static RPCErrorKind? fromCode(int code) { + return _codeToErrorMap[code]; + } + + String get message { + switch (this) { + case kServerError: + return 'Application error'; + case kInvalidRequest: + return 'Invalid request object'; + case kMethodNotFound: + return 'Method not found'; + case kInvalidParams: + return 'Invalid method parameters'; + case kInternalError: + return 'Internal JSON-RPC error'; + case kFeatureDisabled: + return 'Feature is disabled'; + case kStreamAlreadySubscribed: + return 'Stream already subscribed'; + case kStreamNotSubscribed: + return 'Stream not subscribed'; + case kIsolateMustBePaused: + return 'Isolate must be paused'; + case kServiceAlreadyRegistered: + return 'Service already registered'; + case kServiceDisappeared: + return 'Service has disappeared'; + case kExpressionCompilationError: + return 'Expression compilation error'; + case kCustomStreamDoesNotExist: + return 'Custom stream does not exist'; + case kCoreStreamNotAllowed: + return 'Core streams are not allowed'; + } + } + + int get code { + switch (this) { + case kServerError: + return -32000; + case kInvalidRequest: + return -32600; + case kMethodNotFound: + return -32601; + case kInvalidParams: + return -32602; + case kInternalError: + return -32603; + case kFeatureDisabled: + return 100; + case kStreamAlreadySubscribed: + return 103; + case kStreamNotSubscribed: + return 104; + case kIsolateMustBePaused: + return 106; + case kServiceAlreadyRegistered: + return 111; + case kServiceDisappeared: + return 112; + case kExpressionCompilationError: + return 113; + case kCustomStreamDoesNotExist: + return 130; + case kCoreStreamNotAllowed: + return 131; + } + } +} + +class RPCError implements Exception { + @Deprecated('Use RPCErrorKind.kServerError.code instead.') + static int get kServerError => RPCErrorKind.kServerError.code; + + @Deprecated('Use RPCErrorKind.kInvalidRequest.code instead.') + static int get kInvalidRequest => RPCErrorKind.kInvalidRequest.code; + + @Deprecated('Use RPCErrorKind.kMethodNotFound.code instead.') + static int get kMethodNotFound => RPCErrorKind.kMethodNotFound.code; + + @Deprecated('Use RPCErrorKind.kInvalidParams.code instead.') + static int get kInvalidParams => RPCErrorKind.kInvalidParams.code; + + @Deprecated('Use RPCErrorKind.kInternalError.code instead.') + static int get kInternalError => RPCErrorKind.kInternalError.code; static RPCError parse(String callingMethod, dynamic json) { return RPCError(callingMethod, json['code'], json['message'], json['data']); @@ -282,7 +411,9 @@ class RPCError implements Exception { final String message; final Map? data; - RPCError(this.callingMethod, this.code, this.message, [this.data]); + RPCError(this.callingMethod, this.code, [message, this.data]) + : message = + message ?? RPCErrorKind.fromCode(code)?.message ?? 'Unknown error'; RPCError.withDetails(this.callingMethod, this.code, this.message, {Object? details}) @@ -710,8 +841,8 @@ class VmServerConnection { } final method = request['method'] as String?; if (method == null) { - throw RPCError( - null, RPCError.kInvalidRequest, 'Invalid Request', request); + throw RPCError(null, RPCErrorKind.kInvalidRequest.code, + 'Invalid Request', request); } final params = request['params'] as Map?; late Response response; @@ -789,7 +920,8 @@ class VmServerConnection { response = await _serviceImplementation.callServiceExtension(method, isolateId: isolateId, args: args); } else { - throw RPCError(method, RPCError.kMethodNotFound, 'Method not found', request); + throw RPCError(method, RPCErrorKind.kMethodNotFound.code, + 'Method not found', request); } '''); // Terminate the switch @@ -815,7 +947,7 @@ class VmServerConnection { final error = e is RPCError ? e.toMap() : { - 'code': RPCError.kInternalError, + 'code': RPCErrorKind.kInternalError.code, 'message': '${request['method']}: $e', 'data': {'details': '$st'}, };