This commit is contained in:
blueloveTH 2023-02-02 16:58:01 +08:00
parent b63b1a69dc
commit 91b314d7f2
9 changed files with 158 additions and 188 deletions

View File

@ -1,3 +1,7 @@
## 0.8.0+1
+ Reconstruction
## 0.6.2+1 ## 0.6.2+1
+ Break change + Break change

View File

@ -36,7 +36,6 @@ The features marked with `YES` are supported, and the features marked with `NO`
</p> </p>
This plugin provides object-oriented interfaces including full functionality of PocketPy [C-API](https://pocketpy.dev/c-api/vm). 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. Run the following script to install this plugin.

View File

@ -50,8 +50,7 @@ class _MyAppState extends State<MyApp> {
buffer.write(needMoreLines ? '... $text' : '>>> $text\n'); buffer.write(needMoreLines ? '... $text' : '>>> $text\n');
}); });
if (text == "exit()") exit(0); if (text == "exit()") exit(0);
repl.input(text); needMoreLines = repl.input(text) == 0;
needMoreLines = repl.last_input_result() == 0;
refresh(); refresh();
} }

View File

@ -108,7 +108,7 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "0.6.1+2" version: "0.8.0+1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View File

@ -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<String, dynamic> payload = {};
_JsonRpcError(int code, String message, {dynamic data}) {
payload['code'] = code;
payload['message'] = message;
if (data != null) {
payload['data'] = data;
}
}
}
class JsonRpcServer {
final Map<String, FutureOr<dynamic> 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<dynamic> Function(List) method) {
_methods[name] = method;
}
FutureOr<dynamic> _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<void> dispatch(ThreadedVM vm) async {
onPreDispatch?.call();
try {
dynamic ret = _dispatch(vm);
if (ret is Future<dynamic>) 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<void> 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<int, File> _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);
});
}
}

View File

