in pkg/vm_service/tool/dart/generate_dart.dart [690:1107]
void _delegateRequest(Map<String, Object?> request) async {
try {
var id = request['id'];
// Check if this is actually a response to a pending request.
if (_pendingServiceExtensionRequests.containsKey(id)) {
final pending = _pendingServiceExtensionRequests[id]!;
pending.complete(Map<String, Object?>.of(request));
return;
}
final method = request['method'] as String?;
if (method == null) {
throw RPCError(
null, RPCError.kInvalidRequest, 'Invalid Request', request);
}
final params = request['params'] as Map<String, dynamic>?;
late Response response;
switch(method) {
case 'registerService':
$_registerServiceImpl
break;
''');
methods.forEach((m) {
if (m.name != 'registerService') {
gen.writeln("case '${m.name}':");
if (m.name == 'streamListen') {
gen.writeln(_streamListenCaseImpl);
} else if (m.name == 'streamCancel') {
gen.writeln(_streamCancelCaseImpl);
} else {
bool firstParam = true;
final nullCheck = () {
final result = firstParam ? '!' : '';
if (firstParam) {
firstParam = false;
}
return result;
};
if (m.deprecated) {
gen.writeln("// ignore: deprecated_member_use_from_same_package");
}
gen.write("response = await _serviceImplementation.${m.name}(");
// Positional args
m.args.where((arg) => !arg.optional).forEach((MethodArg arg) {
if (arg.type.isArray) {
gen.write(
"${arg.type.listCreationRef}.from(params${nullCheck()}['${arg.name}'] ?? []), ");
} else {
gen.write("params${nullCheck()}['${arg.name}'], ");
}
});
// Optional named args
var namedArgs = m.args.where((arg) => arg.optional);
if (namedArgs.isNotEmpty) {
namedArgs.forEach((arg) {
if (arg.name == 'scope') {
gen.writeln(
"${arg.name}: params${nullCheck()}['${arg.name}']?.cast<String, String>(), ");
} else {
gen.writeln(
"${arg.name}: params${nullCheck()}['${arg.name}'], ");
}
});
}
gen.writeln(");");
}
gen.writeln('break;');
}
});
// Handle service extensions
gen.writeln('default:');
gen.writeln('''
final registeredClient = _serviceExtensionRegistry.clientFor(method);
if (registeredClient != null) {
// Check for any client which has registered this extension, if we
// have one then delegate the request to that client.
_responseSink.add(
await registeredClient._forwardServiceExtensionRequest(request));
// Bail out early in this case, we are just acting as a proxy and
// never get a `Response` instance.
return;
} else if (method.startsWith('ext.')) {
// Remaining methods with `ext.` are assumed to be registered via
// dart:developer, which the service implementation handles.
final args = params == null ? null : Map<String, dynamic>.of(params);
final isolateId = args?.remove('isolateId');
response = await _serviceImplementation.callServiceExtension(method,
isolateId: isolateId, args: args);
} else {
throw RPCError(method, RPCError.kMethodNotFound, 'Method not found', request);
}
''');
// Terminate the switch
gen.writeln('}');
// Generate the json success response
gen.write("""_responseSink.add({
'jsonrpc': '2.0',
'id': id,
'result': response.toJson(),
});
""");
// Close the try block, handle errors
gen.write(r'''
} catch (e, st) {
final error = e is RPCError
? e.toMap()
: {
'code': RPCError.kInternalError,
'message': '${request['method']}: $e',
'data': {'details': '$st'},
};
_responseSink.add({
'jsonrpc': '2.0',
'id': request['id'],
'error': error,
});
}
''');
// terminate the _delegateRequest method
gen.write('}');
gen.writeln();
gen.write('}');
gen.writeln();
gen.write('''
class _OutstandingRequest<T> {
_OutstandingRequest(this.method);
static int _idCounter = 0;
final String id = '\${_idCounter++}';
final String method;
final StackTrace _stackTrace = StackTrace.current;
final Completer<T> _completer = Completer<T>();
Future<T> get future => _completer.future;
void complete(T value) => _completer.complete(value);
void completeError(Object error) =>
_completer.completeError(error, _stackTrace);
}
''');
// The client side service implementation.
gen.writeStatement('class VmService implements VmServiceInterface {');
gen.writeStatement('late final StreamSubscription _streamSub;');
gen.writeStatement('late final Function _writeMessage;');
gen.writeStatement(
'final Map<String, _OutstandingRequest> _outstandingRequests = {};');
gen.writeStatement('Map<String, ServiceCallback> _services = {};');
gen.writeStatement('late final Log _log;');
gen.write('''
StreamController<String> _onSend = StreamController.broadcast(sync: true);
StreamController<String> _onReceive = StreamController.broadcast(sync: true);
final Completer _onDoneCompleter = Completer();
Map<String, StreamController<Event>> _eventControllers = {};
StreamController<Event> _getEventController(String eventName) {
StreamController<Event>? controller = _eventControllers[eventName];
if (controller == null) {
controller = StreamController.broadcast();
_eventControllers[eventName] = controller;
}
return controller;
}
late final DisposeHandler? _disposeHandler;
VmService(Stream<dynamic> /*String|List<int>*/ inStream, void writeMessage(String message), {
Log? log,
DisposeHandler? disposeHandler,
Future? streamClosed,
}) {
_streamSub = inStream.listen(_processMessage, onDone: ()=> _onDoneCompleter.complete());
_writeMessage = writeMessage;
_log = log == null ? _NullLog() : log;
_disposeHandler = disposeHandler;
streamClosed?.then((_) {
if (!_onDoneCompleter.isCompleted) {
_onDoneCompleter.complete();
}
});
}
@override
Stream<Event> onEvent(String streamId) => _getEventController(streamId).stream;
''');
// streamCategories
streamCategories.forEach((s) => s.generate(gen));
gen.writeln();
methods.forEach((m) => m.generate(gen));
gen.out(_implCode);
gen.writeStatement('}');
gen.writeln();
gen.out(_rpcError);
gen.writeln('// enums');
enums.forEach((e) {
if (e.name == 'EventKind') {
_generateEventStream(gen);
}
e.generate(gen);
});
gen.writeln();
gen.writeln('// types');
types.where((t) => !t!.skip).forEach((t) => t!.generate(gen));
}
void generateAsserts(DartGenerator gen) {
gen.out(r'''
// 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.
// This is a generated file.
/// A library for asserting correct responses from the VM Service.
import 'package:vm_service/vm_service.dart' as vms;
dynamic assertNotNull(dynamic obj) {
if (obj == null) throw 'assert failed';
return obj;
}
bool assertBool(bool obj) {
return obj;
}
int assertInt(int obj) {
return obj;
}
double assertDouble(double obj) {
return obj;
}
dynamic assertDynamic(dynamic obj) {
assertNotNull(obj);
return obj;
}
List<dynamic> assertListOfDynamic(List<dynamic> list) {
return list;
}
List<int> assertListOfInt(List<int> list) {
for (int elem in list) {
assertInt(elem);
}
return list;
}
List<String> assertListOfString(List<String> list) {
for (String elem in list) {
assertString(elem);
}
return list;
}
List<vms.IsolateFlag> assertListOfIsolateFlag(List<vms.IsolateFlag> list) {
for (vms.IsolateFlag elem in list) {
assertIsolateFlag(elem);
}
return list;
}
String assertString(String obj) {
if (obj.isEmpty) throw 'expected non-zero length string';
return obj;
}
vms.Success assertSuccess(vms.Success obj) {
if (obj.type != 'Success') throw 'expected Success';
return obj;
}
/// Assert PauseStart, PauseExit, PauseBreakpoint, PauseInterrupted,
/// PauseException, Resume, BreakpointAdded, BreakpointResolved,
/// BreakpointRemoved, and Inspect events.
vms.Event assertDebugEvent(vms.Event event) {
assertEvent(event);
if (event.kind == vms.EventKind.kPauseBreakpoint ||
event.kind == vms.EventKind.kBreakpointAdded ||
event.kind == vms.EventKind.kBreakpointRemoved ||
event.kind == vms.EventKind.kBreakpointResolved) {
assertBreakpoint(event.breakpoint!);
}
if (event.kind == vms.EventKind.kPauseBreakpoint) {
for (vms.Breakpoint elem in event.pauseBreakpoints!) {
assertBreakpoint(elem);
}
}
if (event.kind == vms.EventKind.kPauseBreakpoint ||
event.kind == vms.EventKind.kPauseInterrupted ||
event.kind == vms.EventKind.kPauseException ||
event.kind == vms.EventKind.kResume) {
// For PauseInterrupted events, there will be no top frame if the isolate is
// idle (waiting in the message loop).
// For the Resume event, the top frame is provided at all times except for
// the initial resume event that is delivered when an isolate begins
// execution.
if (event.topFrame != null ||
(event.kind != vms.EventKind.kPauseInterrupted &&
event.kind != vms.EventKind.kResume)) {
assertFrame(event.topFrame!);
}
}
if (event.kind == vms.EventKind.kPauseException) {
assertInstanceRef(event.exception!);
}
if (event.kind == vms.EventKind.kPauseBreakpoint ||
event.kind == vms.EventKind.kPauseInterrupted) {
assertBool(event.atAsyncSuspension!);
}
if (event.kind == vms.EventKind.kInspect) {
assertInstanceRef(event.inspectee!);
}
return event;
}
/// Assert IsolateStart, IsolateRunnable, IsolateExit, IsolateUpdate,
/// and ServiceExtensionAdded events.
vms.Event assertIsolateEvent(vms.Event event) {
assertEvent(event);
if (event.kind == vms.EventKind.kServiceExtensionAdded) {
assertString(event.extensionRPC!);
}
return event;
}
''');
for (Enum e in enums) {
e.generateAssert(gen);
}
for (Type? type in types) {
if (type!.name == 'Success') continue;
type.generateAssert(gen);
if (type.name!.endsWith('Ref') ||
[
'BoundVariable',
'Breakpoint',
'ClassHeapStats',
'CodeRegion',
'ContextElement',
'CpuSample',
'Flag',
'Frame',
'InboundReference',
'LibraryDependency',
'Message',
'ProcessMemoryItem',
'ProfileFunction',
'ProcessMemoryItem',
'Protocol',
'RetainingObject',
'SourceReportRange',
'TimelineEvent',
].contains(type.name)) {
type.generateListAssert(gen);
}
}
}
void setDefaultValue(String typeName, String fieldName, String defaultValue) {
types
.firstWhere((t) => t!.name == typeName)!
.fields
.firstWhere((f) => f.name == fieldName)
.defaultValue = defaultValue;
}
bool isEnumName(String? typeName) =>
enums.any((Enum e) => e.name == typeName);
Type? getType(String? name) =>
types.firstWhere((t) => t!.name == name, orElse: () => null);
void _parseStreamListenDocs(String docs) {
Iterator<String> lines = docs.split('\n').map((l) => l.trim()).iterator;
bool inStreamDef = false;
while (lines.moveNext()) {
final String line = lines.current;
if (line.startsWith('streamId |')) {
inStreamDef = true;
lines.moveNext();
} else if (inStreamDef) {
if (line.isEmpty) {
inStreamDef = false;
} else {
streamCategories.add(StreamCategory(line));
}
}
}
}
void _generateEventStream(DartGenerator gen) {
gen.writeln();
gen.writeDocs('An enum of available event streams.');
gen.writeln('class EventStreams {');
gen.writeln('EventStreams._();');
gen.writeln();
streamCategories.forEach((c) {
gen.writeln("static const String k${c.name} = '${c.name}';");
});
gen.writeln('}');
}
}