jsonrpc.dart 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // ignore_for_file: no_leading_underscores_for_local_identifiers, non_constant_identifier_names
  2. import 'dart:async';
  3. import 'dart:convert';
  4. import 'dart:io';
  5. import 'package:pocketpy/pocketpy.dart';
  6. class _JsonRpcError {
  7. final Map<String, dynamic> payload = {};
  8. _JsonRpcError(int code, String message, {dynamic data}) {
  9. payload['code'] = code;
  10. payload['message'] = message;
  11. if (data != null) {
  12. payload['data'] = data;
  13. }
  14. }
  15. }
  16. class JsonRpcServer {
  17. final Map<String, FutureOr<dynamic> Function(List)> _methods = {};
  18. final void Function()? onPreDispatch;
  19. final void Function()? onPostDispatch;
  20. final bool enableFileAccess;
  21. JsonRpcServer(
  22. {this.onPreDispatch,
  23. this.onPostDispatch,
  24. this.enableFileAccess = false}) {
  25. if (!enableFileAccess) return;
  26. _registerOS(this);
  27. }
  28. /// Register a JSONRPC handler.
  29. void register(String name, FutureOr<dynamic> Function(List) method) {
  30. _methods[name] = method;
  31. }
  32. FutureOr<dynamic> _dispatch(ThreadedVM vm) {
  33. if (vm.state != ThreadState.suspended) throw Exception("Unexpected state");
  34. String? json = vm.read_jsonrpc_request();
  35. if (json == null) throw Exception("JSONRPC request is null");
  36. var request = jsonDecode(json);
  37. var f = _methods[request['method']];
  38. if (f == null) throw _JsonRpcError(-32601, "Method not found");
  39. try {
  40. return f(request['params'] as List);
  41. } catch (e) {
  42. throw _JsonRpcError(-32000, e.toString());
  43. }
  44. }
  45. /// Dispatch a JSONRPC request.
  46. FutureOr<void> dispatch(ThreadedVM vm) async {
  47. onPreDispatch?.call();
  48. try {
  49. dynamic ret = _dispatch(vm);
  50. if (ret is Future<dynamic>) ret = await ret;
  51. vm.write_jsonrpc_response(jsonEncode({"result": ret}));
  52. onPostDispatch?.call();
  53. } on _JsonRpcError catch (e) {
  54. vm.write_jsonrpc_response(jsonEncode({"error": e.payload}));
  55. return;
  56. }
  57. }
  58. /// Attach the JsonRpcServer into a ThreadedVM. Once the ThreadedVM encounters JSONRPC request, it takes care of it automatically. This process will be stopped when the whole execution is done.
  59. Future<void> attach(ThreadedVM vm,
  60. {Duration? spinFreq = const Duration(milliseconds: 20)}) async {
  61. while (vm.state.index <= ThreadState.running.index) {
  62. if (spinFreq != null) await Future.delayed(spinFreq);
  63. }
  64. switch (vm.state) {
  65. case ThreadState.suspended:
  66. await dispatch(vm);
  67. await attach(vm, spinFreq: spinFreq);
  68. break;
  69. case ThreadState.finished:
  70. break;
  71. default:
  72. throw Exception("Unexpected state");
  73. }
  74. }
  75. int _fileId = 0;
  76. final Map<int, File> _files = {};
  77. void _registerOS(JsonRpcServer rpcServer) {
  78. rpcServer.register("fopen", (params) {
  79. var path = params[0];
  80. //var mode = params[1];
  81. var fp = File(path);
  82. _fileId += 1;
  83. _files[_fileId] = fp;
  84. return _fileId;
  85. });
  86. rpcServer.register("fclose", (params) {
  87. var fp = _files[params[0]];
  88. if (fp == null) throw Exception("FileIO was closed");
  89. _files.remove(params[0]);
  90. });
  91. rpcServer.register("fread", (params) {
  92. var fp = _files[params[0]];
  93. if (fp == null) throw Exception("FileIO was closed");
  94. return fp.readAsStringSync();
  95. });
  96. rpcServer.register("fwrite", (params) {
  97. var fp = _files[params[0]];
  98. if (fp == null) throw Exception("FileIO was closed");
  99. fp.writeAsStringSync(params[1]);
  100. });
  101. rpcServer.register("os.listdir", (params) {
  102. String path = params[0];
  103. var entries = Directory(path).listSync(followLinks: false);
  104. var ret = entries.map((e) {
  105. return e.path.split(Platform.pathSeparator).last;
  106. }).toList();
  107. return ret;
  108. });
  109. rpcServer.register("os.mkdir", (params) {
  110. String path = params[0];
  111. Directory(path).createSync();
  112. });
  113. rpcServer.register("os.rmdir", (params) {
  114. String path = params[0];
  115. Directory(path).deleteSync(recursive: true);
  116. });
  117. rpcServer.register("os.remove", (params) {
  118. String path = params[0];
  119. File(path).deleteSync();
  120. });
  121. rpcServer.register("os.path.exists", (params) {
  122. String path = params[0];
  123. bool _0 = Directory(path).existsSync();
  124. bool _1 = File(path).existsSync();
  125. return (_0 || _1);
  126. });
  127. }
  128. }