From 91b314d7f22155a7e156d5fe75668470bcd381f0 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Thu, 2 Feb 2023 16:58:01 +0800 Subject: [PATCH] up --- plugins/flutter/CHANGELOG.md | 4 + plugins/flutter/README.md | 1 - plugins/flutter/example/lib/main.dart | 3 +- plugins/flutter/example/pubspec.lock | 2 +- plugins/flutter/lib/jsonrpc.dart | 149 -------------------------- plugins/flutter/lib/no_web.dart | 106 +++++++++++++++--- plugins/flutter/lib/pocketpy.dart | 2 - plugins/flutter/lib/web.dart | 77 ++++++++++--- plugins/flutter/pubspec.yaml | 2 +- 9 files changed, 158 insertions(+), 188 deletions(-) delete mode 100644 plugins/flutter/lib/jsonrpc.dart diff --git a/plugins/flutter/CHANGELOG.md b/plugins/flutter/CHANGELOG.md index 91357401..199639a7 100644 --- a/plugins/flutter/CHANGELOG.md +++ b/plugins/flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.0+1 + ++ Reconstruction + ## 0.6.2+1 + Break change diff --git a/plugins/flutter/README.md b/plugins/flutter/README.md index 0b40fb3c..4f1c7fd9 100644 --- a/plugins/flutter/README.md +++ b/plugins/flutter/README.md @@ -36,7 +36,6 @@ The features marked with `YES` are supported, and the features marked with `NO`

