jsonrpc.dart 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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. void register(String name, FutureOr<dynamic> Function(List) method) {
  29. _methods[name] = method;
  30. }
  31. FutureOr<dynamic> _dispatch(ThreadedVM vm) {
  32. if (vm.state != ThreadState.suspended) throw Exception("Unexpected state");
  33. String? json = vm.read_jsonrpc_request();
  34. if (json == null) throw Exception("JSONRPC request is null");
  35. var request = jsonDecode(json);
  36. var f = _methods[request['method']];
  37. if (f == null) throw JsonRpcError(-32601, "Method not found");
  38. try {
  39. return f(request['params'] as List);
  40. } catch (e) {
  41. throw JsonRpcError(-32000, e.toString());
  42. }
  43. }
  44. FutureOr<void> dispatch(ThreadedVM vm) async {
  45. onPreDispatch?.call();
  46. try {
  47. dynamic ret = _dispatch(vm);
  48. if (ret is Future<dynamic>) ret = await ret;
  49. vm.write_jsonrpc_response(jsonEncode({"result": ret}));
  50. onPostDispatch?.call();
  51. } on JsonRpcError catch (e) {
  52. vm.write_jsonrpc_response(jsonEncode({"error": e.payload}));
  53. return;
  54. }
  55. }
  56. Future<void> attach(ThreadedVM vm,
  57. {Duration? spinFreq = const Duration(milliseconds: 20)}) async {
  58. while (vm.state.index <= ThreadState.running.index) {
  59. if (spinFreq != null) await Future.delayed(spinFreq);
  60. }
  61. switch (vm.state) {
  62. case ThreadState.suspended:
  63. await dispatch(vm);
  64. await attach(vm, spinFreq: spinFreq);
  65. break;
  66. case ThreadState.finished:
  67. break;
  68. default:
  69. throw Exception("Unexpected state");
  70. }
  71. }
  72. int _fileId = 0;
  73. final Map<int, File> _files = {};
  74. void registerOS(JsonRpcServer rpcServer) {
  75. rpcServer.register("fopen", (params) {
  76. var path = params[0];
  77. //var mode = params[1];
  78. var fp = File(path);
  79. _fileId += 1;
  80. _files[_fileId] = fp;
  81. return _fileId;
  82. });
  83. rpcServer.register("fclose", (params) {
  84. var fp = _files[params[0]];
  85. if (fp == null) throw Exception("FileIO was closed");
  86. _files.remove(params[0]);
  87. });
  88. rpcServer.register("fread", (params) {
  89. var fp = _files[params[0]];
  90. if (fp == null) throw Exception("FileIO was closed");
  91. return fp.readAsStringSync();
  92. });
  93. rpcServer.register("fwrite", (params) {
  94. var fp = _files[params[0]];
  95. if (fp == null) throw Exception("FileIO was closed");
  96. fp.writeAsStringSync(params[1]);
  97. });
  98. rpcServer.register("os.listdir", (params) {
  99. String path = params[0];
  100. var entries = Directory(path).listSync(followLinks: false);
  101. var ret = entries.map((e) {
  102. return e.path.split(Platform.pathSeparator).last;
  103. }).toList();
  104. return ret;
  105. });
  106. rpcServer.register("os.mkdir", (params) {
  107. String path = params[0];
  108. Directory(path).createSync();
  109. });
  110. rpcServer.register("os.rmdir", (params) {
  111. String path = params[0];
  112. Directory(path).deleteSync(recursive: true);
  113. });
  114. rpcServer.register("os.remove", (params) {
  115. String path = params[0];
  116. File(path).deleteSync();
  117. });
  118. rpcServer.register("os.path.exists", (params) {
  119. String path = params[0];
  120. bool _0 = Directory(path).existsSync();
  121. bool _1 = File(path).existsSync();
  122. return (_0 || _1);
  123. });
  124. }
  125. }