mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-22 20:40:18 +00:00
up
This commit is contained in:
parent
b63b1a69dc
commit
91b314d7f2
@ -1,3 +1,7 @@
|
||||
## 0.8.0+1
|
||||
|
||||
+ Reconstruction
|
||||
|
||||
## 0.6.2+1
|
||||
|
||||
+ Break change
|
||||
|
@ -36,7 +36,6 @@ The features marked with `YES` are supported, and the features marked with `NO`
|
||||
</p>
|
||||
|
||||
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.
|
||||
|
||||
|
@ -50,8 +50,7 @@ class _MyAppState extends State<MyApp> {
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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<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<Utf8> line), 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");
|
||||
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<Utf8> line),
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
library pocketpy;
|
||||
|
||||
export 'jsonrpc.dart';
|
||||
export 'common.dart';
|
||||
export 'no_web.dart' if (dart.library.html) '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<String> argTypes, List<dynamic> args);
|
||||
external dynamic ccall(
|
||||
String name, String? returnType, List<String> argTypes, List<dynamic> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user