| Index: sdk/lib/developer/extension.dart
|
| diff --git a/sdk/lib/developer/extension.dart b/sdk/lib/developer/extension.dart
|
| index f23b5ad5630b266f35c0ef1576cedf6595374ff8..05eeae7d855e48e422e47c8a9a5e212d44eed30c 100644
|
| --- a/sdk/lib/developer/extension.dart
|
| +++ b/sdk/lib/developer/extension.dart
|
| @@ -86,12 +86,15 @@ class ServiceExtensionResponse {
|
| 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) {
|
| + if (method is! String) {
|
| + throw new ArgumentError.value(method,
|
| + 'method',
|
| + 'Must be a String');
|
| + }
|
| + if (_lookupExtension(method) != null) {
|
| throw new ArgumentError('Extension already registered: $method');
|
| }
|
| if (handler is! ServiceExtensionHandler) {
|
| @@ -99,59 +102,68 @@ void registerExtension(String method, ServiceExtensionHandler handler) {
|
| 'handler',
|
| 'Must be a ServiceExtensionHandler');
|
| }
|
| - _extensions[method] = handler;
|
| + _registerExtension(method, handler);
|
| }
|
|
|
| -bool _scheduleExtension(String method,
|
| - List<String> parameterKeys,
|
| - List<String> parameterValues,
|
| - SendPort replyPort,
|
| - Object id) {
|
| - ServiceExtensionHandler handler = _extensions[method];
|
| - if (handler == null) {
|
| - return false;
|
| +// 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.
|
| +external ServiceExtensionHandler _lookupExtension(String method);
|
| +external _registerExtension(String method, ServiceExtensionHandler handler);
|
| +
|
| +// This code is only invoked when there is no other Dart code on the stack.
|
| +_runExtension(ServiceExtensionHandler handler,
|
| + String method,
|
| + List<String> parameterKeys,
|
| + List<String> parameterValues,
|
| + SendPort replyPort,
|
| + Object id) {
|
| + var parameters = {};
|
| + for (var i = 0; i < parameterKeys.length; i++) {
|
| + parameters[parameterKeys[i]] = parameterValues[i];
|
| }
|
| - // Defer execution of handler until next event loop.
|
| - Timer.run(() {
|
| - 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(
|
| + 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;
|
| + }
|
| + if (response is! Future) {
|
| + response = new ServiceExtensionResponse.error(
|
| ServiceExtensionResponse.kExtensionError,
|
| - errorDetails);
|
| - _postResponse(replyPort, id, response);
|
| - return;
|
| - }
|
| - if (response is! Future) {
|
| + "Extension handler must return a Future");
|
| + _postResponse(replyPort, id, response);
|
| + return;
|
| + }
|
| + response.catchError((e, st) {
|
| + // Catch any errors eagerly and wrap them in a ServiceExtensionResponse.
|
| + var errorDetails = (st == null) ? '$e' : '$e\n$st';
|
| + return new ServiceExtensionResponse.error(
|
| + ServiceExtensionResponse.kExtensionError,
|
| + errorDetails);
|
| + }).then((response) {
|
| + // Post the valid response or the wrapped error after verifying that
|
| + // the response is a ServiceExtensionResponse.
|
| + if (response is! ServiceExtensionResponse) {
|
| response = new ServiceExtensionResponse.error(
|
| - ServiceExtensionResponse.kExtensionError,
|
| - "Extension handler must return a Future");
|
| - _postResponse(replyPort, id, response);
|
| - return;
|
| - }
|
| - 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);
|
| - });
|
| + "Extension handler must complete to a ServiceExtensionResponse");
|
| + }
|
| + _postResponse(replyPort, id, response);
|
| + }).catchError((e, st) {
|
| + // We do not expect any errors to occur in the .then or .catchError blocks
|
| + // but, suppress them just in case.
|
| });
|
| - return true;
|
| }
|
|
|
| +// This code is only invoked by _runExtension.
|
| _postResponse(SendPort replyPort,
|
| Object id,
|
| ServiceExtensionResponse response) {
|
|
|