| // Copyright (c) 2013, 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._vmservice; |
| |
| class RunningIsolates implements MessageRouter { |
| final isolates = <int, RunningIsolate>{}; |
| int? _rootPortId; |
| |
| RunningIsolates(); |
| |
| void isolateStartup(int portId, SendPort sp, String name) { |
| if (_rootPortId == null) { |
| _rootPortId = portId; |
| } |
| var ri = RunningIsolate(portId, sp, name); |
| isolates[portId] = ri; |
| } |
| |
| void isolateShutdown(int portId, SendPort sp) { |
| if (_rootPortId == portId) { |
| _rootPortId = null; |
| } |
| (isolates.remove(portId))?.onIsolateExit(); |
| } |
| |
| @override |
| Future<Response> routeRequest(VMService service, Message message) { |
| String isolateParam = message.params['isolateId']!; |
| int isolateId; |
| if (!isolateParam.startsWith('isolates/')) { |
| message.setErrorResponse( |
| kInvalidParams, "invalid 'isolateId' parameter: $isolateParam"); |
| return message.response; |
| } |
| isolateParam = isolateParam.substring('isolates/'.length); |
| if (isolateParam == 'root') { |
| isolateId = _rootPortId!; |
| } else { |
| try { |
| isolateId = int.parse(isolateParam); |
| } catch (e) { |
| message.setErrorResponse( |
| kInvalidParams, "invalid 'isolateId' parameter: $isolateParam"); |
| return message.response; |
| } |
| } |
| final isolate = isolates[isolateId]; |
| if (isolate == null) { |
| // There is some chance that this isolate may have lived before, |
| // so return a sentinel rather than an error. |
| final result = <String, String>{ |
| 'type': 'Sentinel', |
| 'kind': 'Collected', |
| 'valueAsString': '<collected>', |
| }; |
| message.setResponse(encodeResult(message, result)); |
| return message.response; |
| } |
| |
| if (message.method == 'evaluateInFrame' || message.method == 'evaluate') { |
| return _Evaluator(message, isolate, service).run(); |
| } else { |
| return isolate.routeRequest(service, message); |
| } |
| } |
| |
| @override |
| void routeResponse(Message message) {} |
| } |
| |
| /// Class that knows how to orchestrate expression evaluation in dart2 world. |
| class _Evaluator { |
| _Evaluator(this._message, this._isolate, this._service); |
| |
| Future<Response> run() async { |
| final buildScopeResponse = await _buildScope(); |
| final responseJson = buildScopeResponse.decodeJson(); |
| |
| if (responseJson.containsKey('error')) { |
| return Response.from(encodeCompilationError( |
| _message, responseJson['error']['data']['details'])); |
| } |
| |
| String kernelBase64; |
| try { |
| kernelBase64 = await _compileExpression(responseJson['result']); |
| } catch (e) { |
| return Response.from(encodeCompilationError(_message, e.toString())); |
| } |
| return await _evaluateCompiledExpression(kernelBase64); |
| } |
| |
| Message _message; |
| RunningIsolate _isolate; |
| VMService _service; |
| |
| Future<Response> _buildScope() { |
| final params = _setupParams(); |
| params['isolateId'] = _message.params['isolateId']; |
| final buildScopeParams = <String, dynamic>{ |
| 'method': '_buildExpressionEvaluationScope', |
| 'id': _message.serial, |
| 'params': params, |
| }; |
| if (_message.params['scope'] != null) { |
| buildScopeParams['params']['scope'] = _message.params['scope']; |
| } |
| final buildScope = |
| Message._fromJsonRpcRequest(_message.client!, buildScopeParams); |
| |
| // Decode the JSON and and insert it into the map. The map key |
| // is the request Uri. |
| return _isolate.routeRequest(_service, buildScope); |
| } |
| |
| Future<String> _compileExpression( |
| Map<String, dynamic> buildScopeResponseResult) { |
| Client? externalClient = |
| _service._findFirstClientThatHandlesService('compileExpression'); |
| |
| final compileParams = <String, dynamic>{ |
| 'isolateId': _message.params['isolateId']!, |
| 'expression': _message.params['expression']!, |
| 'definitions': buildScopeResponseResult['param_names']!, |
| 'typeDefinitions': buildScopeResponseResult['type_params_names']!, |
| 'libraryUri': buildScopeResponseResult['libraryUri']!, |
| 'isStatic': buildScopeResponseResult['isStatic']!, |
| }; |
| final klass = buildScopeResponseResult['klass']; |
| if (klass != null) { |
| compileParams['klass'] = klass; |
| } |
| if (externalClient != null) { |
| final compileExpression = Message.forMethod('compileExpression'); |
| compileExpression.client = externalClient; |
| compileExpression.params.addAll(compileParams); |
| |
| final id = _service._serviceRequests.newId(); |
| final oldId = _message.serial; |
| final completer = Completer<String>(); |
| externalClient.serviceHandles[id] = (Message? m) { |
| if (m != null) { |
| completer.complete(json.encode(m.forwardToJson({'id': oldId}))); |
| } else { |
| completer.complete(encodeRpcError(_message, kServiceDisappeared)); |
| } |
| }; |
| externalClient.post(Response.json(compileExpression |
| .forwardToJson({'id': id, 'method': 'compileExpression'}))); |
| return completer.future.then((s) => jsonDecode(s)).then((json) { |
| final Map<String, dynamic> jsonMap = json; |
| if (jsonMap.containsKey('error')) { |
| throw jsonMap['error']; |
| } |
| return jsonMap['result']['kernelBytes']; |
| }); |
| } else { |
| // fallback to compile using kernel service |
| final compileExpressionParams = <String, dynamic>{ |
| 'method': '_compileExpression', |
| 'id': _message.serial, |
| 'params': compileParams, |
| }; |
| final compileExpression = Message._fromJsonRpcRequest( |
| _message.client!, compileExpressionParams); |
| |
| return _isolate |
| .routeRequest(_service, compileExpression) |
| .then((response) => response.decodeJson()) |
| .then((json) { |
| if (json['result'] != null) { |
| return json['result']['kernelBytes']; |
| } |
| throw json['error']['data']['details']; |
| }); |
| } |
| } |
| |
| Future<Response> _evaluateCompiledExpression(String kernelBase64) { |
| if (kernelBase64.isNotEmpty) { |
| final params = _setupParams(); |
| params['isolateId'] = _message.params['isolateId']; |
| params['kernelBytes'] = kernelBase64; |
| params['disableBreakpoints'] = _message.params['disableBreakpoints']; |
| final runParams = <String, dynamic>{ |
| 'method': '_evaluateCompiledExpression', |
| 'id': _message.serial, |
| 'params': params, |
| }; |
| if (_message.params['scope'] != null) { |
| runParams['params']['scope'] = _message.params['scope']; |
| } |
| final runExpression = |
| Message._fromJsonRpcRequest(_message.client!, runParams); |
| return _isolate.routeRequest(_service, runExpression); // _message |
| } else { |
| // empty kernel indicates dart1 mode |
| return _isolate.routeRequest(_service, _message); |
| } |
| } |
| |
| Map<String, dynamic> _setupParams() { |
| if (_message.method == 'evaluateInFrame') { |
| return <String, dynamic>{'frameIndex': _message.params['frameIndex']}; |
| } else { |
| assert(_message.method == 'evaluate'); |
| return <String, dynamic>{'targetId': _message.params['targetId']}; |
| } |
| } |
| } |