This plugin provides object-oriented interfaces including full functionality of PocketPy [C-API](https://pocketpy.dev/c-api/vm). -It also provides `JsonRpcServer` class and `os` module bindings. Run the following script to install this plugin. diff --git a/plugins/flutter/example/lib/main.dart b/plugins/flutter/example/lib/main.dart index 66da3eeb..42a42a3e 100644 --- a/plugins/flutter/example/lib/main.dart +++ b/plugins/flutter/example/lib/main.dart @@ -50,8 +50,7 @@ class _MyAppState extends State { buffer.write(needMoreLines ? '... $text' : '>>> $text\n'); }); if (text == "exit()") exit(0); - repl.input(text); - needMoreLines = repl.last_input_result() == 0; + needMoreLines = repl.input(text) == 0; refresh(); } diff --git a/plugins/flutter/example/pubspec.lock b/plugins/flutter/example/pubspec.lock index 80c7aefc..6fa18319 100644 --- a/plugins/flutter/example/pubspec.lock +++ b/plugins/flutter/example/pubspec.lock @@ -108,7 +108,7 @@ packages: path: ".." relative: true source: path - version: "0.6.1+2" + version: "0.8.0+1" sky_engine: dependency: transitive description: flutter diff --git a/plugins/flutter/lib/jsonrpc.dart b/plugins/flutter/lib/jsonrpc.dart deleted file mode 100644 index 38e583fb..00000000 --- a/plugins/flutter/lib/jsonrpc.dart +++ /dev/null @@ -1,149 +0,0 @@ -// ignore_for_file: no_leading_underscores_for_local_identifiers, non_constant_identifier_names - -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'no_web.dart' if (dart.library.html) 'web.dart'; - -class _JsonRpcError { - final Map payload = {}; - - _JsonRpcError(int code, String message, {dynamic data}) { - payload['code'] = code; - payload['message'] = message; - if (data != null) { - payload['data'] = data; - } - } -} - -class JsonRpcServer { - final Map Function(List)> _methods = {}; - - final void Function()? onPreDispatch; - final void Function()? onPostDispatch; - final bool enableFileAccess; - - JsonRpcServer( - {this.onPreDispatch, - this.onPostDispatch, - this.enableFileAccess = false}) { - if (!enableFileAccess) return; - _registerOS(this); - } - - /// Register a JSONRPC handler. - void register(String name, FutureOr Function(List) method) { - _methods[name] = method; - } - - FutureOr _dispatch(ThreadedVM vm) { - if (vm.state != ThreadState.suspended) throw Exception("Unexpected state"); - String? json = vm.read_jsonrpc_request(); - if (json == null) throw Exception("JSONRPC request is null"); - var request = jsonDecode(json); - var f = _methods[request['method']]; - if (f == null) throw _JsonRpcError(-32601, "Method not found"); - try { - return f(request['params'] as List); - } catch (e) { - throw _JsonRpcError(-32000, e.toString()); - } - } - - /// Dispatch a JSONRPC request. - FutureOr dispatch(ThreadedVM vm) async { - onPreDispatch?.call(); - try { - dynamic ret = _dispatch(vm); - if (ret is Future) ret = await ret; - vm.write_jsonrpc_response(jsonEncode({"result": ret})); - onPostDispatch?.call(); - } on _JsonRpcError catch (e) { - vm.write_jsonrpc_response(jsonEncode({"error": e.payload})); - return; - } - } - - /// 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. - Future attach(ThreadedVM vm, - {Duration? spinFreq = const Duration(milliseconds: 20)}) async { - while (vm.state.index <= ThreadState.running.index) { - if (spinFreq != null) await Future.delayed(spinFreq); - } - switch (vm.state) { - case ThreadState.suspended: - await dispatch(vm); - await attach(vm, spinFreq: spinFreq); - break; - case ThreadState.finished: - break; - default: - throw Exception("Unexpected state"); - } - } - - int _fileId = 0; - final Map _files = {}; - - void _registerOS(JsonRpcServer rpcServer) { - rpcServer.register("fopen", (params) { - var path = params[0]; - //var mode = params[1]; - var fp = File(path); - _fileId += 1; - _files[_fileId] = fp; - return _fileId; - }); - - rpcServer.register("fclose", (params) { - var fp = _files[params[0]]; - if (fp == null) throw Exception("FileIO was closed"); - _files.remove(params[0]); - }); - - rpcServer.register("fread", (params) { - var fp = _files[params[0]]; - if (fp == null) throw Exception("FileIO was closed"); - return fp.readAsStringSync(); - }); - - rpcServer.register("fwrite", (params) { - var fp = _files[params[0]]; - if (fp == null) throw Exception("FileIO was closed"); - fp.writeAsStringSync(params[1]); - }); - - rpcServer.register("os.listdir", (params) { - String path = params[0]; - var entries = Directory(path).listSync(followLinks: false); - var ret = entries.map((e) { - return e.path.split(Platform.pathSeparator).last; - }).toList(); - return ret; - }); - - rpcServer.register("os.mkdir", (params) { - String path = params[0]; - Directory(path).createSync(); - }); - - rpcServer.register("os.rmdir", (params) { - String path = params[0]; - Directory(path).deleteSync(recursive: true); - }); - - rpcServer.register("os.remove", (params) { - String path = params[0]; - File(path).deleteSync(); - }); - - rpcServer.register("os.path.exists", (params) { - String path = params[0]; - bool _0 = Directory(path).existsSync(); - bool _1 = File(path).existsSync(); - return (_0 || _1); - }); - } -} diff --git a/plugins/flutter/lib/no_web.dart b/plugins/flutter/lib/no_web.dart index 247d1f20..1e848a99 100644 --- a/plugins/flutter/lib/no_web.dart +++ b/plugins/flutter/lib/no_web.dart @@ -6,8 +6,7 @@ import 'dart:io'; import 'package:ffi/ffi.dart'; import 'common.dart'; -class _Bindings -{ +class _Bindings { static ffi.DynamicLibrary _load() { String _libName = "pocketpy"; if (Platform.isIOS) { @@ -24,20 +23,98 @@ class _Bindings static final _lib = _load(); - static final pkpy_delete = _lib.lookupFunction("pkpy_delete"); - static final pkpy_new_repl = _lib.lookupFunction("pkpy_new_repl"); - static final pkpy_repl_input = _lib.lookupFunction line), int Function(ffi.Pointer r, ffi.Pointer line)>("pkpy_repl_input"); - static final pkpy_new_vm = _lib.lookupFunction("pkpy_new_vm"); - static final pkpy_vm_add_module = _lib.lookupFunction name, ffi.Pointer source), void Function(ffi.Pointer vm, ffi.Pointer name, ffi.Pointer source)>("pkpy_vm_add_module"); - static final pkpy_vm_eval = _lib.lookupFunction Function(ffi.Pointer vm, ffi.Pointer source), ffi.Pointer Function(ffi.Pointer vm, ffi.Pointer source)>("pkpy_vm_eval"); - static final pkpy_vm_exec = _lib.lookupFunction source), void Function(ffi.Pointer vm, ffi.Pointer source)>("pkpy_vm_exec"); - static final pkpy_vm_get_global = _lib.lookupFunction Function(ffi.Pointer vm, ffi.Pointer name), ffi.Pointer Function(ffi.Pointer vm, ffi.Pointer name)>("pkpy_vm_get_global"); - static final pkpy_vm_read_output = _lib.lookupFunction Function(ffi.Pointer vm), ffi.Pointer Function(ffi.Pointer vm)>("pkpy_vm_read_output"); + static final pkpy_delete = _lib.lookupFunction< + ffi.Void Function(ffi.Pointer p), + void Function(ffi.Pointer p)>("pkpy_delete"); + static final pkpy_new_repl = _lib.lookupFunction< + ffi.Pointer Function(ffi.Pointer vm), + ffi.Pointer Function(ffi.Pointer vm)>("pkpy_new_repl"); + static final pkpy_repl_input = _lib.lookupFunction< + ffi.Int32 Function(ffi.Pointer r, ffi.Pointer line), + int Function(ffi.Pointer r, ffi.Pointer line)>("pkpy_repl_input"); + static final pkpy_new_vm = _lib.lookupFunction< + ffi.Pointer Function(ffi.Bool use_stdio), + ffi.Pointer Function(bool use_stdio)>("pkpy_new_vm"); + static final pkpy_vm_add_module = _lib.lookupFunction< + ffi.Void Function( + ffi.Pointer vm, ffi.Pointer name, ffi.Pointer source), + void Function(ffi.Pointer vm, ffi.Pointer name, + ffi.Pointer source)>("pkpy_vm_add_module"); + static final pkpy_vm_eval = _lib.lookupFunction< + ffi.Pointer Function(ffi.Pointer vm, ffi.Pointer source), + ffi.Pointer Function( + ffi.Pointer vm, ffi.Pointer source)>("pkpy_vm_eval"); + static final pkpy_vm_exec = _lib.lookupFunction< + ffi.Void Function(ffi.Pointer vm, ffi.Pointer source), + void Function(ffi.Pointer vm, ffi.Pointer source)>("pkpy_vm_exec"); + static final pkpy_vm_get_global = _lib.lookupFunction< + ffi.Pointer Function(ffi.Pointer vm, ffi.Pointer name), + ffi.Pointer Function( + ffi.Pointer vm, ffi.Pointer name)>("pkpy_vm_get_global"); + static final pkpy_vm_read_output = _lib.lookupFunction< + ffi.Pointer Function(ffi.Pointer vm), + ffi.Pointer Function(ffi.Pointer vm)>("pkpy_vm_read_output"); } +class _Str { + static final Finalizer> finalizer = + Finalizer((p) => malloc.free(p)); + + late final ffi.Pointer _p; + _Str(String s) { + _p = s.toNativeUtf8(); + finalizer.attach(this, _p); + } + + ffi.Pointer get p => _p; +} + +class VM { + late final pointer = _Bindings.pkpy_new_vm(false); + + void dispose() { + _Bindings.pkpy_delete(pointer); + } + + PyOutput read_output() { + var _o = _Bindings.pkpy_vm_read_output(pointer); + String _j = _o.toDartString(); + var ret = PyOutput.fromJson(cvt.jsonDecode(_j)); + _Bindings.pkpy_delete(_o); + return ret; + } + + /// Add a source module into a virtual machine. + void add_module(String name, String source) { + _Bindings.pkpy_vm_add_module(pointer, _Str(name).p, _Str(source).p); + } + + /// Evaluate an expression. Return `__repr__` of the result. If there is any error, return `nullptr`. + String? eval(String source) { + var ret = _Bindings.pkpy_vm_eval(pointer, _Str(source).p); + if (ret == ffi.nullptr) return null; + String s = ret.toDartString(); + _Bindings.pkpy_delete(ret); + return s; + } + + /// Run a given source on a virtual machine. + void exec(String source) { + _Bindings.pkpy_vm_exec(pointer, _Str(source).p); + } + + /// Get a global variable of a virtual machine. Return `__repr__` of the result. If the variable is not found, return `nullptr`. + String? get_global(String name) { + var ret = _Bindings.pkpy_vm_get_global(pointer, _Str(name).p); + if (ret == ffi.nullptr) return null; + String s = ret.toDartString(); + _Bindings.pkpy_delete(ret); + return s; + } +} class REPL { - late final ffi.Pointer pointer; + late final dynamic pointer; REPL(VM vm) { pointer = _Bindings.pkpy_new_repl(vm.pointer); @@ -48,11 +125,8 @@ class REPL { } /// Input a source line to an interactive console. - int input(String line) - { + int input(String line) { var ret = _Bindings.pkpy_repl_input(pointer, _Str(line).p); return ret; } - } - diff --git a/plugins/flutter/lib/pocketpy.dart b/plugins/flutter/lib/pocketpy.dart index 84ed9c8f..9b52a027 100644 --- a/plugins/flutter/lib/pocketpy.dart +++ b/plugins/flutter/lib/pocketpy.dart @@ -1,5 +1,3 @@ library pocketpy; - -export 'jsonrpc.dart'; export 'common.dart'; export 'no_web.dart' if (dart.library.html) 'web.dart'; \ No newline at end of file diff --git a/plugins/flutter/lib/web.dart b/plugins/flutter/lib/web.dart index d1053eb4..d21464f6 100644 --- a/plugins/flutter/lib/web.dart +++ b/plugins/flutter/lib/web.dart @@ -4,19 +4,67 @@ import 'package:js/js.dart'; import 'common.dart'; @JS("Module.ccall") -external dynamic ccall(String name, String? returnType, List argTypes, List args); +external dynamic ccall( + String name, String? returnType, List argTypes, List args); -class _Bindings -{ - static final pkpy_delete = (dynamic p) => ccall("pkpy_delete", null, ["number"], [p]); - static final pkpy_new_repl = (dynamic vm) => ccall("pkpy_new_repl", "number", ["number"], [vm]); - static final pkpy_repl_input = (dynamic r, String line) => ccall("pkpy_repl_input", "number", ["number", "string"], [r, line]); - static final pkpy_new_vm = (bool use_stdio) => ccall("pkpy_new_vm", "number", ["boolean"], [use_stdio]); - static final pkpy_vm_add_module = (dynamic vm, String name, String source) => ccall("pkpy_vm_add_module", null, ["number", "string", "string"], [vm, name, source]); - static final pkpy_vm_eval = (dynamic vm, String source) => ccall("pkpy_vm_eval", "string", ["number", "string"], [vm, source]); - static final pkpy_vm_exec = (dynamic vm, String source) => ccall("pkpy_vm_exec", null, ["number", "string"], [vm, source]); - static final pkpy_vm_get_global = (dynamic vm, String name) => ccall("pkpy_vm_get_global", "string", ["number", "string"], [vm, name]); - static final pkpy_vm_read_output = (dynamic vm) => ccall("pkpy_vm_read_output", "string", ["number"], [vm]); +class _Bindings { + static final pkpy_delete = + (dynamic p) => ccall("pkpy_delete", null, ["number"], [p]); + static final pkpy_new_repl = + (dynamic vm) => ccall("pkpy_new_repl", "number", ["number"], [vm]); + static final pkpy_repl_input = (dynamic r, String line) => + ccall("pkpy_repl_input", "number", ["number", "string"], [r, line]); + static final pkpy_new_vm = (bool use_stdio) => + ccall("pkpy_new_vm", "number", ["boolean"], [use_stdio]); + static final pkpy_vm_add_module = (dynamic vm, String name, String source) => + ccall("pkpy_vm_add_module", null, ["number", "string", "string"], + [vm, name, source]); + static final pkpy_vm_eval = (dynamic vm, String source) => + ccall("pkpy_vm_eval", "string", ["number", "string"], [vm, source]); + static final pkpy_vm_exec = (dynamic vm, String source) => + ccall("pkpy_vm_exec", null, ["number", "string"], [vm, source]); + static final pkpy_vm_get_global = (dynamic vm, String name) => + ccall("pkpy_vm_get_global", "string", ["number", "string"], [vm, name]); + static final pkpy_vm_read_output = + (dynamic vm) => ccall("pkpy_vm_read_output", "string", ["number"], [vm]); +} + +class VM { + late final pointer = _Bindings.pkpy_new_vm(false); + + void dispose() { + _Bindings.pkpy_delete(pointer); + } + + PyOutput read_output() { + var _o = _Bindings.pkpy_vm_read_output(pointer); + String _j = _o; + var ret = PyOutput.fromJson(cvt.jsonDecode(_j)); + + return ret; + } + + /// Add a source module into a virtual machine. + void add_module(String name, String source) { + _Bindings.pkpy_vm_add_module(pointer, name, source); + } + + /// Evaluate an expression. Return `__repr__` of the result. If there is any error, return `nullptr`. + String? eval(String source) { + var ret = _Bindings.pkpy_vm_eval(pointer, source); + return ret; + } + + /// Run a given source on a virtual machine. + void exec(String source) { + _Bindings.pkpy_vm_exec(pointer, source); + } + + /// Get a global variable of a virtual machine. Return `__repr__` of the result. If the variable is not found, return `nullptr`. + String? get_global(String name) { + var ret = _Bindings.pkpy_vm_get_global(pointer, name); + return ret; + } } class REPL { @@ -31,11 +79,8 @@ class REPL { } /// Input a source line to an interactive console. - int input(String line) - { + int input(String line) { var ret = _Bindings.pkpy_repl_input(pointer, line); return ret; } - } - diff --git a/plugins/flutter/pubspec.yaml b/plugins/flutter/pubspec.yaml index d271c4b2..7ce2bb51 100644 --- a/plugins/flutter/pubspec.yaml +++ b/plugins/flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: pocketpy description: A lightweight Python interpreter for game engines. -version: 0.6.2+1 +version: 0.8.0+1 homepage: https://pocketpy.dev repository: https://github.com/blueloveth/pocketpy