| Index: sdk/lib/developer/extension.dart
|
| diff --git a/sdk/lib/developer/extension.dart b/sdk/lib/developer/extension.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..95c3db2e7253eb3b3d2b7aab9bba0d2f5f2853b0
|
| --- /dev/null
|
| +++ b/sdk/lib/developer/extension.dart
|
| @@ -0,0 +1,182 @@
|
| +// 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;
|
| +
|
| +class ServiceExtensionResponse {
|
| + final String _result;
|
| + final int _errorCode;
|
| + final String _errorDetail;
|
| +
|
| + ServiceExtensionResponse.result(this._result)
|
| + : _errorCode = null,
|
| + _errorDetail = null {
|
| + if (_result is! String) {
|
| + throw new ArgumentError.value(_result, "result", "Must be a String");
|
| + }
|
| + }
|
| +
|
| + ServiceExtensionResponse.error(this._errorCode, this._errorDetail)
|
| + : _result = null {
|
| + _validateErrorCode(_errorCode);
|
| + if (_errorDetail is! String) {
|
| + throw new ArgumentError.value(_errorDetail,
|
| + "errorDetail",
|
| + "Must be a String");
|
| + }
|
| + }
|
| +
|
| + /// Invalid method parameter(s) error code.
|
| + static const kInvalidParams = -32602;
|
| + /// Generic extension error code.
|
| + static const kExtensionError = -32000;
|
| + /// Maximum extension provided error code.
|
| + static const kExtensionErrorMax = -32000;
|
| + /// Minimum extension provided error code.
|
| + static const kExtensionErrorMin = -32016;
|
| +
|
| + static String _errorCodeMessage(int errorCode) {
|
| + _validateErrorCode(errorCode);
|
| + if (errorCode == kInvalidParams) {
|
| + return "Invalid params";
|
| + }
|
| + return "Server error";
|
| + }
|
| +
|
| + static _validateErrorCode(int errorCode) {
|
| + if (errorCode is! int) {
|
| + throw new ArgumentError.value(errorCode, "errorCode", "Must be an int");
|
| + }
|
| + if (errorCode == kInvalidParams) {
|
| + return;
|
| + }
|
| + if ((errorCode >= kExtensionErrorMin) &&
|
| + (errorCode <= kExtensionErrorMax)) {
|
| + return;
|
| + }
|
| + throw new ArgumentError.value(errorCode, "errorCode", "Out of range");
|
| + }
|
| +
|
| + bool _isError() => (_errorCode != null) && (_errorDetail != null);
|
| +
|
| + String _toString() {
|
| + if (_result != null) {
|
| + return _result;
|
| + } else {
|
| + assert(_errorCode != null);
|
| + 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.
|
| +/// [parameters] - the parameters.
|
| +typedef Future<ServiceExtensionResponse>
|
| + ServiceExtensionHandler(String method, Map parameters);
|
| +
|
| +final _extensions = new Map<String, ServiceExtensionHandler>();
|
| +
|
| +/// Register a [ServiceExtensionHandler] that will be invoked in this isolate
|
| +/// for [method].
|
| +void registerExtension(String method, ServiceExtensionHandler handler) {
|
| + if (_extensions[method] != null) {
|
| + throw new ArgumentError('Extension already registered: $method');
|
| + }
|
| + if (handler is! ServiceExtensionHandler) {
|
| + throw new ArgumentError.value(handler,
|
| + 'handler',
|
| + 'Must be a ServiceExtensionHandler');
|
| + }
|
| + _extensions[method] = handler;
|
| +}
|
| +
|
| +bool _extensionExists(String method) {
|
| + return _extensions[method] != null;
|
| +}
|
| +
|
| +bool _invokeExtension(String method,
|
| + List<String> parameterKeys,
|
| + List<String> parameterValues,
|
| + SendPort replyPort,
|
| + Object id) {
|
| + ServiceExtensionHandler handler = _extensions[method];
|
| + assert(handler != null);
|
| + var parameters = {};
|
| + for (var i = 0; i < parameterKeys.length; i++) {
|
| + parameters[parameterKeys[i]] = parameterValues[i];
|
| + }
|
| + var response;
|
| + try {
|
| + response = handler(method, parameters);
|
| + } catch (e, st) {
|
| + var errorDetails = (st == null) ? '$e' : '$e\n$st';
|
| + response = new ServiceExtensionResponse.error(
|
| + ServiceExtensionResponse.kExtensionError,
|
| + errorDetails);
|
| + _postResponse(replyPort, id, response);
|
| + return true;
|
| + }
|
| + if (response is! Future) {
|
| + response = new ServiceExtensionResponse.error(
|
| + ServiceExtensionResponse.kExtensionError,
|
| + "Extension handler must return a Future");
|
| + _postResponse(replyPort, id, response);
|
| + return true;
|
| + }
|
| + response.catchError((e, st) {
|
| + var errorDetails = (st == null) ? '$e' : '$e\n$st';
|
| + return new ServiceExtensionResponse.error(
|
| + ServiceExtensionResponse.kExtensionError,
|
| + errorDetails);
|
| + }).then((response) {
|
| + if (response == null) {
|
| + response = new ServiceExtensionResponse.error(
|
| + ServiceExtensionResponse.kExtensionError,
|
| + "Extension handler returned null");
|
| + }
|
| + _postResponse(replyPort, id, response);
|
| + });
|
| + // Push an event on the event loop so that we invoke the scheduled microtasks.
|
| + Timer.run(() {});
|
| + return true;
|
| +}
|
| +
|
| +_postResponse(SendPort replyPort,
|
| + Object id,
|
| + ServiceExtensionResponse response) {
|
| + assert(replyPort != null);
|
| + if (id == null) {
|
| + // No id -> no response.
|
| + // TODO(johnmccutchan): This code and the code in service.cc leave the
|
| + // service isolate with an open port. Consider posting 'null' to indicate
|
| + // that no response is coming.
|
| + return;
|
| + }
|
| + assert(id != null);
|
| + StringBuffer sb = new StringBuffer();
|
| + sb.write('{"jsonrpc":"2.0",');
|
| + if (response._isError()) {
|
| + sb.write('"error":');
|
| + } else {
|
| + sb.write('"result":');
|
| + }
|
| + sb.write('${response._toString()},');
|
| + if (id is String) {
|
| + sb.write('"id":"$id"}');
|
| + } else {
|
| + sb.write('"id":$id}');
|
| + }
|
| + replyPort.send(sb.toString());
|
| +}
|
|
|