@ -6,8 +6,7 @@ import 'dart:io';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'common.dart'; import 'common.dart';
class _Bindings class _Bindings {
{
static ffi.DynamicLibrary _load() { static ffi.DynamicLibrary _load() {
String _libName = "pocketpy"; String _libName = "pocketpy";
if (Platform.isIOS) { if (Platform.isIOS) {
@ -24,20 +23,98 @@ class _Bindings
static final _lib = _load(); static final _lib = _load();
static final pkpy_delete = _lib.lookupFunction<ffi.Void Function(ffi.Pointer p), void Function(ffi.Pointer p)>("pkpy_delete"); static final pkpy_delete = _lib.lookupFunction<
static final pkpy_new_repl = _lib.lookupFunction<ffi.Pointer Function(ffi.Pointer vm), ffi.Pointer Function(ffi.Pointer vm)>("pkpy_new_repl"); ffi.Void Function(ffi.Pointer p),
static final pkpy_repl_input = _lib.lookupFunction<ffi.Int32 Function(ffi.Pointer r, ffi.Pointer<Utf8> line), int Function(ffi.Pointer r, ffi.Pointer<Utf8> line)>("pkpy_repl_input"); void Function(ffi.Pointer p)>("pkpy_delete");
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_new_repl = _lib.lookupFunction<
static final pkpy_vm_add_module = _lib.lookupFunction<ffi.Void Function(ffi.Pointer vm, ffi.Pointer<Utf8> name, ffi.Pointer<Utf8> source), void Function(ffi.Pointer vm, ffi.Pointer<Utf8> name, ffi.Pointer<Utf8> source)>("pkpy_vm_add_module"); ffi.Pointer Function(ffi.Pointer vm),
static final pkpy_vm_eval = _lib.lookupFunction<ffi.Pointer<Utf8> Function(ffi.Pointer vm, ffi.Pointer<Utf8> source), ffi.Pointer<Utf8> Function(ffi.Pointer vm, ffi.Pointer<Utf8> source)>("pkpy_vm_eval"); ffi.Pointer Function(ffi.Pointer vm)>("pkpy_new_repl");
static final pkpy_vm_exec = _lib.lookupFunction<ffi.Void Function(ffi.Pointer vm, ffi.Pointer<Utf8> source), void Function(ffi.Pointer vm, ffi.Pointer<Utf8> source)>("pkpy_vm_exec"); static final pkpy_repl_input = _lib.lookupFunction<
static final pkpy_vm_get_global = _lib.lookupFunction<ffi.Pointer<Utf8> Function(ffi.Pointer vm, ffi.Pointer<Utf8> name), ffi.Pointer<Utf8> Function(ffi.Pointer vm, ffi.Pointer<Utf8> name)>("pkpy_vm_get_global"); ffi.Int32 Function(ffi.Pointer r, ffi.Pointer<Utf8> line),
static final pkpy_vm_read_output = _lib.lookupFunction<ffi.Pointer<Utf8> Function(ffi.Pointer vm), ffi.Pointer<Utf8> Function(ffi.Pointer vm)>("pkpy_vm_read_output"); int Function(ffi.Pointer r, ffi.Pointer<Utf8> 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<Utf8> name, ffi.Pointer<Utf8> source),
void Function(ffi.Pointer vm, ffi.Pointer<Utf8> name,
ffi.Pointer<Utf8> source)>("pkpy_vm_add_module");
static final pkpy_vm_eval = _lib.lookupFunction<
ffi.Pointer<Utf8> Function(ffi.Pointer vm, ffi.Pointer<Utf8> source),
ffi.Pointer<Utf8> Function(
ffi.Pointer vm, ffi.Pointer<Utf8> source)>("pkpy_vm_eval");
static final pkpy_vm_exec = _lib.lookupFunction<
ffi.Void Function(ffi.Pointer vm, ffi.Pointer<Utf8> source),
void Function(ffi.Pointer vm, ffi.Pointer<Utf8> source)>("pkpy_vm_exec");
static final pkpy_vm_get_global = _lib.lookupFunction<
ffi.Pointer<Utf8> Function(ffi.Pointer vm, ffi.Pointer<Utf8> name),
ffi.Pointer<Utf8> Function(
ffi.Pointer vm, ffi.Pointer<Utf8> name)>("pkpy_vm_get_global");
static final pkpy_vm_read_output = _lib.lookupFunction<
ffi.Pointer<Utf8> Function(ffi.Pointer vm),
ffi.Pointer<Utf8> Function(ffi.Pointer vm)>("pkpy_vm_read_output");
} }
class _Str {
static final Finalizer<ffi.Pointer<Utf8>> finalizer =
Finalizer((p) => malloc.free(p));
late final ffi.Pointer<Utf8> _p;
_Str(String s) {
_p = s.toNativeUtf8();
finalizer.attach(this, _p);
}
ffi.Pointer<Utf8> 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 { class REPL {
late final ffi.Pointer pointer; late final dynamic pointer;
REPL(VM vm) { REPL(VM vm) {
pointer = _Bindings.pkpy_new_repl(vm.pointer); pointer = _Bindings.pkpy_new_repl(vm.pointer);
@ -48,11 +125,8 @@ class REPL {
} }
/// Input a source line to an interactive console. /// 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); var ret = _Bindings.pkpy_repl_input(pointer, _Str(line).p);
return ret; return ret;
} }
} }

View File

@ -1,5 +1,3 @@
library pocketpy; library pocketpy;
export 'jsonrpc.dart';
export 'common.dart'; export 'common.dart';
export 'no_web.dart' if (dart.library.html) 'web.dart'; export 'no_web.dart' if (dart.library.html) 'web.dart';

View File

@ -4,19 +4,67 @@ import 'package:js/js.dart';
import 'common.dart'; import 'common.dart';
@JS("Module.ccall") @JS("Module.ccall")
external dynamic ccall(String name, String? returnType, List<String> argTypes, List<dynamic> args); external dynamic ccall(
String name, String? returnType, List<String> argTypes, List<dynamic> args);
class _Bindings class _Bindings {
{ static final pkpy_delete =
static final pkpy_delete = (dynamic p) => ccall("pkpy_delete", null, ["number"], [p]); (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_new_repl =
static final pkpy_repl_input = (dynamic r, String line) => ccall("pkpy_repl_input", "number", ["number", "string"], [r, line]); (dynamic vm) => ccall("pkpy_new_repl", "number", ["number"], [vm]);
static final pkpy_new_vm = (bool use_stdio) => ccall("pkpy_new_vm", "number", ["boolean"], [use_stdio]); static final pkpy_repl_input = (dynamic r, String line) =>
static final pkpy_vm_add_module = (dynamic vm, String name, String source) => ccall("pkpy_vm_add_module", null, ["number", "string", "string"], [vm, name, source]); ccall("pkpy_repl_input", "number", ["number", "string"], [r, line]);
static final pkpy_vm_eval = (dynamic vm, String source) => ccall("pkpy_vm_eval", "string", ["number", "string"], [vm, source]); static final pkpy_new_vm = (bool use_stdio) =>
static final pkpy_vm_exec = (dynamic vm, String source) => ccall("pkpy_vm_exec", null, ["number", "string"], [vm, source]); ccall("pkpy_new_vm", "number", ["boolean"], [use_stdio]);
static final pkpy_vm_get_global = (dynamic vm, String name) => ccall("pkpy_vm_get_global", "string", ["number", "string"], [vm, name]); static final pkpy_vm_add_module = (dynamic vm, String name, String source) =>
static final pkpy_vm_read_output = (dynamic vm) => ccall("pkpy_vm_read_output", "string", ["number"], [vm]); 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 { class REPL {
@ -31,11 +79,8 @@ class REPL {
} }
/// Input a source line to an interactive console. /// Input a source line to an interactive console.
int input(String line) int input(String line) {
{
var ret = _Bindings.pkpy_repl_input(pointer, line); var ret = _Bindings.pkpy_repl_input(pointer, line);
return ret; return ret;
} }
} }

View File

@ -1,6 +1,6 @@
name: pocketpy name: pocketpy
description: A lightweight Python interpreter for game engines. description: A lightweight Python interpreter for game engines.
version: 0.6.2+1 version: 0.8.0+1
homepage: https://pocketpy.dev homepage: https://pocketpy.dev
repository: https://github.com/blueloveth/pocketpy repository: https://github.com/blueloveth/pocketpy