blob: a69f696dc78dc0b42fade575efa63a60a4e4571e [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart.developer;
/// A response to a service protocol extension RPC.
///
/// If the RPC was successful, use [ServiceExtensionResponse.result], otherwise
/// use [ServiceExtensionResponse.error].
class ServiceExtensionResponse {
final String _result;
final int _errorCode;
final String _errorDetail;
/// Creates a successful response to a service protocol extension RPC.
///
/// Requires [result] to be a JSON object encoded as a string. When forming
/// the JSON-RPC message [result] will be inlined directly.
ServiceExtensionResponse.result(String result)
: _result = result,
_errorCode = -1,
_errorDetail = null {
if (_result is! String) {
throw new ArgumentError.value(_result, "result", "Must be a String");
}
}
/// Creates an error response to a service protocol extension RPC.
///
/// Requires [errorCode] to be [invalidParams] or between [extensionErrorMin]
/// and [extensionErrorMax]. Requires [errorDetail] to be a JSON object
/// encoded as a string. When forming the JSON-RPC message [errorDetail] will
/// be inlined directly.
ServiceExtensionResponse.error(int errorCode, String errorDetail)
: _result = null,
_errorCode = errorCode,
_errorDetail = errorDetail {
_validateErrorCode(_errorCode);
if (_errorDetail is! String) {
throw new ArgumentError.value(
_errorDetail, "errorDetail", "Must be a String");
}
}
/// Invalid method parameter(s) error code.
@deprecated
static const kInvalidParams = invalidParams;
/// Generic extension error code.
@deprecated
static const kExtensionError = extensionError;
/// Maximum extension provided error code.
@deprecated
static const kExtensionErrorMax = extensionErrorMax;
/// Minimum extension provided error code.
@deprecated
static const kExtensionErrorMin = extensionErrorMin;
/// Invalid method parameter(s) error code.
static const invalidParams = -32602;
/// Generic extension error code.
static const extensionError = -32000;
/// Maximum extension provided error code.
static const extensionErrorMax = -32000;
/// Minimum extension provided error code.
static const extensionErrorMin = -32016;
static String _errorCodeMessage(int errorCode) {
_validateErrorCode(errorCode);
if (errorCode == kInvalidParams) {
return "Invalid params";
}
return "Server error";
}
static _validateErrorCode(int errorCode) {
if (errorCode == invalidParams) {
return;
}
if ((errorCode >= extensionErrorMin) && (errorCode <= extensionErrorMax)) {
return;
}
throw new ArgumentError.value("${errorCode}", "errorCode", "Out of range");
}
bool _isError() => (_errorCode != -1) && (_errorDetail != null);
String _toString() {
if (_result != null) {
return _result;
} else {
assert(_errorCode != -1);
assert(_errorDetail != null);
return JSON.encode({
'code': _errorCode,
'message': _errorCodeMessage(_errorCode),
'data': {'details': _errorDetail}
});
}
}
}
/// A service protocol extension handler. Registered with [registerExtension].
///
/// Must complete to a [ServiceExtensionResponse].
///
/// [method] - the method name of the service protocol request.
/// [parameters] - A map holding the parameters to the service protocol request.
///
/// *NOTE*: All parameter names and values are **encoded as strings**.
typedef Future<ServiceExtensionResponse> ServiceExtensionHandler(
String method, Map<String, String> parameters);
/// Register a [ServiceExtensionHandler] that will be invoked in this isolate
/// for [method]. *NOTE*: Service protocol extensions must be registered
/// in each isolate.
///
/// *NOTE*: [method] must begin with 'ext.' and you should use the following
/// structure to avoid conflicts with other packages: 'ext.package.command'.
/// That is, immediately following the 'ext.' prefix, should be the registering
/// package name followed by another period ('.') and then the command name.
/// For example: 'ext.dart.io.getOpenFiles'.
///
/// Because service extensions are isolate specific, clients using extensions
/// must always include an 'isolateId' parameter with each RPC.
void registerExtension(String method, ServiceExtensionHandler handler) {
if (method is! String) {
throw new ArgumentError.value(method, 'method', 'Must be a String');
}
if (!method.startsWith('ext.')) {
throw new ArgumentError.value(method, 'method', 'Must begin with ext.');
}
if (_lookupExtension(method) != null) {
throw new ArgumentError('Extension already registered: $method');
}
if (handler is! ServiceExtensionHandler) {
throw new ArgumentError.value(
handler, 'handler', 'Must be a ServiceExtensionHandler');
}
_registerExtension(method, handler);
}
/// Post an event of [eventKind] with payload of [eventData] to the `Extension`
/// event stream.
void postEvent(String eventKind, Map eventData) {
if (eventKind is! String) {
throw new ArgumentError.value(eventKind, 'eventKind', 'Must be a String');
}
if (eventData is! Map) {
throw new ArgumentError.value(eventData, 'eventData', 'Must be a Map');
}
String eventDataAsString = JSON.encode(eventData);
_postEvent(eventKind, eventDataAsString);
}
void _postEvent(String eventKind, String eventData)
native "Developer_postEvent";
// Both of these functions are written inside C++ to avoid updating the data
// structures in Dart, getting an OOB, and observing stale state. Do not move
// these into Dart code unless you can ensure that the operations will can be
// done atomically. Native code lives in vm/isolate.cc-
// LookupServiceExtensionHandler and RegisterServiceExtensionHandler.
ServiceExtensionHandler _lookupExtension(String method)
native "Developer_lookupExtension";
_registerExtension(String method, ServiceExtensionHandler handler)
native "Developer_registerExtension";