From 9b9a78524e21d4490cc4c4d194b83879e614ac45 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Fri, 28 Apr 2023 23:53:23 -0600 Subject: [PATCH 01/26] made initial structure for new way of handling c bindings --- build_c.sh | 12 ++++++++++++ c_bindings/main.c | 13 +++++++++++++ c_bindings/pocketpy_c.cpp | 21 +++++++++++++++++++++ c_bindings/pocketpy_c.h | 20 ++++++++++++++++++++ src/pocketpy.h | 4 +++- 5 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 build_c.sh create mode 100644 c_bindings/main.c create mode 100644 c_bindings/pocketpy_c.cpp create mode 100644 c_bindings/pocketpy_c.h diff --git a/build_c.sh b/build_c.sh new file mode 100644 index 00000000..6d7355d3 --- /dev/null +++ b/build_c.sh @@ -0,0 +1,12 @@ +python3 preprocess.py + +echo "compiling c++ lib" +clang++ -c -o pocketpy_c.o c_bindings/pocketpy_c.cpp -Wfatal-errors --std=c++17 -O2 -Wall -Wno-sign-compare -Wno-unused-variable -fno-rtti -stdlib=libc++ -I src/ +echo "compiling c executable" +clang -c -o main.o c_bindings/main.c -Wfatal-errors -O2 -Wall -Wno-sign-compare -Wno-unused-variable -I src/ +echo "linking" +clang++ -o pocketpy_c main.o pocketpy_c.o -stdlib=libc++ +echo "cleaning up" +rm pocketpy_c.o +rm main.o + diff --git a/c_bindings/main.c b/c_bindings/main.c new file mode 100644 index 00000000..04290ebf --- /dev/null +++ b/c_bindings/main.c @@ -0,0 +1,13 @@ +#include "pocketpy_c.h" + + +int main(int argc, char** argv) { + + pkpy_vm vm = pkpy_vm_create(true, true); + + pkpy_vm_exec(vm, "print('hello world!')"); + + pkpy_vm_destroy(vm); + + return 0; +} diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp new file mode 100644 index 00000000..152ea532 --- /dev/null +++ b/c_bindings/pocketpy_c.cpp @@ -0,0 +1,21 @@ +#include "pocketpy.h" +#include "pocketpy_c.h" + +pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os) { + pkpy::VM* p = new pkpy::VM(use_stdio, enable_os); + + return (pkpy_vm) p; +} + +void pkpy_vm_exec(pkpy_vm vm_handle, const char* source) { + pkpy::VM* vm = (pkpy::VM*) vm_handle; + + vm->exec(source, "main.py", pkpy::EXEC_MODE); +} + +void pkpy_vm_destroy(pkpy_vm vm_handle) { + pkpy::VM* vm = (pkpy::VM*) vm_handle; + + delete vm; +} + diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h new file mode 100644 index 00000000..12672495 --- /dev/null +++ b/c_bindings/pocketpy_c.h @@ -0,0 +1,20 @@ +#ifndef POCKETPY_C_H +#define POCKETPY_C_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct pkpy_vm_handle* pkpy_vm; + +pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os); +void pkpy_vm_exec(pkpy_vm vm_handle, const char* source); +void pkpy_vm_destroy(pkpy_vm vm); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/pocketpy.h b/src/pocketpy.h index 2501c396..db49cbfa 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -975,6 +975,7 @@ inline void VM::post_init(){ } // namespace pkpy /*************************GLOBAL NAMESPACE*************************/ +/* static std::map _pk_deleter_map; extern "C" { @@ -1046,4 +1047,5 @@ extern "C" { std::string json = vm->read_output(); return strdup(json.c_str()); } -} \ No newline at end of file + +}*/ From 6bdc8fd4947a0a6c6937f4a00ae1d73c43b74d5e Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Sat, 29 Apr 2023 13:33:51 -0600 Subject: [PATCH 02/26] basic stack based api for creating a binding from c to python is functional --- c_bindings/main.c | 13 +++++++++++++ c_bindings/pocketpy_c.cpp | 24 ++++++++++++++++++++++++ c_bindings/pocketpy_c.h | 14 ++++++++++++++ src/obj.h | 12 +++++++++++- src/vm.h | 21 +++++++++++++++++++-- 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/c_bindings/main.c b/c_bindings/main.c index 04290ebf..f3592583 100644 --- a/c_bindings/main.c +++ b/c_bindings/main.c @@ -1,12 +1,25 @@ #include "pocketpy_c.h" +void test_binding(pkpy_vm vm) { + pkpy_push_int(vm, 12); +} + int main(int argc, char** argv) { pkpy_vm vm = pkpy_vm_create(true, true); pkpy_vm_exec(vm, "print('hello world!')"); + pkpy_push_int(vm, 11); + pkpy_set_global(vm, "eleven"); + + pkpy_push_cfunction(vm, test_binding); + pkpy_set_global(vm, "binding"); + + pkpy_vm_exec(vm, "print(eleven)"); + pkpy_vm_exec(vm, "print(binding())"); + pkpy_vm_destroy(vm); return 0; diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 152ea532..070dfaca 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -19,3 +19,27 @@ void pkpy_vm_destroy(pkpy_vm vm_handle) { delete vm; } +void pkpy_push_cfunction(pkpy_vm vm_handle, pkpy_cfunction f) { + pkpy::VM* vm = (pkpy::VM*) vm_handle; + vm->s_data.push(pkpy::py_var(vm, pkpy::StackFunc((pkpy::StackFuncC) f))); +} + +void pkpy_push_int(pkpy_vm vm_handle, int64_t value) { + pkpy::VM* vm = (pkpy::VM*) vm_handle; + vm->s_data.push(VAR(value)); +} + +void pkpy_push_float(pkpy_vm vm_handle, double value) { + pkpy::VM* vm = (pkpy::VM*) vm_handle; + vm->s_data.push(VAR(value)); +} + +void pkpy_set_global(pkpy_vm vm_handle, const char* name) { + pkpy::VM* vm = (pkpy::VM*) vm_handle; + + vm->_main->attr().set(name, vm->s_data.top()); + + vm->s_data.pop(); +} + + diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index 12672495..80f153cc 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -6,6 +6,7 @@ extern "C" { #endif #include +#include typedef struct pkpy_vm_handle* pkpy_vm; @@ -13,6 +14,19 @@ pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os); void pkpy_vm_exec(pkpy_vm vm_handle, const char* source); void pkpy_vm_destroy(pkpy_vm vm); +////////binding a c function to pocketpy +typedef void (*pkpy_cfunction)(pkpy_vm); + +void pkpy_push_cfunction(pkpy_vm, pkpy_cfunction); +void pkpy_push_int(pkpy_vm, int64_t); +void pkpy_push_float(pkpy_vm, double); + +void pkpy_set_global(pkpy_vm, const char* name); + + + + + #ifdef __cplusplus } #endif diff --git a/src/obj.h b/src/obj.h index 515fcf73..b817c9ee 100644 --- a/src/obj.h +++ b/src/obj.h @@ -23,6 +23,16 @@ struct NativeFunc { PyObject* operator()(VM* vm, ArgsView args) const; }; + +typedef void (*StackFuncC)(VM*); +struct StackFunc { + StackFuncC f; + + StackFunc(StackFuncC f) : f(f) {} + void operator()(VM* vm) const; +}; + + struct FuncDecl { struct KwArg { int key; // index in co->varnames @@ -259,4 +269,4 @@ __T _py_cast(VM* vm, PyObject* obj) { #define CAST(T, x) py_cast(vm, x) #define _CAST(T, x) _py_cast(vm, x) -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/vm.h b/src/vm.h index b360d941..9ca31153 100644 --- a/src/vm.h +++ b/src/vm.h @@ -100,7 +100,7 @@ public: // for quick access Type tp_object, tp_type, tp_int, tp_float, tp_bool, tp_str; Type tp_list, tp_tuple; - Type tp_function, tp_native_func, tp_iterator, tp_bound_method; + Type tp_function, tp_stack_func, tp_native_func, tp_iterator, tp_bound_method; Type tp_slice, tp_range, tp_module; Type tp_super, tp_exception, tp_bytes, tp_mappingproxy; @@ -414,6 +414,11 @@ inline PyObject* NativeFunc::operator()(VM* vm, ArgsView args) const{ return f(vm, args); } +inline void StackFunc::operator()(VM* vm) const{ + return f(vm); +} + + inline void CodeObject::optimize(VM* vm){ // uint32_t base_n = (uint32_t)(names.size() / kLocalsLoadFactor + 0.5); // perfect_locals_capacity = std::max(find_next_capacity(base_n), NameDict::__Capacity); @@ -425,6 +430,7 @@ DEF_NATIVE_2(List, tp_list) DEF_NATIVE_2(Tuple, tp_tuple) DEF_NATIVE_2(Function, tp_function) DEF_NATIVE_2(NativeFunc, tp_native_func) +DEF_NATIVE_2(StackFunc, tp_stack_func) DEF_NATIVE_2(BoundMethod, tp_bound_method) DEF_NATIVE_2(Range, tp_range) DEF_NATIVE_2(Slice, tp_slice) @@ -927,6 +933,17 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ // [unbound, self, args..., kwargs...] } + if(is_non_tagged_type(callable, tp_stack_func)) { + const auto& f = OBJ_GET(StackFunc, callable); + if(ARGC != 0) TypeError("stack_func does not track argument counts "); + if(KWARGC != 0) TypeError("stack_func does not accept keyword arguments"); + f(this); + PyObject* ret = s_data.top(); + s_data.reset(p0); + return ret; + } + + ArgsView args(p1 - ARGC - int(method_call), p1); if(is_non_tagged_type(callable, tp_native_func)){ @@ -1201,4 +1218,4 @@ inline Str obj_type_name(VM *vm, Type type){ #undef PY_VAR_INT #undef PY_VAR_FLOAT -} // namespace pkpy \ No newline at end of file +} // namespace pkpy From f701cc1e67dbf1138dc1fa2b01653d6acc1ab1e0 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Sat, 29 Apr 2023 17:49:26 -0600 Subject: [PATCH 03/26] basic proof of concept working --- build_c.sh | 8 ++++---- c_bindings/main.c | 12 +++++++++++- c_bindings/pocketpy_c.cpp | 28 +++++++++++++++++++++++++++- c_bindings/pocketpy_c.h | 4 ++++ src/frame.h | 2 +- src/obj.h | 9 +-------- src/vm.h | 6 +----- 7 files changed, 49 insertions(+), 20 deletions(-) diff --git a/build_c.sh b/build_c.sh index 6d7355d3..54d82df6 100644 --- a/build_c.sh +++ b/build_c.sh @@ -1,11 +1,11 @@ python3 preprocess.py echo "compiling c++ lib" -clang++ -c -o pocketpy_c.o c_bindings/pocketpy_c.cpp -Wfatal-errors --std=c++17 -O2 -Wall -Wno-sign-compare -Wno-unused-variable -fno-rtti -stdlib=libc++ -I src/ -echo "compiling c executable" -clang -c -o main.o c_bindings/main.c -Wfatal-errors -O2 -Wall -Wno-sign-compare -Wno-unused-variable -I src/ +clang++ -c -o pocketpy_c.o c_bindings/pocketpy_c.cpp -Wfatal-errors --std=c++17 -O2 -Wall -Wno-sign-compare -Wno-unused-variable -fno-rtti -stdlib=libc++ -I src/ -fsanitize=address -g +echo "compiling c executable" +clang -c -o main.o c_bindings/main.c -Wfatal-errors -O2 -Wall -Wno-sign-compare -Wno-unused-variable -I src/ -fsanitize=address -g echo "linking" -clang++ -o pocketpy_c main.o pocketpy_c.o -stdlib=libc++ +clang++ -o pocketpy_c main.o pocketpy_c.o -stdlib=libc++ -fsanitize=address -g echo "cleaning up" rm pocketpy_c.o rm main.o diff --git a/c_bindings/main.c b/c_bindings/main.c index f3592583..ff18459a 100644 --- a/c_bindings/main.c +++ b/c_bindings/main.c @@ -1,4 +1,5 @@ #include "pocketpy_c.h" +#include void test_binding(pkpy_vm vm) { @@ -20,7 +21,16 @@ int main(int argc, char** argv) { pkpy_vm_exec(vm, "print(eleven)"); pkpy_vm_exec(vm, "print(binding())"); - pkpy_vm_destroy(vm); + pkpy_vm_exec(vm, "x = lambda x : x + 1"); + pkpy_get_global(vm, "x"); + pkpy_push_null(vm); + pkpy_push_int(vm, 1); + pkpy_call(vm, 1); + + int64_t r = pkpy_toint(vm, 1); + printf("%li\n", r); + + pkpy_vm_destroy(vm); return 0; } diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 070dfaca..9e0ec369 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -21,7 +21,7 @@ void pkpy_vm_destroy(pkpy_vm vm_handle) { void pkpy_push_cfunction(pkpy_vm vm_handle, pkpy_cfunction f) { pkpy::VM* vm = (pkpy::VM*) vm_handle; - vm->s_data.push(pkpy::py_var(vm, pkpy::StackFunc((pkpy::StackFuncC) f))); + vm->s_data.push(VAR((pkpy::StackFunc) f)); } void pkpy_push_int(pkpy_vm vm_handle, int64_t value) { @@ -29,6 +29,12 @@ void pkpy_push_int(pkpy_vm vm_handle, int64_t value) { vm->s_data.push(VAR(value)); } +void pkpy_push_null(pkpy_vm vm_handle) { + pkpy::VM* vm = (pkpy::VM*) vm_handle; + vm->s_data.push(pkpy::PY_NULL); +} + + void pkpy_push_float(pkpy_vm vm_handle, double value) { pkpy::VM* vm = (pkpy::VM*) vm_handle; vm->s_data.push(VAR(value)); @@ -42,4 +48,24 @@ void pkpy_set_global(pkpy_vm vm_handle, const char* name) { vm->s_data.pop(); } +void pkpy_get_global(pkpy_vm vm_handle, const char* name) { + pkpy::VM* vm = (pkpy::VM*) vm_handle; + + pkpy::PyObject* o = vm->_main->attr().try_get(name); + + vm->s_data.push(o); +} + +void pkpy_call(pkpy_vm vm_handle, int argc) { + pkpy::VM* vm = (pkpy::VM*) vm_handle; + pkpy::PyObject* o = vm->vectorcall(argc, 0, 0); + vm->s_data.push(o); +} + +int pkpy_toint(pkpy_vm vm_handle, int index) { + pkpy::VM* vm = (pkpy::VM*) vm_handle; + + pkpy::PyObject* o = vm->s_data.peek(index); + return pkpy::py_cast(vm, o); +} diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index 80f153cc..1bb96397 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -20,9 +20,13 @@ typedef void (*pkpy_cfunction)(pkpy_vm); void pkpy_push_cfunction(pkpy_vm, pkpy_cfunction); void pkpy_push_int(pkpy_vm, int64_t); void pkpy_push_float(pkpy_vm, double); +void pkpy_push_null(pkpy_vm); void pkpy_set_global(pkpy_vm, const char* name); +void pkpy_get_global(pkpy_vm vm_handle, const char* name); +void pkpy_call(pkpy_vm vm_handle, int argc); +int pkpy_toint(pkpy_vm vm_handle, int index); diff --git a/src/frame.h b/src/frame.h index 150e75c4..552fbfa5 100644 --- a/src/frame.h +++ b/src/frame.h @@ -188,4 +188,4 @@ struct Frame { } }; -}; // namespace pkpy \ No newline at end of file +}; // namespace pkpy diff --git a/src/obj.h b/src/obj.h index fc787db5..78c4c631 100644 --- a/src/obj.h +++ b/src/obj.h @@ -24,14 +24,7 @@ struct NativeFunc { }; -typedef void (*StackFuncC)(VM*); -struct StackFunc { - StackFuncC f; - - StackFunc(StackFuncC f) : f(f) {} - void operator()(VM* vm) const; -}; - +typedef void (*StackFunc)(VM*); struct FuncDecl { struct KwArg { diff --git a/src/vm.h b/src/vm.h index a6612a47..dcbf4222 100644 --- a/src/vm.h +++ b/src/vm.h @@ -414,11 +414,6 @@ inline PyObject* NativeFunc::operator()(VM* vm, ArgsView args) const{ return f(vm, args); } -inline void StackFunc::operator()(VM* vm) const{ - return f(vm); -} - - inline void CodeObject::optimize(VM* vm){ // uint32_t base_n = (uint32_t)(names.size() / kLocalsLoadFactor + 0.5); // perfect_locals_capacity = std::max(find_next_capacity(base_n), NameDict::__Capacity); @@ -866,6 +861,7 @@ inline void VM::init_builtin_types(){ tp_module = _new_type_object("module"); tp_function = _new_type_object("function"); tp_native_func = _new_type_object("native_func"); + tp_stack_func = _new_type_object("stack_func"); tp_iterator = _new_type_object("iterator"); tp_bound_method = _new_type_object("bound_method"); tp_super = _new_type_object("super"); From 1fc08dae628836e0e672c906e3c748edb158aca0 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Sun, 30 Apr 2023 08:51:21 -0600 Subject: [PATCH 04/26] got rid of stack function --- src/vm.h | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/vm.h b/src/vm.h index 8f4969c1..ac50942a 100644 --- a/src/vm.h +++ b/src/vm.h @@ -102,7 +102,7 @@ public: // for quick access Type tp_object, tp_type, tp_int, tp_float, tp_bool, tp_str; Type tp_list, tp_tuple; - Type tp_function, tp_stack_func, tp_native_func, tp_iterator, tp_bound_method; + Type tp_function, tp_native_func, tp_iterator, tp_bound_method; Type tp_slice, tp_range, tp_module; Type tp_super, tp_exception, tp_bytes, tp_mappingproxy; @@ -427,7 +427,6 @@ DEF_NATIVE_2(List, tp_list) DEF_NATIVE_2(Tuple, tp_tuple) DEF_NATIVE_2(Function, tp_function) DEF_NATIVE_2(NativeFunc, tp_native_func) -DEF_NATIVE_2(StackFunc, tp_stack_func) DEF_NATIVE_2(BoundMethod, tp_bound_method) DEF_NATIVE_2(Range, tp_range) DEF_NATIVE_2(Slice, tp_slice) @@ -863,7 +862,6 @@ inline void VM::init_builtin_types(){ tp_module = _new_type_object("module"); tp_function = _new_type_object("function"); tp_native_func = _new_type_object("native_func"); - tp_stack_func = _new_type_object("stack_func"); tp_iterator = _new_type_object("iterator"); tp_bound_method = _new_type_object("bound_method"); tp_super = _new_type_object("super"); @@ -931,17 +929,6 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ // [unbound, self, args..., kwargs...] } - if(is_non_tagged_type(callable, tp_stack_func)) { - const auto& f = OBJ_GET(StackFunc, callable); - if(ARGC != 0) TypeError("stack_func does not track argument counts "); - if(KWARGC != 0) TypeError("stack_func does not accept keyword arguments"); - f(this); - PyObject* ret = s_data.top(); - s_data.reset(p0); - return ret; - } - - ArgsView args(p1 - ARGC - int(method_call), p1); if(is_non_tagged_type(callable, tp_native_func)){ From 7882d18df66dcc537f0bec6dead3a92b8e61c6fa Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Sun, 30 Apr 2023 09:03:32 -0600 Subject: [PATCH 05/26] added separate stack for c interop --- c_bindings/pocketpy_c.h | 2 +- src/frame.h | 32 ++++++++++++++++++++++++++++++++ src/vm.h | 1 + 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index 1bb96397..f800548f 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -15,7 +15,7 @@ void pkpy_vm_exec(pkpy_vm vm_handle, const char* source); void pkpy_vm_destroy(pkpy_vm vm); ////////binding a c function to pocketpy -typedef void (*pkpy_cfunction)(pkpy_vm); +typedef int (*pkpy_cfunction)(pkpy_vm); void pkpy_push_cfunction(pkpy_vm, pkpy_cfunction); void pkpy_push_int(pkpy_vm, int64_t); diff --git a/src/frame.h b/src/frame.h index 552fbfa5..91ee5bda 100644 --- a/src/frame.h +++ b/src/frame.h @@ -98,6 +98,38 @@ struct ValueStack { ValueStack& operator=(ValueStack&&) = delete; }; +//stack for working with c bindings +struct CVirtualStack { + static const size_t MAX_SIZE = 256; + // We allocate 512 more bytes to keep `_sp` valid when `is_overflow() == true`. + PyObject* _begin[MAX_SIZE]; + PyObject** _sp; + + CVirtualStack(): _sp(_begin) {} + + PyObject* top() const { return _sp[-1]; } + PyObject* peek(int n) const { return _sp[-n]; } + void push(PyObject* v){ *_sp++ = v; } + void pop(){ --_sp; } + void shrink(int n){ _sp -= n; } + int size() const { return _sp - _begin; } + bool empty() const { return _sp == _begin; } + PyObject** begin() { return _begin; } + PyObject** end() { return _sp; } + void reset(PyObject** sp) { +#if DEBUG_EXTRA_CHECK + if(sp < _begin || sp > _begin + MAX_SIZE) FATAL_ERROR(); +#endif + _sp = sp; + } + void clear() { _sp = _begin; } + + ValueStack(const ValueStack&) = delete; + ValueStack(ValueStack&&) = delete; + ValueStack& operator=(const ValueStack&) = delete; + ValueStack& operator=(ValueStack&&) = delete; +}; + struct Frame { int _ip = -1; int _next_ip = 0; diff --git a/src/vm.h b/src/vm.h index ac50942a..97951a18 100644 --- a/src/vm.h +++ b/src/vm.h @@ -78,6 +78,7 @@ class VM { public: ManagedHeap heap; ValueStack s_data; + CVirtualStack c_data; stack< Frame > callstack; std::vector _all_types; From aea01c5acaf02899321548f5b6936a73121b43b8 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Sun, 30 Apr 2023 11:30:45 -0600 Subject: [PATCH 06/26] function calls are working --- c_bindings/main.c | 16 ++-- c_bindings/pocketpy_c.cpp | 181 ++++++++++++++++++++++++++++++-------- c_bindings/pocketpy_c.h | 51 ++++++++--- src/error.h | 2 +- src/frame.h | 17 ++-- src/main.cpp | 12 +-- src/str.h | 2 +- 7 files changed, 208 insertions(+), 73 deletions(-) diff --git a/c_bindings/main.c b/c_bindings/main.c index ff18459a..65670880 100644 --- a/c_bindings/main.c +++ b/c_bindings/main.c @@ -1,6 +1,8 @@ #include "pocketpy_c.h" #include +//tests the c bindings for pocketpy + void test_binding(pkpy_vm vm) { pkpy_push_int(vm, 12); @@ -15,22 +17,24 @@ int main(int argc, char** argv) { pkpy_push_int(vm, 11); pkpy_set_global(vm, "eleven"); - pkpy_push_cfunction(vm, test_binding); - pkpy_set_global(vm, "binding"); + //pkpy_push_cfunction(vm, test_binding); + //pkpy_set_global(vm, "binding"); pkpy_vm_exec(vm, "print(eleven)"); - pkpy_vm_exec(vm, "print(binding())"); + //pkpy_vm_exec(vm, "print(binding())"); - pkpy_vm_exec(vm, "x = lambda x : x + 1"); + pkpy_vm_exec(vm, "def x(x) : return x + 1"); pkpy_get_global(vm, "x"); - pkpy_push_null(vm); pkpy_push_int(vm, 1); pkpy_call(vm, 1); - int64_t r = pkpy_toint(vm, 1); + int r; + pkpy_to_int(vm, -1, &r); printf("%li\n", r); + pkpy_clear_error(vm, NULL); + pkpy_vm_destroy(vm); return 0; } diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 9e0ec369..d890eaa5 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -1,71 +1,180 @@ #include "pocketpy.h" #include "pocketpy_c.h" -pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os) { - pkpy::VM* p = new pkpy::VM(use_stdio, enable_os); +using namespace pkpy; - return (pkpy_vm) p; + +#define ERRHANDLER_OPEN try { \ + try { \ + if (vm->c_data.top() == nullptr) \ + return false; \ + +#define ERRHANDLER_CLOSE \ + } catch( Exception e ) { \ + vm->c_data.clear(); \ + vm->c_data.push(VAR(e.summary())); \ + vm->c_data.push(NULL); \ + return false; \ + } \ + } catch(...) { \ + std::cerr << "ERROR: a non pocketpy exeception was thrown " \ + << "this probably means pocketpy itself has a bug!\n"; \ + exit(2); \ + } + +bool pkpy_clear_error(pkpy_vm vm_handle, const char** message) { + VM* vm = (VM*) vm_handle; + + try { + if (vm->c_data.top() != nullptr) + return false; + + vm->c_data.pop(); + Str wrapped_message = CAST(Str&, vm->c_data.top()); + if (message != nullptr) + *message = wrapped_message.c_str_dup(); + else + std::cerr << "ERROR: " << wrapped_message << "\n"; + + vm->c_data.pop(); + //at this point the stack is clear + + return true; + + } catch(...) { + std::cerr << "ERROR: a non pocketpy exeception was thrown " + << "this probably means pocketpy itself has a bug!\n"; + exit(2); + } } -void pkpy_vm_exec(pkpy_vm vm_handle, const char* source) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; +pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os) { + VM* vm = new VM(use_stdio, enable_os); - vm->exec(source, "main.py", pkpy::EXEC_MODE); + return (pkpy_vm) vm; +} + +bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + vm->exec(source, "main.py", EXEC_MODE); + + return true; + ERRHANDLER_CLOSE } void pkpy_vm_destroy(pkpy_vm vm_handle) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - + VM* vm = (VM*) vm_handle; delete vm; } -void pkpy_push_cfunction(pkpy_vm vm_handle, pkpy_cfunction f) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - vm->s_data.push(VAR((pkpy::StackFunc) f)); +bool pkpy_push_function(pkpy_vm vm_handle, pkpy_function f) { + return true; } -void pkpy_push_int(pkpy_vm vm_handle, int64_t value) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - vm->s_data.push(VAR(value)); +bool pkpy_push_int(pkpy_vm vm_handle, int value) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + vm->c_data.push(VAR(value)); + + return true; + ERRHANDLER_CLOSE } -void pkpy_push_null(pkpy_vm vm_handle) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - vm->s_data.push(pkpy::PY_NULL); +bool pkpy_push_float(pkpy_vm vm_handle, double value) { + VM* vm = (VM*) vm_handle; + + ERRHANDLER_OPEN + vm->c_data.push(VAR(value)); + + return true; + ERRHANDLER_CLOSE } +bool pkpy_set_global(pkpy_vm vm_handle, const char* name) { + VM* vm = (VM*) vm_handle; -void pkpy_push_float(pkpy_vm vm_handle, double value) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - vm->s_data.push(VAR(value)); + ERRHANDLER_OPEN + + vm->_main->attr().set(name, vm->c_data.top()); + + vm->c_data.pop(); + + return true; + ERRHANDLER_CLOSE } -void pkpy_set_global(pkpy_vm vm_handle, const char* name) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; +bool pkpy_get_global(pkpy_vm vm_handle, const char* name) { + VM* vm = (VM*) vm_handle; - vm->_main->attr().set(name, vm->s_data.top()); + ERRHANDLER_OPEN - vm->s_data.pop(); + PyObject* o = vm->_main->attr().try_get(name); + + vm->c_data.push(o); + + return true; + ERRHANDLER_CLOSE } -void pkpy_get_global(pkpy_vm vm_handle, const char* name) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; +static void call_wrapper(VM* vm, int argc, bool method_call) { + int callable_index = vm->c_data.size() - argc - 1; - pkpy::PyObject* o = vm->_main->attr().try_get(name); + PyObject* callable = vm->c_data.get(callable_index); - vm->s_data.push(o); + vm->s_data.push(callable); + if (method_call) + vm->s_data.push(vm->c_data.get(callable_index - 1)); + else + vm->s_data.push(PY_NULL); + + for (int i = 0; i < argc; i++) + vm->s_data.push(vm->c_data.get(callable_index + i + 1)); + + PyObject* o = vm->vectorcall(argc); + + vm->c_data.shrink(argc + 1 + (int) method_call); + + //TODO unpack tuple? + vm->c_data.push(o); } -void pkpy_call(pkpy_vm vm_handle, int argc) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; - pkpy::PyObject* o = vm->vectorcall(argc, 0, 0); - vm->s_data.push(o); +bool pkpy_call(pkpy_vm vm_handle, int argc) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + call_wrapper(vm, argc, false); + + return true; + ERRHANDLER_CLOSE } -int pkpy_toint(pkpy_vm vm_handle, int index) { - pkpy::VM* vm = (pkpy::VM*) vm_handle; +bool pkpy_call_method(pkpy_vm vm_handle, int argc) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + call_wrapper(vm, argc, true); - pkpy::PyObject* o = vm->s_data.peek(index); - return pkpy::py_cast(vm, o); + return true; + ERRHANDLER_CLOSE } +static int lua_to_cstack_index(int index, int size) { + if (index < 0) + index = size + index; + return index; +} + +bool pkpy_to_int(pkpy_vm vm_handle, int index, int* ret) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + index = lua_to_cstack_index(index, vm->c_data.size()); + + PyObject* o = vm->c_data.get(index); + if (ret != nullptr) + *ret = py_cast(vm, o); + + return true; + ERRHANDLER_CLOSE +} diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index f800548f..e88d707e 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -10,23 +10,52 @@ extern "C" { typedef struct pkpy_vm_handle* pkpy_vm; +//we mostly follow the lua api for these bindings +//the key difference being each method returns a bool, true if it succeeded +//false if it did not + +//if a method returns false call this next method to check the error and clear it +//if this method returns false it means that no error was set, and no action is taken +//if it returns true it means there was an error and it was cleared, it will provide a string summary of the error in the message parameter (if it is not NULL) +//NOTE : you need to free the message that is passed back after you are done using it +//or else pass in null +bool pkpy_clear_error(pkpy_vm, const char** message); + + + pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os); -void pkpy_vm_exec(pkpy_vm vm_handle, const char* source); +bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source); void pkpy_vm_destroy(pkpy_vm vm); -////////binding a c function to pocketpy -typedef int (*pkpy_cfunction)(pkpy_vm); -void pkpy_push_cfunction(pkpy_vm, pkpy_cfunction); -void pkpy_push_int(pkpy_vm, int64_t); -void pkpy_push_float(pkpy_vm, double); -void pkpy_push_null(pkpy_vm); +typedef int (*pkpy_function)(pkpy_vm); -void pkpy_set_global(pkpy_vm, const char* name); -void pkpy_get_global(pkpy_vm vm_handle, const char* name); +bool pkpy_push_function(pkpy_vm, pkpy_function); +bool pkpy_push_int(pkpy_vm, int); +bool pkpy_push_float(pkpy_vm, double); -void pkpy_call(pkpy_vm vm_handle, int argc); -int pkpy_toint(pkpy_vm vm_handle, int index); +bool pkpy_set_global(pkpy_vm, const char* name); +bool pkpy_get_global(pkpy_vm vm_handle, const char* name); + +//first push callable you want to call +//then push the arguments to send +//argc is the number of arguments +bool pkpy_call(pkpy_vm vm_handle, int argc); + +//first push the object the method belongs to (self) +//then push the callable you want to call +//then push the the argments +//argc is the number of arguments that was pushed +bool pkpy_call_method(pkpy_vm vm_handle, int argc); + + + +//we will break with the lua api here +//lua uses 1 as the index to the first pushed element for all of these functions +//but we will start counting at zero to match python +//we will allow negative numbers to count backwards from the top + +bool pkpy_to_int(pkpy_vm vm_handle, int index, int* ret); diff --git a/src/error.h b/src/error.h index bd7182cd..a216b8a9 100644 --- a/src/error.h +++ b/src/error.h @@ -101,4 +101,4 @@ public: } }; -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/frame.h b/src/frame.h index 91ee5bda..0240a6ac 100644 --- a/src/frame.h +++ b/src/frame.h @@ -101,14 +101,13 @@ struct ValueStack { //stack for working with c bindings struct CVirtualStack { static const size_t MAX_SIZE = 256; - // We allocate 512 more bytes to keep `_sp` valid when `is_overflow() == true`. PyObject* _begin[MAX_SIZE]; PyObject** _sp; CVirtualStack(): _sp(_begin) {} PyObject* top() const { return _sp[-1]; } - PyObject* peek(int n) const { return _sp[-n]; } + PyObject* get(int index) const { return _begin[index]; } void push(PyObject* v){ *_sp++ = v; } void pop(){ --_sp; } void shrink(int n){ _sp -= n; } @@ -116,18 +115,12 @@ struct CVirtualStack { bool empty() const { return _sp == _begin; } PyObject** begin() { return _begin; } PyObject** end() { return _sp; } - void reset(PyObject** sp) { -#if DEBUG_EXTRA_CHECK - if(sp < _begin || sp > _begin + MAX_SIZE) FATAL_ERROR(); -#endif - _sp = sp; - } void clear() { _sp = _begin; } - ValueStack(const ValueStack&) = delete; - ValueStack(ValueStack&&) = delete; - ValueStack& operator=(const ValueStack&) = delete; - ValueStack& operator=(ValueStack&&) = delete; + CVirtualStack(const CVirtualStack&) = delete; + CVirtualStack(CVirtualStack&&) = delete; + CVirtualStack& operator=(const CVirtualStack&) = delete; + CVirtualStack& operator=(CVirtualStack&&) = delete; }; struct Frame { diff --git a/src/main.cpp b/src/main.cpp index 282d4522..2e7f94a1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,21 +6,21 @@ #ifndef __EMSCRIPTEN__ int main(int argc, char** argv){ - pkpy::VM* vm = pkpy_new_vm(); + pkpy::VM* vm = new pkpy::VM(); vm->bind_builtin_func<0>("input", [](pkpy::VM* vm, pkpy::ArgsView args){ return VAR(pkpy::getline()); }); if(argc == 1){ - pkpy::REPL* repl = pkpy_new_repl(vm); + pkpy::REPL* repl = new pkpy::REPL(vm); bool need_more_lines = false; while(true){ (*vm->_stdout) << (need_more_lines ? "... " : ">>> "); bool eof = false; std::string line = pkpy::getline(&eof); if(eof) break; - need_more_lines = pkpy_repl_input(repl, line.c_str()); + need_more_lines = repl->input(line.c_str()); } - pkpy_delete(vm); + delete vm; return 0; } @@ -47,7 +47,7 @@ int main(int argc, char** argv){ pkpy::PyObject* ret = nullptr; ret = vm->exec(src.c_str(), argv_1, pkpy::EXEC_MODE); - pkpy_delete(vm); + delete vm; return ret != nullptr ? 0 : 1; } @@ -56,4 +56,4 @@ __HELP: return 0; } -#endif \ No newline at end of file +#endif diff --git a/src/str.h b/src/str.h index bbac8fb4..72a6235d 100644 --- a/src/str.h +++ b/src/str.h @@ -434,4 +434,4 @@ const StrName __and__ = StrName::get("__and__"); const StrName __or__ = StrName::get("__or__"); const StrName __xor__ = StrName::get("__xor__"); -} // namespace pkpy \ No newline at end of file +} // namespace pkpy From 8244a8a1a49cda7784556f15e79217ae2f8b4def Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Sun, 30 Apr 2023 12:06:27 -0600 Subject: [PATCH 07/26] basic functionality is working much more smoothly thanks to LuaStyleFuncC also added the code for handling python exceptions --- c_bindings/main.c | 11 ++++++----- c_bindings/pocketpy_c.cpp | 34 ++++++++++++++++++++++++++++++++++ c_bindings/pocketpy_c.h | 13 ++++--------- src/frame.h | 16 ++++++++++------ 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/c_bindings/main.c b/c_bindings/main.c index 65670880..d66759f8 100644 --- a/c_bindings/main.c +++ b/c_bindings/main.c @@ -4,8 +4,9 @@ //tests the c bindings for pocketpy -void test_binding(pkpy_vm vm) { +int test_binding(pkpy_vm vm) { pkpy_push_int(vm, 12); + return 1; } int main(int argc, char** argv) { @@ -17,11 +18,11 @@ int main(int argc, char** argv) { pkpy_push_int(vm, 11); pkpy_set_global(vm, "eleven"); - //pkpy_push_cfunction(vm, test_binding); - //pkpy_set_global(vm, "binding"); + pkpy_push_function(vm, test_binding); + pkpy_set_global(vm, "binding"); pkpy_vm_exec(vm, "print(eleven)"); - //pkpy_vm_exec(vm, "print(binding())"); + pkpy_vm_exec(vm, "print(binding())"); pkpy_vm_exec(vm, "def x(x) : return x + 1"); @@ -31,7 +32,7 @@ int main(int argc, char** argv) { int r; pkpy_to_int(vm, -1, &r); - printf("%li\n", r); + printf("%i\n", r); pkpy_clear_error(vm, NULL); diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index d890eaa5..8a01971a 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -69,8 +69,42 @@ void pkpy_vm_destroy(pkpy_vm vm_handle) { delete vm; } +PyObject* c_function_wrapper(VM* vm, ArgsView args) { + LuaStyleFuncC f = CAST(NativeFunc&, args[-2])._lua_f; + + //setup c stack + int stored = vm->c_data.store(); + + for (int i = 0; i < args.size(); i++) + vm->c_data.push(args[i]); + + int retc = f(vm); + + PyObject* ret = vm->None; + + //TODO handle tuple packing for multiple returns + if (retc > 0) + ret = vm->c_data.top(); + + vm->c_data.clear(); + vm->c_data.restore(stored); + + return ret; +} + bool pkpy_push_function(pkpy_vm vm_handle, pkpy_function f) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + //TODO right now we just treat all c bound functions a varargs functions + //do we want to change that? + NativeFunc nf = NativeFunc(c_function_wrapper, -1, 0); + nf._lua_f = (LuaStyleFuncC) f; + + vm->c_data.push(VAR(nf)); + return true; + ERRHANDLER_CLOSE } bool pkpy_push_int(pkpy_vm vm_handle, int value) { diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index e88d707e..786756a4 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -11,18 +11,17 @@ extern "C" { typedef struct pkpy_vm_handle* pkpy_vm; //we mostly follow the lua api for these bindings -//the key difference being each method returns a bool, true if it succeeded +//the key difference being most methods return a bool, true if it succeeded //false if it did not //if a method returns false call this next method to check the error and clear it //if this method returns false it means that no error was set, and no action is taken //if it returns true it means there was an error and it was cleared, it will provide a string summary of the error in the message parameter (if it is not NULL) //NOTE : you need to free the message that is passed back after you are done using it -//or else pass in null +//or else pass in null as message, and it will just print the message to stderr bool pkpy_clear_error(pkpy_vm, const char** message); - pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os); bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source); void pkpy_vm_destroy(pkpy_vm vm); @@ -39,27 +38,23 @@ bool pkpy_get_global(pkpy_vm vm_handle, const char* name); //first push callable you want to call //then push the arguments to send -//argc is the number of arguments +//argc is the number of arguments that was pushed (not counting the callable) bool pkpy_call(pkpy_vm vm_handle, int argc); //first push the object the method belongs to (self) //then push the callable you want to call //then push the the argments -//argc is the number of arguments that was pushed +//argc is the number of arguments that was pushed (not counting the callable or self) bool pkpy_call_method(pkpy_vm vm_handle, int argc); - //we will break with the lua api here //lua uses 1 as the index to the first pushed element for all of these functions //but we will start counting at zero to match python //we will allow negative numbers to count backwards from the top - bool pkpy_to_int(pkpy_vm vm_handle, int index, int* ret); - - #ifdef __cplusplus } #endif diff --git a/src/frame.h b/src/frame.h index 0240a6ac..ee8c1789 100644 --- a/src/frame.h +++ b/src/frame.h @@ -103,20 +103,24 @@ struct CVirtualStack { static const size_t MAX_SIZE = 256; PyObject* _begin[MAX_SIZE]; PyObject** _sp; + size_t offset; - CVirtualStack(): _sp(_begin) {} + CVirtualStack(): _sp(_begin), offset(0) {} PyObject* top() const { return _sp[-1]; } - PyObject* get(int index) const { return _begin[index]; } + PyObject* get(int index) const { return _begin[offset + index]; } void push(PyObject* v){ *_sp++ = v; } void pop(){ --_sp; } void shrink(int n){ _sp -= n; } - int size() const { return _sp - _begin; } - bool empty() const { return _sp == _begin; } - PyObject** begin() { return _begin; } + int size() const { return (_sp - _begin) - offset; } + bool empty() const { return size() == 0; } + PyObject** begin() { return _begin + offset; } PyObject** end() { return _sp; } - void clear() { _sp = _begin; } + void clear() { _sp = _begin + offset;} + size_t store() { size_t ret = offset; offset = _sp - _begin; return ret; } + void restore(size_t stored) { offset = stored; } + CVirtualStack(const CVirtualStack&) = delete; CVirtualStack(CVirtualStack&&) = delete; CVirtualStack& operator=(const CVirtualStack&) = delete; From d4b9d354edd65a87e979b3c9b474abf1ada03ab1 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Sun, 30 Apr 2023 23:45:47 -0600 Subject: [PATCH 08/26] finalized initial c api and built tests for it --- c_bindings/main.c | 41 ----- c_bindings/pocketpy_c.cpp | 320 +++++++++++++++++++++++++++++++----- c_bindings/pocketpy_c.h | 61 +++++-- c_bindings/test.c | 262 +++++++++++++++++++++++++++++ c_bindings/test_answers.txt | 49 ++++++ run_c_binding_test.sh | 24 +++ src/frame.h | 1 + 7 files changed, 660 insertions(+), 98 deletions(-) delete mode 100644 c_bindings/main.c create mode 100644 c_bindings/test.c create mode 100644 c_bindings/test_answers.txt create mode 100644 run_c_binding_test.sh diff --git a/c_bindings/main.c b/c_bindings/main.c deleted file mode 100644 index d66759f8..00000000 --- a/c_bindings/main.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "pocketpy_c.h" -#include - -//tests the c bindings for pocketpy - - -int test_binding(pkpy_vm vm) { - pkpy_push_int(vm, 12); - return 1; -} - -int main(int argc, char** argv) { - - pkpy_vm vm = pkpy_vm_create(true, true); - - pkpy_vm_exec(vm, "print('hello world!')"); - - pkpy_push_int(vm, 11); - pkpy_set_global(vm, "eleven"); - - pkpy_push_function(vm, test_binding); - pkpy_set_global(vm, "binding"); - - pkpy_vm_exec(vm, "print(eleven)"); - pkpy_vm_exec(vm, "print(binding())"); - - pkpy_vm_exec(vm, "def x(x) : return x + 1"); - - pkpy_get_global(vm, "x"); - pkpy_push_int(vm, 1); - pkpy_call(vm, 1); - - int r; - pkpy_to_int(vm, -1, &r); - printf("%i\n", r); - - pkpy_clear_error(vm, NULL); - - pkpy_vm_destroy(vm); - return 0; -} diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 8a01971a..8a271d6d 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -3,52 +3,82 @@ using namespace pkpy; +#define SAFEGUARD_OPEN try { \ -#define ERRHANDLER_OPEN try { \ +#define SAFEGUARD_CLOSE \ + } catch(std::exception& e) { \ + std::cerr << "ERROR: a std::exception " \ + << "this probably means pocketpy itself has a bug!\n" \ + << e.what() << "\n"; \ + exit(2); \ + } catch(...) { \ + std::cerr << "ERROR: a unknown exception was thrown " \ + << "this probably means pocketpy itself has a bug!\n"; \ + exit(2); \ + } + + +#define ERRHANDLER_OPEN SAFEGUARD_OPEN \ try { \ if (vm->c_data.top() == nullptr) \ return false; \ #define ERRHANDLER_CLOSE \ } catch( Exception e ) { \ - vm->c_data.clear(); \ - vm->c_data.push(VAR(e.summary())); \ + vm->c_data.push(VAR(e)); \ vm->c_data.push(NULL); \ return false; \ } \ - } catch(...) { \ - std::cerr << "ERROR: a non pocketpy exeception was thrown " \ - << "this probably means pocketpy itself has a bug!\n"; \ - exit(2); \ - } + SAFEGUARD_CLOSE \ -bool pkpy_clear_error(pkpy_vm vm_handle, const char** message) { + +//for now I will unpack a tuple automatically, we may not want to handle +//it this way, not sure +//it is more lua like, but maybe not python like +static void unpack_return(VM* vm, PyObject* ret) { + if (is_type(ret, vm->tp_tuple)) { + Tuple& t = CAST(Tuple&, ret); + for (int i = 0; i < t.size(); i++) + vm->c_data.push(t[i]); + } else if (ret == vm->None) { + //do nothing here + //having to pop the stack after every call that returns none is annoying + //lua does not do this + // + //so for now we will not push none on the stack when it is the sole thing returned + //if this becomes a problem we can change it + // + //you can still check if it returned none by comparing stack size before + //and after if you have too + } else + vm->c_data.push(ret); + +} + + +bool pkpy_clear_error(pkpy_vm vm_handle, char** message) { VM* vm = (VM*) vm_handle; + SAFEGUARD_OPEN - try { if (vm->c_data.top() != nullptr) return false; vm->c_data.pop(); - Str wrapped_message = CAST(Str&, vm->c_data.top()); + Exception& e = CAST(Exception&, vm->c_data.top()); if (message != nullptr) - *message = wrapped_message.c_str_dup(); + *message = e.summary().c_str_dup(); else - std::cerr << "ERROR: " << wrapped_message << "\n"; - - vm->c_data.pop(); - //at this point the stack is clear + std::cerr << "ERROR: " << e.summary() << "\n"; + vm->c_data.clear(); + vm->callstack.clear(); + vm->s_data.clear(); return true; - } catch(...) { - std::cerr << "ERROR: a non pocketpy exeception was thrown " - << "this probably means pocketpy itself has a bug!\n"; - exit(2); - } + SAFEGUARD_CLOSE } -pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os) { +pkpy_vm pkpy_new_vm(bool use_stdio, bool enable_os) { VM* vm = new VM(use_stdio, enable_os); return (pkpy_vm) vm; @@ -58,17 +88,55 @@ bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source) { VM* vm = (VM*) vm_handle; ERRHANDLER_OPEN - vm->exec(source, "main.py", EXEC_MODE); + CodeObject_ code = vm->compile(source, "", EXEC_MODE); + PyObject* result = vm->_exec(code, vm->_main); + unpack_return(vm, result); return true; ERRHANDLER_CLOSE } -void pkpy_vm_destroy(pkpy_vm vm_handle) { +void pkpy_delete_vm(pkpy_vm vm_handle) { VM* vm = (VM*) vm_handle; delete vm; } +pkpy_repl pkpy_new_repl(pkpy_vm vm_handle) { + VM* vm = (VM*) vm_handle; + REPL* repl = new REPL(vm); + return (pkpy_repl) repl; +} + +bool pkpy_repl_input(pkpy_repl repl_handle, const char* line) { + REPL* repl = (REPL*) repl_handle; + return repl->input(line); +} + +void pkpy_delete_repl(pkpy_repl repl_handle) { + REPL* repl = (REPL*) repl_handle; + delete repl; +} + +static void propagate_if_errored(VM* vm) { + try { + if (vm->c_data.top() != nullptr) + return; + + vm->c_data.pop(); + Exception& e = CAST(Exception&, vm->c_data.top()); + vm->c_data.pop(); + + throw e; + } catch(Exception& e) { + throw; + } catch(...) { + std::cerr << "ERROR: a non pocketpy exeception was thrown " + << "this probably means pocketpy itself has a bug!\n"; + exit(2); + } +} + + PyObject* c_function_wrapper(VM* vm, ArgsView args) { LuaStyleFuncC f = CAST(NativeFunc&, args[-2])._lua_f; @@ -81,10 +149,20 @@ PyObject* c_function_wrapper(VM* vm, ArgsView args) { int retc = f(vm); PyObject* ret = vm->None; + propagate_if_errored(vm); - //TODO handle tuple packing for multiple returns - if (retc > 0) + if (retc == 1) ret = vm->c_data.top(); + else if (retc > 1) { + Tuple t = Tuple(retc); + + for (int i = 0; i < retc; i++) { + int stack_index = (vm->c_data.size() - retc) + i; + t[i] = vm->c_data.get(stack_index); + } + + ret = VAR(t); + } vm->c_data.clear(); vm->c_data.restore(stored); @@ -127,6 +205,50 @@ bool pkpy_push_float(pkpy_vm vm_handle, double value) { ERRHANDLER_CLOSE } +bool pkpy_push_bool(pkpy_vm vm_handle, bool value) { + VM* vm = (VM*) vm_handle; + + ERRHANDLER_OPEN + vm->c_data.push(VAR(value)); + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_push_string(pkpy_vm vm_handle, const char* value) { + VM* vm = (VM*) vm_handle; + + ERRHANDLER_OPEN + vm->c_data.push(VAR(value)); + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_push_stringn(pkpy_vm vm_handle, const char* value, int length) { + VM* vm = (VM*) vm_handle; + + ERRHANDLER_OPEN + + Str s = Str(value, length); + vm->c_data.push(VAR(s)); + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_push_none(pkpy_vm vm_handle) { + VM* vm = (VM*) vm_handle; + + ERRHANDLER_OPEN + vm->c_data.push(vm->None); + + return true; + ERRHANDLER_CLOSE +} + + + bool pkpy_set_global(pkpy_vm vm_handle, const char* name) { VM* vm = (VM*) vm_handle; @@ -140,12 +262,18 @@ bool pkpy_set_global(pkpy_vm vm_handle, const char* name) { ERRHANDLER_CLOSE } +//get global will also get bulitins bool pkpy_get_global(pkpy_vm vm_handle, const char* name) { VM* vm = (VM*) vm_handle; ERRHANDLER_OPEN PyObject* o = vm->_main->attr().try_get(name); + if (o == nullptr) { + o = vm->builtins->attr().try_get(name); + if (o == nullptr) + throw Exception("AttributeError", "could not find requested global"); + } vm->c_data.push(o); @@ -153,46 +281,58 @@ bool pkpy_get_global(pkpy_vm vm_handle, const char* name) { ERRHANDLER_CLOSE } -static void call_wrapper(VM* vm, int argc, bool method_call) { + +bool pkpy_call(pkpy_vm vm_handle, int argc) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + int callable_index = vm->c_data.size() - argc - 1; PyObject* callable = vm->c_data.get(callable_index); vm->s_data.push(callable); - if (method_call) - vm->s_data.push(vm->c_data.get(callable_index - 1)); - else - vm->s_data.push(PY_NULL); + vm->s_data.push(PY_NULL); for (int i = 0; i < argc; i++) vm->s_data.push(vm->c_data.get(callable_index + i + 1)); PyObject* o = vm->vectorcall(argc); - vm->c_data.shrink(argc + 1 + (int) method_call); + vm->c_data.shrink(argc + 1); - //TODO unpack tuple? - vm->c_data.push(o); -} - -bool pkpy_call(pkpy_vm vm_handle, int argc) { - VM* vm = (VM*) vm_handle; - ERRHANDLER_OPEN - call_wrapper(vm, argc, false); + unpack_return(vm, o); return true; ERRHANDLER_CLOSE } -bool pkpy_call_method(pkpy_vm vm_handle, int argc) { +bool pkpy_call_method(pkpy_vm vm_handle, const char* name, int argc) { VM* vm = (VM*) vm_handle; ERRHANDLER_OPEN - call_wrapper(vm, argc, true); + + int self_index = vm->c_data.size() - argc - 1; + PyObject* self = vm->c_data.get(self_index); + + PyObject* callable = vm->get_unbound_method(self, name, &self); + + vm->s_data.push(callable); + vm->s_data.push(self); + + for (int i = 0; i < argc; i++) + vm->s_data.push(vm->c_data.get(self_index + i + 1)); + + PyObject* o = vm->vectorcall(argc); + + vm->c_data.shrink(argc + 1); + + unpack_return(vm, o); return true; ERRHANDLER_CLOSE } + + static int lua_to_cstack_index(int index, int size) { if (index < 0) index = size + index; @@ -212,3 +352,99 @@ bool pkpy_to_int(pkpy_vm vm_handle, int index, int* ret) { return true; ERRHANDLER_CLOSE } + +bool pkpy_to_float(pkpy_vm vm_handle, int index, double* ret) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + index = lua_to_cstack_index(index, vm->c_data.size()); + + PyObject* o = vm->c_data.get(index); + if (ret != nullptr) + *ret = py_cast(vm, o); + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_to_bool(pkpy_vm vm_handle, int index, bool* ret) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + index = lua_to_cstack_index(index, vm->c_data.size()); + + PyObject* o = vm->c_data.get(index); + if (ret != nullptr) + *ret = py_cast(vm, o); + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_to_string(pkpy_vm vm_handle, int index, char** ret) { + VM* vm = (VM*) vm_handle; + ERRHANDLER_OPEN + + index = lua_to_cstack_index(index, vm->c_data.size()); + + PyObject* o = vm->c_data.get(index); + if (ret != nullptr) { + Str& s = CAST(Str&, o); + *ret = s.c_str_dup(); + } + + return true; + ERRHANDLER_CLOSE +} + +bool pkpy_is_int(pkpy_vm vm_handle, int index) { + VM* vm = (VM*) vm_handle; + + index = lua_to_cstack_index(index, vm->c_data.size()); + PyObject* o = vm->c_data.get(index); + + return is_type(o, vm->tp_int); +} +bool pkpy_is_float(pkpy_vm vm_handle, int index) { + VM* vm = (VM*) vm_handle; + + index = lua_to_cstack_index(index, vm->c_data.size()); + PyObject* o = vm->c_data.get(index); + + return is_type(o, vm->tp_float); +} +bool pkpy_is_bool(pkpy_vm vm_handle, int index) { + VM* vm = (VM*) vm_handle; + + index = lua_to_cstack_index(index, vm->c_data.size()); + PyObject* o = vm->c_data.get(index); + + return is_type(o, vm->tp_bool); +} +bool pkpy_is_string(pkpy_vm vm_handle, int index) { + VM* vm = (VM*) vm_handle; + + index = lua_to_cstack_index(index, vm->c_data.size()); + PyObject* o = vm->c_data.get(index); + + return is_type(o, vm->tp_str); +} +bool pkpy_is_none(pkpy_vm vm_handle, int index) { + VM* vm = (VM*) vm_handle; + + index = lua_to_cstack_index(index, vm->c_data.size()); + PyObject* o = vm->c_data.get(index); + + return o == vm->None; +} + +bool pkpy_check_stack(pkpy_vm vm_handle, int free) { + VM* vm = (VM*) vm_handle; + return free <= vm->c_data.remaining(); +} + +int pkpy_stack_size(pkpy_vm vm_handle) { + VM* vm = (VM*) vm_handle; + return vm->c_data.size(); +} + diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index 786756a4..6c56742f 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -9,22 +9,30 @@ extern "C" { #include typedef struct pkpy_vm_handle* pkpy_vm; +typedef struct pkpy_repl_hande* pkpy_repl; -//we mostly follow the lua api for these bindings -//the key difference being most methods return a bool, true if it succeeded -//false if it did not +//we we take a lot of inspiration from the lua api for these bindings +//the key difference being most methods return a bool, +//true if it succeeded false if it did not -//if a method returns false call this next method to check the error and clear it -//if this method returns false it means that no error was set, and no action is taken -//if it returns true it means there was an error and it was cleared, it will provide a string summary of the error in the message parameter (if it is not NULL) +//if a method returns false call the pkpy_clear_error method to check the error and clear it +//if pkpy_clear_error returns false it means that no error was set, and it takes no action +//if pkpy_clear_error returns true it means there was an error and it was cleared, +//it will provide a string summary of the error in the message parameter (if it is not NULL) //NOTE : you need to free the message that is passed back after you are done using it //or else pass in null as message, and it will just print the message to stderr -bool pkpy_clear_error(pkpy_vm, const char** message); +bool pkpy_clear_error(pkpy_vm, char** message); -pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os); -bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source); -void pkpy_vm_destroy(pkpy_vm vm); +pkpy_vm pkpy_new_vm(bool use_stdio, bool enable_os); +bool pkpy_vm_exec(pkpy_vm, const char* source); +void pkpy_delete_vm(pkpy_vm); + +pkpy_repl pkpy_new_repl(pkpy_vm); +//the repl does its own error management, so this method doesn't follow the ame +//error semantics, it is just a thin wrapper around the REPL classes input method +bool pkpy_repl_input(pkpy_repl, const char* line); +void pkpy_delete_repl(pkpy_repl); typedef int (*pkpy_function)(pkpy_vm); @@ -32,29 +40,52 @@ typedef int (*pkpy_function)(pkpy_vm); bool pkpy_push_function(pkpy_vm, pkpy_function); bool pkpy_push_int(pkpy_vm, int); bool pkpy_push_float(pkpy_vm, double); +bool pkpy_push_bool(pkpy_vm, bool); +bool pkpy_push_string(pkpy_vm, const char*); +bool pkpy_push_stringn(pkpy_vm, const char*, int length); +bool pkpy_push_none(pkpy_vm); bool pkpy_set_global(pkpy_vm, const char* name); -bool pkpy_get_global(pkpy_vm vm_handle, const char* name); +bool pkpy_get_global(pkpy_vm, const char* name); //first push callable you want to call //then push the arguments to send //argc is the number of arguments that was pushed (not counting the callable) -bool pkpy_call(pkpy_vm vm_handle, int argc); +bool pkpy_call(pkpy_vm, int argc); //first push the object the method belongs to (self) -//then push the callable you want to call //then push the the argments //argc is the number of arguments that was pushed (not counting the callable or self) -bool pkpy_call_method(pkpy_vm vm_handle, int argc); +//name is the name of the method to call on the object +bool pkpy_call_method(pkpy_vm, const char* name, int argc); //we will break with the lua api here //lua uses 1 as the index to the first pushed element for all of these functions //but we will start counting at zero to match python //we will allow negative numbers to count backwards from the top -bool pkpy_to_int(pkpy_vm vm_handle, int index, int* ret); +bool pkpy_to_int(pkpy_vm, int index, int* ret); +bool pkpy_to_float(pkpy_vm, int index, double* ret); +bool pkpy_to_bool(pkpy_vm, int index, bool* ret); +//you have to free ret after you are done using it +bool pkpy_to_string(pkpy_vm, int index, char** ret); +//these do not follow the same error semantics as above, their return values +//just say whether the check succeeded or not, or else return the value asked for + +bool pkpy_is_int(pkpy_vm, int index); +bool pkpy_is_float(pkpy_vm, int index); +bool pkpy_is_bool(pkpy_vm, int index); +bool pkpy_is_string(pkpy_vm, int index); +bool pkpy_is_none(pkpy_vm, int index); + +//will return true if at least free empty slots remain on the stack +bool pkpy_check_stack(pkpy_vm, int free); + +//returns the number of elements on the stack +int pkpy_stack_size(pkpy_vm); + #ifdef __cplusplus } #endif diff --git a/c_bindings/test.c b/c_bindings/test.c new file mode 100644 index 00000000..42df6048 --- /dev/null +++ b/c_bindings/test.c @@ -0,0 +1,262 @@ +#include "pocketpy_c.h" +#include +#include + +//tests the c bindings for pocketpy + +void check_impl(pkpy_vm vm, bool result, int lineno) { + if (!result) { + printf("ERROR: failed where it should have succeed at line %i\n", lineno); + char* message; + if (!pkpy_clear_error(vm, &message)) { + printf("clear error reported everything was fine\n"); + exit(1); + } + + printf("%s\n", message); + free(message); + exit(1); + } +} + +void fail_impl(pkpy_vm vm, bool result, int lineno) { + if (result) { + printf("ERROR: succeeded where it should have failed line %i\n", lineno); + exit(1); + } else { + char* message; + if (pkpy_clear_error(vm, &message)) { + printf("actually errored!\n"); + free(message); + exit(1); + } + } +} + +void error_impl(pkpy_vm vm, bool result, int lineno) { + if (result) { + printf("ERROR: succeeded where it should have failed line %i\n", lineno); + exit(1); + } else { + char* message; + if (!pkpy_clear_error(vm, &message)) + printf("clear error reported everything was fine\n"); + else { + printf("successfully errored with this message: \n"); + printf("%s\n", message); + free(message); + } + } +} + + +#define check(r) check_impl(vm, (r), __LINE__) +#define fail(r) fail_impl(vm, (r), __LINE__) +#define error(r) error_impl(vm, (r), __LINE__) + +int test_binding(pkpy_vm vm) { + pkpy_push_int(vm, 12); + return 1; +} + +int test_multiple_return(pkpy_vm vm) { + pkpy_push_int(vm, 12); + pkpy_push_int(vm, 13); + return 2; +} + +int test_return_none(pkpy_vm vm) { + return 0; +} + +int test_error_propagate(pkpy_vm vm) { + pkpy_get_global(vm, "does not exist"); + return 1; +} + + +pkpy_vm vm; + +void cleanup(void) { + pkpy_delete_vm(vm); +} + +int main(int argc, char** argv) { + + vm = pkpy_new_vm(true, true); + atexit(cleanup); + + //test exec + check(pkpy_vm_exec(vm, "print('hello world!')")); + + error(pkpy_get_global(vm, "nonexistatn")); + + printf("\ntesting int methods\n"); + int r_int; + check(pkpy_push_int(vm, 11)); + check(pkpy_set_global(vm, "eleven")); + check(pkpy_vm_exec(vm, "print(eleven)")); + check(pkpy_get_global(vm, "eleven")); + check(pkpy_is_int(vm, -1)); + check(pkpy_to_int(vm, -1, &r_int)); + printf("%i\n", r_int); + fail(pkpy_is_float(vm, -1)); + fail(pkpy_is_bool(vm, -1)); + fail(pkpy_is_string(vm, -1)); + fail(pkpy_is_none(vm, -1)); + + printf("\ntesting float methods\n"); + double r_float; + check(pkpy_push_float(vm, 11.11)); + check(pkpy_set_global(vm, "elevenf")); + check(pkpy_vm_exec(vm, "print(elevenf)")); + check(pkpy_get_global(vm, "elevenf")); + check(pkpy_is_float(vm, -1)); + check(pkpy_to_float(vm, -1, &r_float)); + printf("%f\n", r_float); + fail(pkpy_is_int(vm, -1)); + fail(pkpy_is_bool(vm, -1)); + fail(pkpy_is_string(vm, -1)); + fail(pkpy_is_none(vm, -1)); + + printf("\ntesting bool methods\n"); + bool r_bool; + check(pkpy_push_bool(vm, false)); + check(pkpy_set_global(vm, "false_test")); + check(pkpy_vm_exec(vm, "print(false_test)")); + check(pkpy_get_global(vm, "false_test")); + check(pkpy_is_bool(vm, -1)); + check(pkpy_to_bool(vm, -1, &r_bool)); + printf("%i\n", r_bool); + fail(pkpy_is_int(vm, -1)); + fail(pkpy_is_float(vm, -1)); + fail(pkpy_is_string(vm, -1)); + fail(pkpy_is_none(vm, -1)); + + printf("\ntesting string methods\n"); + char* r_string; + check(pkpy_push_string(vm, "hello!")); + check(pkpy_set_global(vm, "hello1")); + check(pkpy_vm_exec(vm, "print(hello1)")); + check(pkpy_push_stringn(vm, "hello!", 5)); + check(pkpy_is_string(vm, -1)); + check(pkpy_to_string(vm, -1, &r_string)); + printf("%s\n", r_string); + fail(pkpy_is_int(vm, -1)); + fail(pkpy_is_float(vm, -1)); + fail(pkpy_is_bool(vm, -1)); + fail(pkpy_is_none(vm, -1)); + free(r_string); + + printf("\ntesting None methods\n"); + check(pkpy_push_none(vm)); + check(pkpy_set_global(vm, "none")); + check(pkpy_vm_exec(vm, "print(none)")); + check(pkpy_get_global(vm, "none")); + check(pkpy_is_none(vm, -1)); + fail(pkpy_is_int(vm, -1)); + fail(pkpy_is_float(vm, -1)); + fail(pkpy_is_bool(vm, -1)); + fail(pkpy_is_string(vm, -1)); + + printf("\ntesting sizing and indexing\n"); + int stack_size = pkpy_stack_size(vm); + printf("stack size %i\n", stack_size); + check(pkpy_check_stack(vm, 10)); + check(pkpy_check_stack(vm, 251)); + fail(pkpy_check_stack(vm, 252)); + check(pkpy_is_int(vm, 0)); + check(pkpy_is_float(vm, 1)); + check(pkpy_is_bool(vm, 2)); + check(pkpy_is_string(vm, 3)); + check(pkpy_is_none(vm, 4)); + check(pkpy_is_int(vm, -5)); + check(pkpy_is_float(vm, -4)); + check(pkpy_is_bool(vm, -3)); + check(pkpy_is_string(vm, -2)); + check(pkpy_is_none(vm, -1)); + + printf("\ntesting error catching\n"); + error(pkpy_vm_exec(vm, "let's make sure syntax errors get caught")); + check(pkpy_stack_size(vm) == 0); //stack should be cleared after error is resolved + + printf("\ntesting calls\n"); + + check(pkpy_vm_exec(vm, "def x(x, y) : return x - y")); + check(pkpy_vm_exec(vm, "def vararg_x(*x) : return sum(x)")); + check(pkpy_vm_exec(vm, "def keyword_x(x=1, y=1) : return x+y")); + check(pkpy_vm_exec(vm, "def retmany_x() : return 1, 2, 3")); + + check(pkpy_get_global(vm, "x")); + check(pkpy_push_int(vm, 2)); + check(pkpy_push_int(vm, 3)); + check(pkpy_call(vm, 2)); + check(pkpy_to_int(vm, -1, &r_int)); + printf("x : %i\n", r_int); + + check(pkpy_get_global(vm, "vararg_x")); + check(pkpy_push_int(vm, 1)); + check(pkpy_push_int(vm, 2)); + check(pkpy_push_int(vm, 3)); + check(pkpy_push_int(vm, 4)); + check(pkpy_push_int(vm, 5)); + check(pkpy_push_int(vm, 6)); + check(pkpy_call(vm, 6)); + check(pkpy_to_int(vm, -1, &r_int)); + printf("vararg_x : %i\n", r_int); + + check(pkpy_get_global(vm, "keyword_x")); + check(pkpy_push_int(vm, 3)); + check(pkpy_call(vm, 1)); + check(pkpy_to_int(vm, -1, &r_int)); + printf("keyword_x : %i\n", r_int); + + check(pkpy_get_global(vm, "keyword_x")); + check(pkpy_call(vm, 0)); + check(pkpy_to_int(vm, -1, &r_int)); + printf("keyword_x : %i\n", r_int); + + check(pkpy_stack_size(vm) == 4); + + check(pkpy_get_global(vm, "retmany_x")); + check(pkpy_call(vm, 0)); + check(pkpy_stack_size(vm) == 7); + check(pkpy_to_int(vm, -3, &r_int)); + printf("retmany_x : %i\n", r_int); + check(pkpy_to_int(vm, -2, &r_int)); + printf("retmany_x : %i\n", r_int); + check(pkpy_to_int(vm, -1, &r_int)); + printf("retmany_x : %i\n", r_int); + + check(pkpy_get_global(vm, "x")); + error(pkpy_call(vm, 0)); + + check(pkpy_vm_exec(vm, "l = []")); + + check(pkpy_get_global(vm, "l")); + check(pkpy_push_string(vm, "hello")); + check(pkpy_call_method(vm, "append", 1)); + check(pkpy_vm_exec(vm, "print(l)")); + + + printf("\ntesting pushing functions\n"); + + check(pkpy_push_function(vm, test_binding)); + check(pkpy_set_global(vm, "test_binding")); + check(pkpy_vm_exec(vm, "print(test_binding())")); + + check(pkpy_push_function(vm, test_multiple_return)); + check(pkpy_set_global(vm, "test_multiple_return")); + check(pkpy_vm_exec(vm, "test_multiple_return()")); + check(pkpy_stack_size(vm) == 2); + + check(pkpy_push_function(vm, test_error_propagate)); + check(pkpy_set_global(vm, "test_error_propagate")); + error(pkpy_vm_exec(vm, "test_error_propagate()")); + + check(pkpy_get_global(vm, "test_multiple_return")); + check(pkpy_call(vm, 0)); + check(pkpy_stack_size(vm) == 2); + + return 0; +} diff --git a/c_bindings/test_answers.txt b/c_bindings/test_answers.txt new file mode 100644 index 00000000..66be2403 --- /dev/null +++ b/c_bindings/test_answers.txt @@ -0,0 +1,49 @@ +hello world! +successfully errored with this message: +Traceback (most recent call last): +AttributeError: could not find requested global + +testing int methods +11 +11 + +testing float methods +11.11 +11.110000 + +testing bool methods +False +0 + +testing string methods +hello! +hello + +testing None methods +None + +testing sizing and indexing +stack size 5 + +testing error catching +successfully errored with this message: + File "", line 1 + let's make sure syntax errors get caught +SyntaxError: EOL while scanning string literal + +testing calls +x : -1 +vararg_x : 21 +keyword_x : 4 +keyword_x : 2 +retmany_x : 1 +retmany_x : 2 +retmany_x : 3 +successfully errored with this message: +TypeError: expected 2 positional arguments, but got 0 (x) +['hello'] + +testing pushing functions +12 +ERROR: failed where it should have succeed at line 251 +clear error reported everything was fine diff --git a/run_c_binding_test.sh b/run_c_binding_test.sh new file mode 100644 index 00000000..e92b1439 --- /dev/null +++ b/run_c_binding_test.sh @@ -0,0 +1,24 @@ +python3 preprocess.py + +if [ ! -f "pocketpy_c.o" ] +then + echo "compiling c++ lib" + clang++ -c -o pocketpy_c.o c_bindings/pocketpy_c.cpp -Wfatal-errors --std=c++17 -O2 -Wall -Wno-sign-compare -Wno-unused-variable -fno-rtti -stdlib=libc++ -I src/ -fsanitize=address -g +else + echo "DETECTED PREVIOUS COMPILATION USING IT" +fi + +echo "compiling c executable" +clang -c -o test.o c_bindings/test.c -Wfatal-errors -O2 -Wall -Wno-sign-compare -Wno-unused-variable -I src/ -fsanitize=address -g +echo "linking" +clang++ -o c_binding_test test.o pocketpy_c.o -stdlib=libc++ -fsanitize=address -g +echo "running, no weird output should show up" +./c_binding_test > binding_test_scratch +echo "checking results (they should be identical)" +diff -s binding_test_scratch c_bindings/test_answers.txt + +echo "cleaning up" +rm pocketpy_c.o +rm test.o +rm binding_test_scratch + diff --git a/src/frame.h b/src/frame.h index ee8c1789..d0facbae 100644 --- a/src/frame.h +++ b/src/frame.h @@ -117,6 +117,7 @@ struct CVirtualStack { PyObject** begin() { return _begin + offset; } PyObject** end() { return _sp; } void clear() { _sp = _begin + offset;} + int remaining() { return MAX_SIZE - (_sp - _begin); } size_t store() { size_t ret = offset; offset = _sp - _begin; return ret; } void restore(size_t stored) { offset = stored; } From 527a783d131347f1689b37e07d718bd6c6460fe5 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Mon, 1 May 2023 00:00:02 -0600 Subject: [PATCH 09/26] cleanup and light refactor (with some renames) --- build_c.sh | 12 ------------ c_bindings/pocketpy_c.cpp | 22 +++------------------- c_bindings/pocketpy_c.h | 14 +++----------- c_bindings/test.c | 38 +++++++++++++++++++------------------- run_c_binding_test.sh | 2 +- src/error.h | 1 + src/frame.h | 1 + src/main.cpp | 11 ++++++----- src/pocketpy.h | 4 ++-- src/str.h | 1 + src/vm.h | 1 + 11 files changed, 38 insertions(+), 69 deletions(-) delete mode 100644 build_c.sh diff --git a/build_c.sh b/build_c.sh deleted file mode 100644 index 54d82df6..00000000 --- a/build_c.sh +++ /dev/null @@ -1,12 +0,0 @@ -python3 preprocess.py - -echo "compiling c++ lib" -clang++ -c -o pocketpy_c.o c_bindings/pocketpy_c.cpp -Wfatal-errors --std=c++17 -O2 -Wall -Wno-sign-compare -Wno-unused-variable -fno-rtti -stdlib=libc++ -I src/ -fsanitize=address -g -echo "compiling c executable" -clang -c -o main.o c_bindings/main.c -Wfatal-errors -O2 -Wall -Wno-sign-compare -Wno-unused-variable -I src/ -fsanitize=address -g -echo "linking" -clang++ -o pocketpy_c main.o pocketpy_c.o -stdlib=libc++ -fsanitize=address -g -echo "cleaning up" -rm pocketpy_c.o -rm main.o - diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 8a271d6d..50d0d4a6 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -78,13 +78,13 @@ bool pkpy_clear_error(pkpy_vm vm_handle, char** message) { SAFEGUARD_CLOSE } -pkpy_vm pkpy_new_vm(bool use_stdio, bool enable_os) { +pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os) { VM* vm = new VM(use_stdio, enable_os); return (pkpy_vm) vm; } -bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source) { +bool pkpy_vm_run(pkpy_vm vm_handle, const char* source) { VM* vm = (VM*) vm_handle; ERRHANDLER_OPEN @@ -96,27 +96,11 @@ bool pkpy_vm_exec(pkpy_vm vm_handle, const char* source) { ERRHANDLER_CLOSE } -void pkpy_delete_vm(pkpy_vm vm_handle) { +void pkpy_vm_destroy(pkpy_vm vm_handle) { VM* vm = (VM*) vm_handle; delete vm; } -pkpy_repl pkpy_new_repl(pkpy_vm vm_handle) { - VM* vm = (VM*) vm_handle; - REPL* repl = new REPL(vm); - return (pkpy_repl) repl; -} - -bool pkpy_repl_input(pkpy_repl repl_handle, const char* line) { - REPL* repl = (REPL*) repl_handle; - return repl->input(line); -} - -void pkpy_delete_repl(pkpy_repl repl_handle) { - REPL* repl = (REPL*) repl_handle; - delete repl; -} - static void propagate_if_errored(VM* vm) { try { if (vm->c_data.top() != nullptr) diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index 6c56742f..d64af7c2 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -23,17 +23,9 @@ typedef struct pkpy_repl_hande* pkpy_repl; //or else pass in null as message, and it will just print the message to stderr bool pkpy_clear_error(pkpy_vm, char** message); - -pkpy_vm pkpy_new_vm(bool use_stdio, bool enable_os); -bool pkpy_vm_exec(pkpy_vm, const char* source); -void pkpy_delete_vm(pkpy_vm); - -pkpy_repl pkpy_new_repl(pkpy_vm); -//the repl does its own error management, so this method doesn't follow the ame -//error semantics, it is just a thin wrapper around the REPL classes input method -bool pkpy_repl_input(pkpy_repl, const char* line); -void pkpy_delete_repl(pkpy_repl); - +pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os); +bool pkpy_vm_run(pkpy_vm, const char* source); +void pkpy_vm_destroy(pkpy_vm); typedef int (*pkpy_function)(pkpy_vm); diff --git a/c_bindings/test.c b/c_bindings/test.c index 42df6048..8aa8cd76 100644 --- a/c_bindings/test.c +++ b/c_bindings/test.c @@ -78,16 +78,16 @@ int test_error_propagate(pkpy_vm vm) { pkpy_vm vm; void cleanup(void) { - pkpy_delete_vm(vm); + pkpy_vm_destroy(vm); } int main(int argc, char** argv) { - vm = pkpy_new_vm(true, true); + vm = pkpy_vm_create(true, true); atexit(cleanup); - //test exec - check(pkpy_vm_exec(vm, "print('hello world!')")); + //test run + check(pkpy_vm_run(vm, "print('hello world!')")); error(pkpy_get_global(vm, "nonexistatn")); @@ -95,7 +95,7 @@ int main(int argc, char** argv) { int r_int; check(pkpy_push_int(vm, 11)); check(pkpy_set_global(vm, "eleven")); - check(pkpy_vm_exec(vm, "print(eleven)")); + check(pkpy_vm_run(vm, "print(eleven)")); check(pkpy_get_global(vm, "eleven")); check(pkpy_is_int(vm, -1)); check(pkpy_to_int(vm, -1, &r_int)); @@ -109,7 +109,7 @@ int main(int argc, char** argv) { double r_float; check(pkpy_push_float(vm, 11.11)); check(pkpy_set_global(vm, "elevenf")); - check(pkpy_vm_exec(vm, "print(elevenf)")); + check(pkpy_vm_run(vm, "print(elevenf)")); check(pkpy_get_global(vm, "elevenf")); check(pkpy_is_float(vm, -1)); check(pkpy_to_float(vm, -1, &r_float)); @@ -123,7 +123,7 @@ int main(int argc, char** argv) { bool r_bool; check(pkpy_push_bool(vm, false)); check(pkpy_set_global(vm, "false_test")); - check(pkpy_vm_exec(vm, "print(false_test)")); + check(pkpy_vm_run(vm, "print(false_test)")); check(pkpy_get_global(vm, "false_test")); check(pkpy_is_bool(vm, -1)); check(pkpy_to_bool(vm, -1, &r_bool)); @@ -137,7 +137,7 @@ int main(int argc, char** argv) { char* r_string; check(pkpy_push_string(vm, "hello!")); check(pkpy_set_global(vm, "hello1")); - check(pkpy_vm_exec(vm, "print(hello1)")); + check(pkpy_vm_run(vm, "print(hello1)")); check(pkpy_push_stringn(vm, "hello!", 5)); check(pkpy_is_string(vm, -1)); check(pkpy_to_string(vm, -1, &r_string)); @@ -151,7 +151,7 @@ int main(int argc, char** argv) { printf("\ntesting None methods\n"); check(pkpy_push_none(vm)); check(pkpy_set_global(vm, "none")); - check(pkpy_vm_exec(vm, "print(none)")); + check(pkpy_vm_run(vm, "print(none)")); check(pkpy_get_global(vm, "none")); check(pkpy_is_none(vm, -1)); fail(pkpy_is_int(vm, -1)); @@ -177,15 +177,15 @@ int main(int argc, char** argv) { check(pkpy_is_none(vm, -1)); printf("\ntesting error catching\n"); - error(pkpy_vm_exec(vm, "let's make sure syntax errors get caught")); + error(pkpy_vm_run(vm, "let's make sure syntax errors get caught")); check(pkpy_stack_size(vm) == 0); //stack should be cleared after error is resolved printf("\ntesting calls\n"); - check(pkpy_vm_exec(vm, "def x(x, y) : return x - y")); - check(pkpy_vm_exec(vm, "def vararg_x(*x) : return sum(x)")); - check(pkpy_vm_exec(vm, "def keyword_x(x=1, y=1) : return x+y")); - check(pkpy_vm_exec(vm, "def retmany_x() : return 1, 2, 3")); + check(pkpy_vm_run(vm, "def x(x, y) : return x - y")); + check(pkpy_vm_run(vm, "def vararg_x(*x) : return sum(x)")); + check(pkpy_vm_run(vm, "def keyword_x(x=1, y=1) : return x+y")); + check(pkpy_vm_run(vm, "def retmany_x() : return 1, 2, 3")); check(pkpy_get_global(vm, "x")); check(pkpy_push_int(vm, 2)); @@ -231,28 +231,28 @@ int main(int argc, char** argv) { check(pkpy_get_global(vm, "x")); error(pkpy_call(vm, 0)); - check(pkpy_vm_exec(vm, "l = []")); + check(pkpy_vm_run(vm, "l = []")); check(pkpy_get_global(vm, "l")); check(pkpy_push_string(vm, "hello")); check(pkpy_call_method(vm, "append", 1)); - check(pkpy_vm_exec(vm, "print(l)")); + check(pkpy_vm_run(vm, "print(l)")); printf("\ntesting pushing functions\n"); check(pkpy_push_function(vm, test_binding)); check(pkpy_set_global(vm, "test_binding")); - check(pkpy_vm_exec(vm, "print(test_binding())")); + check(pkpy_vm_run(vm, "print(test_binding())")); check(pkpy_push_function(vm, test_multiple_return)); check(pkpy_set_global(vm, "test_multiple_return")); - check(pkpy_vm_exec(vm, "test_multiple_return()")); + check(pkpy_vm_run(vm, "test_multiple_return()")); check(pkpy_stack_size(vm) == 2); check(pkpy_push_function(vm, test_error_propagate)); check(pkpy_set_global(vm, "test_error_propagate")); - error(pkpy_vm_exec(vm, "test_error_propagate()")); + error(pkpy_vm_run(vm, "test_error_propagate()")); check(pkpy_get_global(vm, "test_multiple_return")); check(pkpy_call(vm, 0)); diff --git a/run_c_binding_test.sh b/run_c_binding_test.sh index e92b1439..c98603d8 100644 --- a/run_c_binding_test.sh +++ b/run_c_binding_test.sh @@ -15,7 +15,7 @@ clang++ -o c_binding_test test.o pocketpy_c.o -stdlib=libc++ -fsanitize=address echo "running, no weird output should show up" ./c_binding_test > binding_test_scratch echo "checking results (they should be identical)" -diff -s binding_test_scratch c_bindings/test_answers.txt +diff -q -s binding_test_scratch c_bindings/test_answers.txt echo "cleaning up" rm pocketpy_c.o diff --git a/src/error.h b/src/error.h index a216b8a9..41bbd8be 100644 --- a/src/error.h +++ b/src/error.h @@ -102,3 +102,4 @@ public: }; } // namespace pkpy + diff --git a/src/frame.h b/src/frame.h index d0facbae..039f0e88 100644 --- a/src/frame.h +++ b/src/frame.h @@ -219,3 +219,4 @@ struct Frame { }; }; // namespace pkpy + diff --git a/src/main.cpp b/src/main.cpp index 2e7f94a1..ca204d0a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,21 +6,21 @@ #ifndef __EMSCRIPTEN__ int main(int argc, char** argv){ - pkpy::VM* vm = new pkpy::VM(); + pkpy::VM* vm = pkpy_new_vm(); vm->bind_builtin_func<0>("input", [](pkpy::VM* vm, pkpy::ArgsView args){ return VAR(pkpy::getline()); }); if(argc == 1){ - pkpy::REPL* repl = new pkpy::REPL(vm); + pkpy::REPL* repl = pkpy_new_repl(vm); bool need_more_lines = false; while(true){ (*vm->_stdout) << (need_more_lines ? "... " : ">>> "); bool eof = false; std::string line = pkpy::getline(&eof); if(eof) break; - need_more_lines = repl->input(line.c_str()); + need_more_lines = pkpy_repl_input(repl, line.c_str()); } - delete vm; + pkpy_delete(vm); return 0; } @@ -47,7 +47,7 @@ int main(int argc, char** argv){ pkpy::PyObject* ret = nullptr; ret = vm->exec(src.c_str(), argv_1, pkpy::EXEC_MODE); - delete vm; + pkpy_delete(vm); return ret != nullptr ? 0 : 1; } @@ -57,3 +57,4 @@ __HELP: } #endif + diff --git a/src/pocketpy.h b/src/pocketpy.h index 957d2cb4..5373019e 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -977,7 +977,6 @@ inline void VM::post_init(){ } // namespace pkpy /*************************GLOBAL NAMESPACE*************************/ -/* static std::map _pk_deleter_map; extern "C" { @@ -1050,4 +1049,5 @@ extern "C" { return strdup(json.c_str()); } -}*/ +} + diff --git a/src/str.h b/src/str.h index 72a6235d..17434014 100644 --- a/src/str.h +++ b/src/str.h @@ -435,3 +435,4 @@ const StrName __or__ = StrName::get("__or__"); const StrName __xor__ = StrName::get("__xor__"); } // namespace pkpy + diff --git a/src/vm.h b/src/vm.h index 97951a18..1603379b 100644 --- a/src/vm.h +++ b/src/vm.h @@ -1205,3 +1205,4 @@ inline Str obj_type_name(VM *vm, Type type){ #undef PY_VAR_FLOAT } // namespace pkpy + From fbfb860a840a27c2148063a39a2883c49ab0edc2 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Mon, 1 May 2023 00:04:44 -0600 Subject: [PATCH 10/26] more cleanup --- src/error.h | 1 - src/str.h | 1 - src/vm.h | 1 - 3 files changed, 3 deletions(-) diff --git a/src/error.h b/src/error.h index 41bbd8be..a216b8a9 100644 --- a/src/error.h +++ b/src/error.h @@ -102,4 +102,3 @@ public: }; } // namespace pkpy - diff --git a/src/str.h b/src/str.h index 17434014..72a6235d 100644 --- a/src/str.h +++ b/src/str.h @@ -435,4 +435,3 @@ const StrName __or__ = StrName::get("__or__"); const StrName __xor__ = StrName::get("__xor__"); } // namespace pkpy - diff --git a/src/vm.h b/src/vm.h index 1603379b..97951a18 100644 --- a/src/vm.h +++ b/src/vm.h @@ -1205,4 +1205,3 @@ inline Str obj_type_name(VM *vm, Type type){ #undef PY_VAR_FLOAT } // namespace pkpy - From e0dc3fd9e25d610b866037a8b2a91db55ee78f53 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Mon, 1 May 2023 00:08:56 -0600 Subject: [PATCH 11/26] cleanup error.h --- src/error.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/error.h b/src/error.h index a216b8a9..41bbd8be 100644 --- a/src/error.h +++ b/src/error.h @@ -102,3 +102,4 @@ public: }; } // namespace pkpy + From 796930654f096c6694641636b263622b24583a5c Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Mon, 1 May 2023 00:10:36 -0600 Subject: [PATCH 12/26] try to clean up error.h again --- src/error.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/error.h b/src/error.h index 41bbd8be..bd7182cd 100644 --- a/src/error.h +++ b/src/error.h @@ -101,5 +101,4 @@ public: } }; -} // namespace pkpy - +} // namespace pkpy \ No newline at end of file From d57b9770747285cd1c72a8af5ea2ef2eca3946d8 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Mon, 1 May 2023 00:12:17 -0600 Subject: [PATCH 13/26] cleanup whitespace shuffling in several files --- src/main.cpp | 3 +-- src/obj.h | 2 +- src/pocketpy.h | 6 +++--- src/str.h | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index ca204d0a..282d4522 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -56,5 +56,4 @@ __HELP: return 0; } -#endif - +#endif \ No newline at end of file diff --git a/src/obj.h b/src/obj.h index c934edc3..a504b597 100644 --- a/src/obj.h +++ b/src/obj.h @@ -370,4 +370,4 @@ struct Py_ final: PyObject { }; -} // namespace pkpy +} // namespace pkpy \ No newline at end of file diff --git a/src/pocketpy.h b/src/pocketpy.h index 5373019e..076d2f47 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -5,6 +5,7 @@ #include "obj.h" #include "repl.h" #include "iter.h" +#include "base64.h" #include "cffi.h" #include "requests.h" #include "io.h" @@ -920,6 +921,7 @@ inline void VM::post_init(){ add_module_c(this); add_module_gc(this); add_module_random(this); + add_module_base64(this); for(const char* name: {"this", "functools", "collections", "heapq", "bisect"}){ _lazy_modules[name] = kPythonLibs[name]; @@ -1048,6 +1050,4 @@ extern "C" { std::string json = vm->read_output(); return strdup(json.c_str()); } - -} - +} \ No newline at end of file diff --git a/src/str.h b/src/str.h index 72a6235d..bbac8fb4 100644 --- a/src/str.h +++ b/src/str.h @@ -434,4 +434,4 @@ const StrName __and__ = StrName::get("__and__"); const StrName __or__ = StrName::get("__or__"); const StrName __xor__ = StrName::get("__xor__"); -} // namespace pkpy +} // namespace pkpy \ No newline at end of file From 669bf8b9b1336b0e8a87cfe5f857c6d88c980922 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Mon, 1 May 2023 20:35:38 -0600 Subject: [PATCH 14/26] adjusted things so that we minimize the number of changes necessary to the main pocketpy library --- c_bindings/pocketpy_c.cpp | 302 ++++++++++++++++++-------------------- c_bindings/pocketpy_c.h | 60 ++++---- c_bindings/test.c | 23 +-- run_c_binding_test.sh | 2 +- src/frame.h | 3 +- src/obj.h | 4 +- src/vm.h | 3 +- 7 files changed, 190 insertions(+), 207 deletions(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 50d0d4a6..8f213238 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -3,6 +3,8 @@ using namespace pkpy; +#define PKPY_STACK_SIZE 32 + #define SAFEGUARD_OPEN try { \ #define SAFEGUARD_CLOSE \ @@ -20,27 +22,32 @@ using namespace pkpy; #define ERRHANDLER_OPEN SAFEGUARD_OPEN \ try { \ - if (vm->c_data.top() == nullptr) \ + if (w->c_data->size() > 0 && w->c_data->top() == nullptr) \ return false; \ #define ERRHANDLER_CLOSE \ } catch( Exception e ) { \ - vm->c_data.push(VAR(e)); \ - vm->c_data.push(NULL); \ + w->c_data->push(py_var(w->vm, e)); \ + w->c_data->push(NULL); \ return false; \ } \ SAFEGUARD_CLOSE \ +struct pkpy_vm_wrapper { + VM* vm; + ValueStackImpl* c_data; +}; + //for now I will unpack a tuple automatically, we may not want to handle //it this way, not sure //it is more lua like, but maybe not python like -static void unpack_return(VM* vm, PyObject* ret) { - if (is_type(ret, vm->tp_tuple)) { - Tuple& t = CAST(Tuple&, ret); +static void unpack_return(struct pkpy_vm_wrapper* w, PyObject* ret) { + if (is_type(ret, w->vm->tp_tuple)) { + Tuple& t = py_cast(w->vm, ret); for (int i = 0; i < t.size(); i++) - vm->c_data.push(t[i]); - } else if (ret == vm->None) { + w->c_data->push(t[i]); + } else if (ret == w->vm->None) { //do nothing here //having to pop the stack after every call that returns none is annoying //lua does not do this @@ -49,66 +56,67 @@ static void unpack_return(VM* vm, PyObject* ret) { //if this becomes a problem we can change it // //you can still check if it returned none by comparing stack size before - //and after if you have too + //and after if you have to } else - vm->c_data.push(ret); + w->c_data->push(ret); } -bool pkpy_clear_error(pkpy_vm vm_handle, char** message) { - VM* vm = (VM*) vm_handle; +bool pkpy_clear_error(struct pkpy_vm_wrapper* w, char** message) { SAFEGUARD_OPEN - if (vm->c_data.top() != nullptr) + if (w->c_data->size() == 0 || w->c_data->top() != nullptr) return false; - vm->c_data.pop(); - Exception& e = CAST(Exception&, vm->c_data.top()); + w->c_data->pop(); + Exception& e = py_cast(w->vm, w->c_data->top()); if (message != nullptr) *message = e.summary().c_str_dup(); else std::cerr << "ERROR: " << e.summary() << "\n"; - vm->c_data.clear(); - vm->callstack.clear(); - vm->s_data.clear(); + w->c_data->clear(); + w->vm->callstack.clear(); + w->vm->s_data.clear(); return true; SAFEGUARD_CLOSE } -pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os) { - VM* vm = new VM(use_stdio, enable_os); +struct pkpy_vm_wrapper* pkpy_vm_create(bool use_stdio, bool enable_os) { + struct pkpy_vm_wrapper* w = (struct pkpy_vm_wrapper*) malloc(sizeof(*w)); + w->vm = new VM(use_stdio, enable_os); + w->c_data = new ValueStackImpl(); - return (pkpy_vm) vm; + return w; } -bool pkpy_vm_run(pkpy_vm vm_handle, const char* source) { - VM* vm = (VM*) vm_handle; +bool pkpy_vm_run(struct pkpy_vm_wrapper* w, const char* source) { ERRHANDLER_OPEN - CodeObject_ code = vm->compile(source, "", EXEC_MODE); - PyObject* result = vm->_exec(code, vm->_main); - unpack_return(vm, result); + CodeObject_ code = w->vm->compile(source, "", EXEC_MODE); + PyObject* result = w->vm->_exec(code, w->vm->_main); + unpack_return(w, result); return true; ERRHANDLER_CLOSE } -void pkpy_vm_destroy(pkpy_vm vm_handle) { - VM* vm = (VM*) vm_handle; - delete vm; +void pkpy_vm_destroy(struct pkpy_vm_wrapper* w) { + delete w->vm; + delete w->c_data; + free(w); } -static void propagate_if_errored(VM* vm) { +static void propagate_if_errored(struct pkpy_vm_wrapper* w) { try { - if (vm->c_data.top() != nullptr) + if (w->c_data->size() == 0 || w->c_data->top() != nullptr) return; - vm->c_data.pop(); - Exception& e = CAST(Exception&, vm->c_data.top()); - vm->c_data.pop(); + w->c_data->pop(); + Exception& e = py_cast(w->vm, w->c_data->top()); + w->c_data->pop(); throw e; } catch(Exception& e) { @@ -122,40 +130,41 @@ static void propagate_if_errored(VM* vm) { PyObject* c_function_wrapper(VM* vm, ArgsView args) { - LuaStyleFuncC f = CAST(NativeFunc&, args[-2])._lua_f; + LuaStyleFuncC f = py_cast(vm, args[-2])._lua_f; //setup c stack - int stored = vm->c_data.store(); + + struct pkpy_vm_wrapper w; + + ValueStackImpl c_stack = ValueStackImpl(); + w.vm = vm; + w.c_data = &c_stack; for (int i = 0; i < args.size(); i++) - vm->c_data.push(args[i]); + w.c_data->push(args[i]); - int retc = f(vm); + int retc = f(&w); - PyObject* ret = vm->None; - propagate_if_errored(vm); + PyObject* ret = w.vm->None; + propagate_if_errored(&w); if (retc == 1) - ret = vm->c_data.top(); + ret = w.c_data->top(); else if (retc > 1) { Tuple t = Tuple(retc); for (int i = 0; i < retc; i++) { - int stack_index = (vm->c_data.size() - retc) + i; - t[i] = vm->c_data.get(stack_index); + int stack_index = (w.c_data->size() - retc) + i; + t[i] = w.c_data->begin()[stack_index]; } - ret = VAR(t); + ret = py_var(w.vm, t); } - vm->c_data.clear(); - vm->c_data.restore(stored); - return ret; } -bool pkpy_push_function(pkpy_vm vm_handle, pkpy_function f) { - VM* vm = (VM*) vm_handle; +bool pkpy_push_function(struct pkpy_vm_wrapper* w, pkpy_function f) { ERRHANDLER_OPEN //TODO right now we just treat all c bound functions a varargs functions @@ -163,69 +172,58 @@ bool pkpy_push_function(pkpy_vm vm_handle, pkpy_function f) { NativeFunc nf = NativeFunc(c_function_wrapper, -1, 0); nf._lua_f = (LuaStyleFuncC) f; - vm->c_data.push(VAR(nf)); + w->c_data->push(py_var(w->vm, nf)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_int(pkpy_vm vm_handle, int value) { - VM* vm = (VM*) vm_handle; +bool pkpy_push_int(struct pkpy_vm_wrapper* w, int value) { ERRHANDLER_OPEN - vm->c_data.push(VAR(value)); + w->c_data->push(py_var(w->vm, value)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_float(pkpy_vm vm_handle, double value) { - VM* vm = (VM*) vm_handle; - +bool pkpy_push_float(struct pkpy_vm_wrapper* w, double value) { ERRHANDLER_OPEN - vm->c_data.push(VAR(value)); + w->c_data->push(py_var(w->vm, value)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_bool(pkpy_vm vm_handle, bool value) { - VM* vm = (VM*) vm_handle; - +bool pkpy_push_bool(struct pkpy_vm_wrapper* w, bool value) { ERRHANDLER_OPEN - vm->c_data.push(VAR(value)); + w->c_data->push(py_var(w->vm, value)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_string(pkpy_vm vm_handle, const char* value) { - VM* vm = (VM*) vm_handle; - +bool pkpy_push_string(struct pkpy_vm_wrapper* w, const char* value) { ERRHANDLER_OPEN - vm->c_data.push(VAR(value)); + w->c_data->push(py_var(w->vm, value)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_stringn(pkpy_vm vm_handle, const char* value, int length) { - VM* vm = (VM*) vm_handle; - +bool pkpy_push_stringn(struct pkpy_vm_wrapper* w, const char* value, int length) { ERRHANDLER_OPEN Str s = Str(value, length); - vm->c_data.push(VAR(s)); + w->c_data->push(py_var(w->vm, s)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_none(pkpy_vm vm_handle) { - VM* vm = (VM*) vm_handle; - +bool pkpy_push_none(struct pkpy_vm_wrapper* w) { ERRHANDLER_OPEN - vm->c_data.push(vm->None); + w->c_data->push(w->vm->None); return true; ERRHANDLER_CLOSE @@ -233,83 +231,77 @@ bool pkpy_push_none(pkpy_vm vm_handle) { -bool pkpy_set_global(pkpy_vm vm_handle, const char* name) { - VM* vm = (VM*) vm_handle; - +bool pkpy_set_global(struct pkpy_vm_wrapper* w, const char* name) { ERRHANDLER_OPEN - vm->_main->attr().set(name, vm->c_data.top()); + w->vm->_main->attr().set(name, w->c_data->top()); - vm->c_data.pop(); + w->c_data->pop(); return true; ERRHANDLER_CLOSE } //get global will also get bulitins -bool pkpy_get_global(pkpy_vm vm_handle, const char* name) { - VM* vm = (VM*) vm_handle; - +bool pkpy_get_global(struct pkpy_vm_wrapper* w, const char* name) { ERRHANDLER_OPEN - PyObject* o = vm->_main->attr().try_get(name); + PyObject* o = w->vm->_main->attr().try_get(name); if (o == nullptr) { - o = vm->builtins->attr().try_get(name); + o = w->vm->builtins->attr().try_get(name); if (o == nullptr) throw Exception("AttributeError", "could not find requested global"); } - vm->c_data.push(o); + w->c_data->push(o); return true; ERRHANDLER_CLOSE } -bool pkpy_call(pkpy_vm vm_handle, int argc) { - VM* vm = (VM*) vm_handle; +bool pkpy_call(struct pkpy_vm_wrapper* w, int argc) { ERRHANDLER_OPEN - int callable_index = vm->c_data.size() - argc - 1; + int callable_index = w->c_data->size() - argc - 1; - PyObject* callable = vm->c_data.get(callable_index); + PyObject* callable = w->c_data->begin()[callable_index]; - vm->s_data.push(callable); - vm->s_data.push(PY_NULL); + w->vm->s_data.push(callable); + w->vm->s_data.push(PY_NULL); for (int i = 0; i < argc; i++) - vm->s_data.push(vm->c_data.get(callable_index + i + 1)); + w->vm->s_data.push(w->c_data->begin()[callable_index + i + 1]); - PyObject* o = vm->vectorcall(argc); + PyObject* o = w->vm->vectorcall(argc); - vm->c_data.shrink(argc + 1); + w->c_data->shrink(argc + 1); - unpack_return(vm, o); + unpack_return(w, o); return true; ERRHANDLER_CLOSE } -bool pkpy_call_method(pkpy_vm vm_handle, const char* name, int argc) { - VM* vm = (VM*) vm_handle; +bool pkpy_call_method(struct pkpy_vm_wrapper* w, const char* name, int argc) { ERRHANDLER_OPEN - int self_index = vm->c_data.size() - argc - 1; - PyObject* self = vm->c_data.get(self_index); + int self_index = w->c_data->size() - argc - 1; + PyObject* self = w->c_data->begin()[self_index]; - PyObject* callable = vm->get_unbound_method(self, name, &self); + PyObject* callable = w->vm->get_unbound_method(self, name, &self); - vm->s_data.push(callable); - vm->s_data.push(self); + w->vm->s_data.push(callable); + w->vm->s_data.push(self); for (int i = 0; i < argc; i++) - vm->s_data.push(vm->c_data.get(self_index + i + 1)); + w->vm->s_data.push(w->c_data->begin()[self_index + i + 1]); - PyObject* o = vm->vectorcall(argc); + PyObject* o = w->vm->vectorcall(argc); - vm->c_data.shrink(argc + 1); + w->c_data->shrink(argc + 1); - unpack_return(vm, o); + unpack_return(w, o); return true; ERRHANDLER_CLOSE @@ -323,57 +315,53 @@ static int lua_to_cstack_index(int index, int size) { return index; } -bool pkpy_to_int(pkpy_vm vm_handle, int index, int* ret) { - VM* vm = (VM*) vm_handle; +bool pkpy_to_int(struct pkpy_vm_wrapper* w, int index, int* ret) { ERRHANDLER_OPEN - index = lua_to_cstack_index(index, vm->c_data.size()); + index = lua_to_cstack_index(index, w->c_data->size()); - PyObject* o = vm->c_data.get(index); + PyObject* o = w->c_data->begin()[index]; if (ret != nullptr) - *ret = py_cast(vm, o); + *ret = py_cast(w->vm, o); return true; ERRHANDLER_CLOSE } -bool pkpy_to_float(pkpy_vm vm_handle, int index, double* ret) { - VM* vm = (VM*) vm_handle; +bool pkpy_to_float(struct pkpy_vm_wrapper* w, int index, double* ret) { ERRHANDLER_OPEN - index = lua_to_cstack_index(index, vm->c_data.size()); + index = lua_to_cstack_index(index, w->c_data->size()); - PyObject* o = vm->c_data.get(index); + PyObject* o = w->c_data->begin()[index]; if (ret != nullptr) - *ret = py_cast(vm, o); + *ret = py_cast(w->vm, o); return true; ERRHANDLER_CLOSE } -bool pkpy_to_bool(pkpy_vm vm_handle, int index, bool* ret) { - VM* vm = (VM*) vm_handle; +bool pkpy_to_bool(struct pkpy_vm_wrapper* w, int index, bool* ret) { ERRHANDLER_OPEN - index = lua_to_cstack_index(index, vm->c_data.size()); + index = lua_to_cstack_index(index, w->c_data->size()); - PyObject* o = vm->c_data.get(index); + PyObject* o = w->c_data->begin()[index]; if (ret != nullptr) - *ret = py_cast(vm, o); + *ret = py_cast(w->vm, o); return true; ERRHANDLER_CLOSE } -bool pkpy_to_string(pkpy_vm vm_handle, int index, char** ret) { - VM* vm = (VM*) vm_handle; +bool pkpy_to_string(struct pkpy_vm_wrapper* w, int index, char** ret) { ERRHANDLER_OPEN - index = lua_to_cstack_index(index, vm->c_data.size()); + index = lua_to_cstack_index(index, w->c_data->size()); - PyObject* o = vm->c_data.get(index); + PyObject* o = w->c_data->begin()[index]; if (ret != nullptr) { - Str& s = CAST(Str&, o); + Str& s = py_cast(w->vm, o); *ret = s.c_str_dup(); } @@ -381,54 +369,46 @@ bool pkpy_to_string(pkpy_vm vm_handle, int index, char** ret) { ERRHANDLER_CLOSE } -bool pkpy_is_int(pkpy_vm vm_handle, int index) { - VM* vm = (VM*) vm_handle; +bool pkpy_is_int(struct pkpy_vm_wrapper* w, int index) { + index = lua_to_cstack_index(index, w->c_data->size()); + PyObject* o = w->c_data->begin()[index]; - index = lua_to_cstack_index(index, vm->c_data.size()); - PyObject* o = vm->c_data.get(index); - - return is_type(o, vm->tp_int); + return is_type(o, w->vm->tp_int); } -bool pkpy_is_float(pkpy_vm vm_handle, int index) { - VM* vm = (VM*) vm_handle; +bool pkpy_is_float(struct pkpy_vm_wrapper* w, int index) { + index = lua_to_cstack_index(index, w->c_data->size()); + PyObject* o = w->c_data->begin()[index]; - index = lua_to_cstack_index(index, vm->c_data.size()); - PyObject* o = vm->c_data.get(index); - - return is_type(o, vm->tp_float); + return is_type(o, w->vm->tp_float); } -bool pkpy_is_bool(pkpy_vm vm_handle, int index) { - VM* vm = (VM*) vm_handle; +bool pkpy_is_bool(struct pkpy_vm_wrapper* w, int index) { + index = lua_to_cstack_index(index, w->c_data->size()); + PyObject* o = w->c_data->begin()[index]; - index = lua_to_cstack_index(index, vm->c_data.size()); - PyObject* o = vm->c_data.get(index); - - return is_type(o, vm->tp_bool); + return is_type(o, w->vm->tp_bool); } -bool pkpy_is_string(pkpy_vm vm_handle, int index) { - VM* vm = (VM*) vm_handle; +bool pkpy_is_string(struct pkpy_vm_wrapper* w, int index) { + index = lua_to_cstack_index(index, w->c_data->size()); + PyObject* o = w->c_data->begin()[index]; - index = lua_to_cstack_index(index, vm->c_data.size()); - PyObject* o = vm->c_data.get(index); - - return is_type(o, vm->tp_str); + return is_type(o, w->vm->tp_str); } -bool pkpy_is_none(pkpy_vm vm_handle, int index) { - VM* vm = (VM*) vm_handle; +bool pkpy_is_none(struct pkpy_vm_wrapper* w, int index) { + index = lua_to_cstack_index(index, w->c_data->size()); + PyObject* o = w->c_data->begin()[index]; - index = lua_to_cstack_index(index, vm->c_data.size()); - PyObject* o = vm->c_data.get(index); - - return o == vm->None; + return o == w->vm->None; } -bool pkpy_check_stack(pkpy_vm vm_handle, int free) { - VM* vm = (VM*) vm_handle; - return free <= vm->c_data.remaining(); +bool pkpy_check_stack(struct pkpy_vm_wrapper* w, int free) { + return free + w->c_data->size() <= PKPY_STACK_SIZE; } -int pkpy_stack_size(pkpy_vm vm_handle) { - VM* vm = (VM*) vm_handle; - return vm->c_data.size(); +int pkpy_stack_size(struct pkpy_vm_wrapper* w) { + return w->c_data->size(); } +bool pkpy_pop(struct pkpy_vm_wrapper* w, int n) { + w->c_data->shrink(n); + return true; +} diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index d64af7c2..b1fda0fb 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -8,8 +8,7 @@ extern "C" { #include #include -typedef struct pkpy_vm_handle* pkpy_vm; -typedef struct pkpy_repl_hande* pkpy_repl; +typedef struct pkpy_vm_wrapper pkpy_vm; //we we take a lot of inspiration from the lua api for these bindings //the key difference being most methods return a bool, @@ -21,62 +20,65 @@ typedef struct pkpy_repl_hande* pkpy_repl; //it will provide a string summary of the error in the message parameter (if it is not NULL) //NOTE : you need to free the message that is passed back after you are done using it //or else pass in null as message, and it will just print the message to stderr -bool pkpy_clear_error(pkpy_vm, char** message); +bool pkpy_clear_error(pkpy_vm*, char** message); -pkpy_vm pkpy_vm_create(bool use_stdio, bool enable_os); -bool pkpy_vm_run(pkpy_vm, const char* source); -void pkpy_vm_destroy(pkpy_vm); +pkpy_vm* pkpy_vm_create(bool use_stdio, bool enable_os); +bool pkpy_vm_run(pkpy_vm*, const char* source); +void pkpy_vm_destroy(pkpy_vm*); -typedef int (*pkpy_function)(pkpy_vm); +typedef int (*pkpy_function)(pkpy_vm*); -bool pkpy_push_function(pkpy_vm, pkpy_function); -bool pkpy_push_int(pkpy_vm, int); -bool pkpy_push_float(pkpy_vm, double); -bool pkpy_push_bool(pkpy_vm, bool); -bool pkpy_push_string(pkpy_vm, const char*); -bool pkpy_push_stringn(pkpy_vm, const char*, int length); -bool pkpy_push_none(pkpy_vm); +bool pkpy_pop(pkpy_vm*, int n); -bool pkpy_set_global(pkpy_vm, const char* name); -bool pkpy_get_global(pkpy_vm, const char* name); +bool pkpy_push_function(pkpy_vm*, pkpy_function); +bool pkpy_push_int(pkpy_vm*, int); +bool pkpy_push_float(pkpy_vm*, double); +bool pkpy_push_bool(pkpy_vm*, bool); +bool pkpy_push_string(pkpy_vm*, const char*); +bool pkpy_push_stringn(pkpy_vm*, const char*, int length); +bool pkpy_push_none(pkpy_vm*); + +bool pkpy_set_global(pkpy_vm*, const char* name); +bool pkpy_get_global(pkpy_vm*, const char* name); //first push callable you want to call //then push the arguments to send //argc is the number of arguments that was pushed (not counting the callable) -bool pkpy_call(pkpy_vm, int argc); +bool pkpy_call(pkpy_vm*, int argc); //first push the object the method belongs to (self) //then push the the argments //argc is the number of arguments that was pushed (not counting the callable or self) //name is the name of the method to call on the object -bool pkpy_call_method(pkpy_vm, const char* name, int argc); +bool pkpy_call_method(pkpy_vm*, const char* name, int argc); //we will break with the lua api here //lua uses 1 as the index to the first pushed element for all of these functions //but we will start counting at zero to match python //we will allow negative numbers to count backwards from the top -bool pkpy_to_int(pkpy_vm, int index, int* ret); -bool pkpy_to_float(pkpy_vm, int index, double* ret); -bool pkpy_to_bool(pkpy_vm, int index, bool* ret); +bool pkpy_to_int(pkpy_vm*, int index, int* ret); +bool pkpy_to_float(pkpy_vm*, int index, double* ret); +bool pkpy_to_bool(pkpy_vm*, int index, bool* ret); //you have to free ret after you are done using it -bool pkpy_to_string(pkpy_vm, int index, char** ret); +bool pkpy_to_string(pkpy_vm*, int index, char** ret); //these do not follow the same error semantics as above, their return values //just say whether the check succeeded or not, or else return the value asked for -bool pkpy_is_int(pkpy_vm, int index); -bool pkpy_is_float(pkpy_vm, int index); -bool pkpy_is_bool(pkpy_vm, int index); -bool pkpy_is_string(pkpy_vm, int index); -bool pkpy_is_none(pkpy_vm, int index); +bool pkpy_is_int(pkpy_vm*, int index); +bool pkpy_is_float(pkpy_vm*, int index); +bool pkpy_is_bool(pkpy_vm*, int index); +bool pkpy_is_string(pkpy_vm*, int index); +bool pkpy_is_none(pkpy_vm*, int index); //will return true if at least free empty slots remain on the stack -bool pkpy_check_stack(pkpy_vm, int free); +bool pkpy_check_stack(pkpy_vm*, int free); //returns the number of elements on the stack -int pkpy_stack_size(pkpy_vm); +int pkpy_stack_size(pkpy_vm*); + #ifdef __cplusplus } diff --git a/c_bindings/test.c b/c_bindings/test.c index 8aa8cd76..e18bbb8e 100644 --- a/c_bindings/test.c +++ b/c_bindings/test.c @@ -4,7 +4,7 @@ //tests the c bindings for pocketpy -void check_impl(pkpy_vm vm, bool result, int lineno) { +void check_impl(pkpy_vm* vm, bool result, int lineno) { if (!result) { printf("ERROR: failed where it should have succeed at line %i\n", lineno); char* message; @@ -19,7 +19,7 @@ void check_impl(pkpy_vm vm, bool result, int lineno) { } } -void fail_impl(pkpy_vm vm, bool result, int lineno) { +void fail_impl(pkpy_vm* vm, bool result, int lineno) { if (result) { printf("ERROR: succeeded where it should have failed line %i\n", lineno); exit(1); @@ -33,7 +33,7 @@ void fail_impl(pkpy_vm vm, bool result, int lineno) { } } -void error_impl(pkpy_vm vm, bool result, int lineno) { +void error_impl(pkpy_vm* vm, bool result, int lineno) { if (result) { printf("ERROR: succeeded where it should have failed line %i\n", lineno); exit(1); @@ -54,28 +54,28 @@ void error_impl(pkpy_vm vm, bool result, int lineno) { #define fail(r) fail_impl(vm, (r), __LINE__) #define error(r) error_impl(vm, (r), __LINE__) -int test_binding(pkpy_vm vm) { +int test_binding(pkpy_vm* vm) { pkpy_push_int(vm, 12); return 1; } -int test_multiple_return(pkpy_vm vm) { +int test_multiple_return(pkpy_vm* vm) { pkpy_push_int(vm, 12); pkpy_push_int(vm, 13); return 2; } -int test_return_none(pkpy_vm vm) { +int test_return_none(pkpy_vm* vm) { return 0; } -int test_error_propagate(pkpy_vm vm) { +int test_error_propagate(pkpy_vm* vm) { pkpy_get_global(vm, "does not exist"); return 1; } -pkpy_vm vm; +pkpy_vm* vm; void cleanup(void) { pkpy_vm_destroy(vm); @@ -163,8 +163,8 @@ int main(int argc, char** argv) { int stack_size = pkpy_stack_size(vm); printf("stack size %i\n", stack_size); check(pkpy_check_stack(vm, 10)); - check(pkpy_check_stack(vm, 251)); - fail(pkpy_check_stack(vm, 252)); + check(pkpy_check_stack(vm, 27)); + fail(pkpy_check_stack(vm, 28)); check(pkpy_is_int(vm, 0)); check(pkpy_is_float(vm, 1)); check(pkpy_is_bool(vm, 2)); @@ -258,5 +258,8 @@ int main(int argc, char** argv) { check(pkpy_call(vm, 0)); check(pkpy_stack_size(vm) == 2); + check(pkpy_pop(vm, 2)); + check(pkpy_stack_size(vm) == 0); + return 0; } diff --git a/run_c_binding_test.sh b/run_c_binding_test.sh index c98603d8..33018288 100644 --- a/run_c_binding_test.sh +++ b/run_c_binding_test.sh @@ -18,7 +18,7 @@ echo "checking results (they should be identical)" diff -q -s binding_test_scratch c_bindings/test_answers.txt echo "cleaning up" -rm pocketpy_c.o +#rm pocketpy_c.o rm test.o rm binding_test_scratch diff --git a/src/frame.h b/src/frame.h index e3705b66..c5f0164a 100644 --- a/src/frame.h +++ b/src/frame.h @@ -190,5 +190,4 @@ struct Frame { } }; -}; // namespace pkpy - +}; // namespace pkpy \ No newline at end of file diff --git a/src/obj.h b/src/obj.h index a504b597..fbc67778 100644 --- a/src/obj.h +++ b/src/obj.h @@ -12,7 +12,7 @@ struct Function; class VM; typedef PyObject* (*NativeFuncC)(VM*, ArgsView); -typedef int (*LuaStyleFuncC)(VM*); +typedef int (*LuaStyleFuncC)(void*); struct NativeFunc { NativeFuncC f; @@ -370,4 +370,4 @@ struct Py_ final: PyObject { }; -} // namespace pkpy \ No newline at end of file +} // namespace pkpy diff --git a/src/vm.h b/src/vm.h index 97951a18..3d6db7eb 100644 --- a/src/vm.h +++ b/src/vm.h @@ -78,7 +78,6 @@ class VM { public: ManagedHeap heap; ValueStack s_data; - CVirtualStack c_data; stack< Frame > callstack; std::vector _all_types; @@ -1204,4 +1203,4 @@ inline Str obj_type_name(VM *vm, Type type){ #undef PY_VAR_INT #undef PY_VAR_FLOAT -} // namespace pkpy +} // namespace pkpy \ No newline at end of file From 87a76936d6ebcdefc2bd9f2e7aa1f34a15e1b799 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Mon, 1 May 2023 21:17:09 -0600 Subject: [PATCH 15/26] fixed test cases failing --- c_bindings/pocketpy_c.cpp | 6 +++++- c_bindings/test.c | 6 ++++-- c_bindings/test_answers.txt | 5 +++-- run_c_binding_test.sh | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 8f213238..dc14c2c6 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -97,7 +97,11 @@ bool pkpy_vm_run(struct pkpy_vm_wrapper* w, const char* source) { CodeObject_ code = w->vm->compile(source, "", EXEC_MODE); PyObject* result = w->vm->_exec(code, w->vm->_main); - unpack_return(w, result); + + //unpack_return(w, result); + //NOTE: it seems like w->vm->_exec should return whatever the last command it + //ran returned but instead it seems to pretty much always return None + //so I guess uncomment this line if that every changes return true; ERRHANDLER_CLOSE diff --git a/c_bindings/test.c b/c_bindings/test.c index e18bbb8e..a295e2ea 100644 --- a/c_bindings/test.c +++ b/c_bindings/test.c @@ -247,8 +247,10 @@ int main(int argc, char** argv) { check(pkpy_push_function(vm, test_multiple_return)); check(pkpy_set_global(vm, "test_multiple_return")); - check(pkpy_vm_run(vm, "test_multiple_return()")); - check(pkpy_stack_size(vm) == 2); + + //uncomment if _exec changes + //check(pkpy_vm_run(vm, "test_multiple_return()")); + //check(pkpy_stack_size(vm) == 2); check(pkpy_push_function(vm, test_error_propagate)); check(pkpy_set_global(vm, "test_error_propagate")); diff --git a/c_bindings/test_answers.txt b/c_bindings/test_answers.txt index 66be2403..ecce1763 100644 --- a/c_bindings/test_answers.txt +++ b/c_bindings/test_answers.txt @@ -45,5 +45,6 @@ TypeError: expected 2 positional arguments, but got 0 (x) testing pushing functions 12 -ERROR: failed where it should have succeed at line 251 -clear error reported everything was fine +successfully errored with this message: +Traceback (most recent call last): +AttributeError: could not find requested global diff --git a/run_c_binding_test.sh b/run_c_binding_test.sh index 33018288..c98603d8 100644 --- a/run_c_binding_test.sh +++ b/run_c_binding_test.sh @@ -18,7 +18,7 @@ echo "checking results (they should be identical)" diff -q -s binding_test_scratch c_bindings/test_answers.txt echo "cleaning up" -#rm pocketpy_c.o +rm pocketpy_c.o rm test.o rm binding_test_scratch From 54134bfecb9f919d3b314c0ffe8f425899401db3 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Mon, 1 May 2023 23:26:55 -0600 Subject: [PATCH 16/26] adjusted api so that api user is no longer responsible for freeing returned strings --- c_bindings/pocketpy_c.cpp | 15 +++++++++++++-- c_bindings/pocketpy_c.h | 7 +++---- c_bindings/test.c | 4 ---- src/gc.h | 2 +- src/tuplelist.h | 2 +- src/vm.h | 2 +- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index dc14c2c6..77baf496 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -36,6 +36,7 @@ using namespace pkpy; struct pkpy_vm_wrapper { VM* vm; ValueStackImpl* c_data; + char* string_ret; }; @@ -62,6 +63,13 @@ static void unpack_return(struct pkpy_vm_wrapper* w, PyObject* ret) { } +static char* manage_string(struct pkpy_vm_wrapper* w, char* s) { + if (w->string_ret != NULL) + free(w->string_ret); + w->string_ret = s; + return w->string_ret; +} + bool pkpy_clear_error(struct pkpy_vm_wrapper* w, char** message) { SAFEGUARD_OPEN @@ -72,7 +80,7 @@ bool pkpy_clear_error(struct pkpy_vm_wrapper* w, char** message) { w->c_data->pop(); Exception& e = py_cast(w->vm, w->c_data->top()); if (message != nullptr) - *message = e.summary().c_str_dup(); + *message = manage_string(w, e.summary().c_str_dup()); else std::cerr << "ERROR: " << e.summary() << "\n"; @@ -88,6 +96,7 @@ struct pkpy_vm_wrapper* pkpy_vm_create(bool use_stdio, bool enable_os) { struct pkpy_vm_wrapper* w = (struct pkpy_vm_wrapper*) malloc(sizeof(*w)); w->vm = new VM(use_stdio, enable_os); w->c_data = new ValueStackImpl(); + w->string_ret = NULL; return w; } @@ -110,6 +119,8 @@ bool pkpy_vm_run(struct pkpy_vm_wrapper* w, const char* source) { void pkpy_vm_destroy(struct pkpy_vm_wrapper* w) { delete w->vm; delete w->c_data; + if (w->string_ret != NULL) + free(w->string_ret); free(w); } @@ -366,7 +377,7 @@ bool pkpy_to_string(struct pkpy_vm_wrapper* w, int index, char** ret) { PyObject* o = w->c_data->begin()[index]; if (ret != nullptr) { Str& s = py_cast(w->vm, o); - *ret = s.c_str_dup(); + *ret = manage_string(w, s.c_str_dup()); } return true; diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index b1fda0fb..09c9660a 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -18,9 +18,9 @@ typedef struct pkpy_vm_wrapper pkpy_vm; //if pkpy_clear_error returns false it means that no error was set, and it takes no action //if pkpy_clear_error returns true it means there was an error and it was cleared, //it will provide a string summary of the error in the message parameter (if it is not NULL) -//NOTE : you need to free the message that is passed back after you are done using it -//or else pass in null as message, and it will just print the message to stderr +//if null is passed in as message, and it will just print the message to stderr bool pkpy_clear_error(pkpy_vm*, char** message); +//the message pointer is only valid until the next api call, so copy it if you want it pkpy_vm* pkpy_vm_create(bool use_stdio, bool enable_os); bool pkpy_vm_run(pkpy_vm*, const char* source); @@ -60,9 +60,8 @@ bool pkpy_call_method(pkpy_vm*, const char* name, int argc); bool pkpy_to_int(pkpy_vm*, int index, int* ret); bool pkpy_to_float(pkpy_vm*, int index, double* ret); bool pkpy_to_bool(pkpy_vm*, int index, bool* ret); -//you have to free ret after you are done using it bool pkpy_to_string(pkpy_vm*, int index, char** ret); - +//the ret string pointer is only valid until the next api call, so copy it if you want it //these do not follow the same error semantics as above, their return values //just say whether the check succeeded or not, or else return the value asked for diff --git a/c_bindings/test.c b/c_bindings/test.c index a295e2ea..75bcf694 100644 --- a/c_bindings/test.c +++ b/c_bindings/test.c @@ -14,7 +14,6 @@ void check_impl(pkpy_vm* vm, bool result, int lineno) { } printf("%s\n", message); - free(message); exit(1); } } @@ -27,7 +26,6 @@ void fail_impl(pkpy_vm* vm, bool result, int lineno) { char* message; if (pkpy_clear_error(vm, &message)) { printf("actually errored!\n"); - free(message); exit(1); } } @@ -44,7 +42,6 @@ void error_impl(pkpy_vm* vm, bool result, int lineno) { else { printf("successfully errored with this message: \n"); printf("%s\n", message); - free(message); } } } @@ -146,7 +143,6 @@ int main(int argc, char** argv) { fail(pkpy_is_float(vm, -1)); fail(pkpy_is_bool(vm, -1)); fail(pkpy_is_none(vm, -1)); - free(r_string); printf("\ntesting None methods\n"); check(pkpy_push_none(vm)); diff --git a/src/gc.h b/src/gc.h index 07567b16..64e86064 100644 --- a/src/gc.h +++ b/src/gc.h @@ -116,4 +116,4 @@ inline void FuncDecl::_gc_mark() const{ for(int i=0; i Date: Tue, 2 May 2023 00:35:22 -0600 Subject: [PATCH 17/26] added operations for working with void* as well as a way to check if a global exists --- c_bindings/pocketpy_c.cpp | 42 +++++++++++++++++++++++++++++++++++++ c_bindings/pocketpy_c.h | 7 +++++++ c_bindings/test.c | 41 +++++++++++++++++++++++++++++------- c_bindings/test_answers.txt | 6 +++++- run_c_binding_test.sh | 2 +- src/cffi.h | 2 +- 6 files changed, 90 insertions(+), 10 deletions(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 77baf496..6bdbe9c6 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -236,6 +236,14 @@ bool pkpy_push_stringn(struct pkpy_vm_wrapper* w, const char* value, int length) ERRHANDLER_CLOSE } +bool pkpy_push_voidp(struct pkpy_vm_wrapper* w, void* value) { + ERRHANDLER_OPEN + w->c_data->push(py_var(w->vm, value)); + + return true; + ERRHANDLER_CLOSE +} + bool pkpy_push_none(struct pkpy_vm_wrapper* w) { ERRHANDLER_OPEN w->c_data->push(w->vm->None); @@ -369,6 +377,19 @@ bool pkpy_to_bool(struct pkpy_vm_wrapper* w, int index, bool* ret) { ERRHANDLER_CLOSE } +bool pkpy_to_voidp(pkpy_vm_wrapper* w, int index, void** ret) { + ERRHANDLER_OPEN + + index = lua_to_cstack_index(index, w->c_data->size()); + + PyObject* o = w->c_data->begin()[index]; + if (ret != nullptr) + *ret = py_cast(w->vm, o); + + return true; + ERRHANDLER_CLOSE +} + bool pkpy_to_string(struct pkpy_vm_wrapper* w, int index, char** ret) { ERRHANDLER_OPEN @@ -408,6 +429,13 @@ bool pkpy_is_string(struct pkpy_vm_wrapper* w, int index) { return is_type(o, w->vm->tp_str); } +bool pkpy_is_voidp(struct pkpy_vm_wrapper* w, int index) { + index = lua_to_cstack_index(index, w->c_data->size()); + PyObject* o = w->c_data->begin()[index]; + + return is_type(o, VoidP::_type(w->vm)); +} + bool pkpy_is_none(struct pkpy_vm_wrapper* w, int index) { index = lua_to_cstack_index(index, w->c_data->size()); PyObject* o = w->c_data->begin()[index]; @@ -415,6 +443,20 @@ bool pkpy_is_none(struct pkpy_vm_wrapper* w, int index) { return o == w->vm->None; } +bool pkpy_check_global(pkpy_vm_wrapper* w, const char* name) { + SAFEGUARD_OPEN + PyObject* o = w->vm->_main->attr().try_get(name); + if (o == nullptr) { + o = w->vm->builtins->attr().try_get(name); + if (o == nullptr) + return false; + } + return true; + + SAFEGUARD_CLOSE +} + + bool pkpy_check_stack(struct pkpy_vm_wrapper* w, int free) { return free + w->c_data->size() <= PKPY_STACK_SIZE; } diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index 09c9660a..42a75c15 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -36,6 +36,7 @@ bool pkpy_push_float(pkpy_vm*, double); bool pkpy_push_bool(pkpy_vm*, bool); bool pkpy_push_string(pkpy_vm*, const char*); bool pkpy_push_stringn(pkpy_vm*, const char*, int length); +bool pkpy_push_voidp(pkpy_vm*, void*); bool pkpy_push_none(pkpy_vm*); bool pkpy_set_global(pkpy_vm*, const char* name); @@ -60,6 +61,7 @@ bool pkpy_call_method(pkpy_vm*, const char* name, int argc); bool pkpy_to_int(pkpy_vm*, int index, int* ret); bool pkpy_to_float(pkpy_vm*, int index, double* ret); bool pkpy_to_bool(pkpy_vm*, int index, bool* ret); +bool pkpy_to_voidp(pkpy_vm*, int index, void** ret); bool pkpy_to_string(pkpy_vm*, int index, char** ret); //the ret string pointer is only valid until the next api call, so copy it if you want it @@ -70,8 +72,13 @@ bool pkpy_is_int(pkpy_vm*, int index); bool pkpy_is_float(pkpy_vm*, int index); bool pkpy_is_bool(pkpy_vm*, int index); bool pkpy_is_string(pkpy_vm*, int index); +bool pkpy_is_voidp(pkpy_vm*, int index); bool pkpy_is_none(pkpy_vm*, int index); + +//will return true if global exists +bool pkpy_check_global(pkpy_vm*, const char* name); + //will return true if at least free empty slots remain on the stack bool pkpy_check_stack(pkpy_vm*, int free); diff --git a/c_bindings/test.c b/c_bindings/test.c index 75bcf694..4d237011 100644 --- a/c_bindings/test.c +++ b/c_bindings/test.c @@ -101,6 +101,7 @@ int main(int argc, char** argv) { fail(pkpy_is_bool(vm, -1)); fail(pkpy_is_string(vm, -1)); fail(pkpy_is_none(vm, -1)); + fail(pkpy_is_voidp(vm, -1)); printf("\ntesting float methods\n"); double r_float; @@ -115,6 +116,7 @@ int main(int argc, char** argv) { fail(pkpy_is_bool(vm, -1)); fail(pkpy_is_string(vm, -1)); fail(pkpy_is_none(vm, -1)); + fail(pkpy_is_voidp(vm, -1)); printf("\ntesting bool methods\n"); bool r_bool; @@ -129,6 +131,7 @@ int main(int argc, char** argv) { fail(pkpy_is_float(vm, -1)); fail(pkpy_is_string(vm, -1)); fail(pkpy_is_none(vm, -1)); + fail(pkpy_is_voidp(vm, -1)); printf("\ntesting string methods\n"); char* r_string; @@ -143,6 +146,7 @@ int main(int argc, char** argv) { fail(pkpy_is_float(vm, -1)); fail(pkpy_is_bool(vm, -1)); fail(pkpy_is_none(vm, -1)); + fail(pkpy_is_voidp(vm, -1)); printf("\ntesting None methods\n"); check(pkpy_push_none(vm)); @@ -154,23 +158,43 @@ int main(int argc, char** argv) { fail(pkpy_is_float(vm, -1)); fail(pkpy_is_bool(vm, -1)); fail(pkpy_is_string(vm, -1)); + fail(pkpy_is_voidp(vm, -1)); + + printf("\ntesting voidp methods\n"); + void* vp = (void*) 123; + check(pkpy_push_voidp(vm, vp)); + check(pkpy_set_global(vm, "vp")); + check(pkpy_vm_run(vm, "print(vp)")); + check(pkpy_get_global(vm, "vp")); + check(pkpy_is_voidp(vm, -1)); + vp = NULL; + check(pkpy_to_voidp(vm, -1, &vp)); + printf("%i\n", (int) (intptr_t) vp); + fail(pkpy_is_int(vm, -1)); + fail(pkpy_is_float(vm, -1)); + fail(pkpy_is_bool(vm, -1)); + fail(pkpy_is_string(vm, -1)); + fail(pkpy_is_none(vm, -1)); + printf("\ntesting sizing and indexing\n"); int stack_size = pkpy_stack_size(vm); printf("stack size %i\n", stack_size); check(pkpy_check_stack(vm, 10)); - check(pkpy_check_stack(vm, 27)); - fail(pkpy_check_stack(vm, 28)); + check(pkpy_check_stack(vm, 26)); + fail(pkpy_check_stack(vm, 27)); check(pkpy_is_int(vm, 0)); check(pkpy_is_float(vm, 1)); check(pkpy_is_bool(vm, 2)); check(pkpy_is_string(vm, 3)); check(pkpy_is_none(vm, 4)); - check(pkpy_is_int(vm, -5)); - check(pkpy_is_float(vm, -4)); - check(pkpy_is_bool(vm, -3)); - check(pkpy_is_string(vm, -2)); - check(pkpy_is_none(vm, -1)); + check(pkpy_is_voidp(vm, 5)); + check(pkpy_is_int(vm, -6)); + check(pkpy_is_float(vm, -5)); + check(pkpy_is_bool(vm, -4)); + check(pkpy_is_string(vm, -3)); + check(pkpy_is_none(vm, -2)); + check(pkpy_is_voidp(vm, -1)); printf("\ntesting error catching\n"); error(pkpy_vm_run(vm, "let's make sure syntax errors get caught")); @@ -259,5 +283,8 @@ int main(int argc, char** argv) { check(pkpy_pop(vm, 2)); check(pkpy_stack_size(vm) == 0); + check(pkpy_check_global(vm, "test_error_propagate")); + fail(pkpy_check_global(vm, "nonexistant")); + return 0; } diff --git a/c_bindings/test_answers.txt b/c_bindings/test_answers.txt index ecce1763..7a6c586e 100644 --- a/c_bindings/test_answers.txt +++ b/c_bindings/test_answers.txt @@ -22,8 +22,12 @@ hello testing None methods None +testing voidp methods + +123 + testing sizing and indexing -stack size 5 +stack size 6 testing error catching successfully errored with this message: diff --git a/run_c_binding_test.sh b/run_c_binding_test.sh index c98603d8..33018288 100644 --- a/run_c_binding_test.sh +++ b/run_c_binding_test.sh @@ -18,7 +18,7 @@ echo "checking results (they should be identical)" diff -q -s binding_test_scratch c_bindings/test_answers.txt echo "cleaning up" -rm pocketpy_c.o +#rm pocketpy_c.o rm test.o rm binding_test_scratch diff --git a/src/cffi.h b/src/cffi.h index 6f97688b..fbcc48aa 100644 --- a/src/cffi.h +++ b/src/cffi.h @@ -81,4 +81,4 @@ T py_pointer_cast(VM* vm, PyObject* var){ VoidP& p = CAST(VoidP&, var); return reinterpret_cast(p.ptr); } -} // namespace pkpy \ No newline at end of file +} // namespace pkpy From 5852ba743582fc70fabf1f617b7a48eca421d627 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Tue, 2 May 2023 22:08:18 -0600 Subject: [PATCH 18/26] switch to using subclass for c virtual machine, strings methods return strong references, except one new one which returns a weak refrence --- c_bindings/pocketpy_c.cpp | 377 ++++++++++++++++++++---------------- c_bindings/pocketpy_c.h | 14 +- c_bindings/test.c | 8 + c_bindings/test_answers.txt | 1 + 4 files changed, 231 insertions(+), 169 deletions(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 6bdbe9c6..fdbace28 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -22,33 +22,42 @@ using namespace pkpy; #define ERRHANDLER_OPEN SAFEGUARD_OPEN \ try { \ - if (w->c_data->size() > 0 && w->c_data->top() == nullptr) \ + if (vm->c_data->size() > 0 && vm->c_data->top() == nullptr) \ return false; \ #define ERRHANDLER_CLOSE \ } catch( Exception e ) { \ - w->c_data->push(py_var(w->vm, e)); \ - w->c_data->push(NULL); \ + vm->c_data->push(py_var(vm, e)); \ + vm->c_data->push(NULL); \ return false; \ } \ SAFEGUARD_CLOSE \ -struct pkpy_vm_wrapper { - VM* vm; + + +class CVM : public VM { + public : + ValueStackImpl* c_data; - char* string_ret; + CVM(bool use_stdio = true, bool enable_os=true) : VM(use_stdio, enable_os) { + c_data = new ValueStackImpl(); + } + + ~CVM() { + delete c_data; + } }; //for now I will unpack a tuple automatically, we may not want to handle //it this way, not sure //it is more lua like, but maybe not python like -static void unpack_return(struct pkpy_vm_wrapper* w, PyObject* ret) { - if (is_type(ret, w->vm->tp_tuple)) { - Tuple& t = py_cast(w->vm, ret); +static void unpack_return(CVM* vm, PyObject* ret) { + if (is_type(ret, vm->tp_tuple)) { + Tuple& t = py_cast(vm, ret); for (int i = 0; i < t.size(); i++) - w->c_data->push(t[i]); - } else if (ret == w->vm->None) { + vm->c_data->push(t[i]); + } else if (ret == vm->None) { //do nothing here //having to pop the stack after every call that returns none is annoying //lua does not do this @@ -59,56 +68,48 @@ static void unpack_return(struct pkpy_vm_wrapper* w, PyObject* ret) { //you can still check if it returned none by comparing stack size before //and after if you have to } else - w->c_data->push(ret); + vm->c_data->push(ret); } -static char* manage_string(struct pkpy_vm_wrapper* w, char* s) { - if (w->string_ret != NULL) - free(w->string_ret); - w->string_ret = s; - return w->string_ret; -} - -bool pkpy_clear_error(struct pkpy_vm_wrapper* w, char** message) { +bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) { + CVM* vm = (CVM*) vm_handle; SAFEGUARD_OPEN - if (w->c_data->size() == 0 || w->c_data->top() != nullptr) + if (vm->c_data->size() == 0 || vm->c_data->top() != nullptr) return false; - w->c_data->pop(); - Exception& e = py_cast(w->vm, w->c_data->top()); + vm->c_data->pop(); + Exception& e = py_cast(vm, vm->c_data->top()); if (message != nullptr) - *message = manage_string(w, e.summary().c_str_dup()); + *message = e.summary().c_str_dup(); else std::cerr << "ERROR: " << e.summary() << "\n"; - w->c_data->clear(); - w->vm->callstack.clear(); - w->vm->s_data.clear(); + vm->c_data->clear(); + vm->callstack.clear(); + vm->s_data.clear(); return true; SAFEGUARD_CLOSE } -struct pkpy_vm_wrapper* pkpy_vm_create(bool use_stdio, bool enable_os) { - struct pkpy_vm_wrapper* w = (struct pkpy_vm_wrapper*) malloc(sizeof(*w)); - w->vm = new VM(use_stdio, enable_os); - w->c_data = new ValueStackImpl(); - w->string_ret = NULL; - - return w; +pkpy_vm* pkpy_vm_create(bool use_stdio, bool enable_os) { + CVM* vm = new CVM(use_stdio, enable_os); + vm->c_data = new ValueStackImpl(); + return (pkpy_vm*) vm; } -bool pkpy_vm_run(struct pkpy_vm_wrapper* w, const char* source) { +bool pkpy_vm_run(pkpy_vm* vm_handle, const char* source) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - CodeObject_ code = w->vm->compile(source, "", EXEC_MODE); - PyObject* result = w->vm->_exec(code, w->vm->_main); + CodeObject_ code = vm->compile(source, "", EXEC_MODE); + PyObject* result = vm->_exec(code, vm->_main); //unpack_return(w, result); - //NOTE: it seems like w->vm->_exec should return whatever the last command it + //NOTE: it seems like vm->_exec should return whatever the last command it //ran returned but instead it seems to pretty much always return None //so I guess uncomment this line if that every changes @@ -116,22 +117,21 @@ bool pkpy_vm_run(struct pkpy_vm_wrapper* w, const char* source) { ERRHANDLER_CLOSE } -void pkpy_vm_destroy(struct pkpy_vm_wrapper* w) { - delete w->vm; - delete w->c_data; - if (w->string_ret != NULL) - free(w->string_ret); - free(w); +void pkpy_vm_destroy(pkpy_vm* vm_handle) { + CVM* vm = (CVM*) vm_handle; + delete vm; } -static void propagate_if_errored(struct pkpy_vm_wrapper* w) { +static void propagate_if_errored(CVM* vm, ValueStackImpl* stored_stack) { try { - if (w->c_data->size() == 0 || w->c_data->top() != nullptr) + if (vm->c_data->size() == 0 || vm->c_data->top() != nullptr) return; - w->c_data->pop(); - Exception& e = py_cast(w->vm, w->c_data->top()); - w->c_data->pop(); + vm->c_data->pop(); + Exception& e = py_cast(vm, vm->c_data->top()); + vm->c_data->pop(); + + vm->c_data = stored_stack; throw e; } catch(Exception& e) { @@ -143,43 +143,44 @@ static void propagate_if_errored(struct pkpy_vm_wrapper* w) { } } - PyObject* c_function_wrapper(VM* vm, ArgsView args) { LuaStyleFuncC f = py_cast(vm, args[-2])._lua_f; + CVM* cvm = (CVM*) vm; //setup c stack - - struct pkpy_vm_wrapper w; - - ValueStackImpl c_stack = ValueStackImpl(); - w.vm = vm; - w.c_data = &c_stack; + ValueStackImpl local_stack = ValueStackImpl(); for (int i = 0; i < args.size(); i++) - w.c_data->push(args[i]); + local_stack.push(args[i]); - int retc = f(&w); + ValueStackImpl* stored_stack = cvm->c_data; + cvm->c_data = &local_stack; - PyObject* ret = w.vm->None; - propagate_if_errored(&w); + int retc = f(cvm); + + propagate_if_errored(cvm, stored_stack); + cvm->c_data = stored_stack; + + PyObject* ret = cvm->None; if (retc == 1) - ret = w.c_data->top(); + ret = local_stack.top(); else if (retc > 1) { Tuple t = Tuple(retc); for (int i = 0; i < retc; i++) { - int stack_index = (w.c_data->size() - retc) + i; - t[i] = w.c_data->begin()[stack_index]; + int stack_index = (local_stack.size() - retc) + i; + t[i] = local_stack.begin()[stack_index]; } - ret = py_var(w.vm, t); + ret = py_var(cvm, t); } return ret; } -bool pkpy_push_function(struct pkpy_vm_wrapper* w, pkpy_function f) { +bool pkpy_push_function(pkpy_vm* vm_handle, pkpy_function f) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN //TODO right now we just treat all c bound functions a varargs functions @@ -187,66 +188,73 @@ bool pkpy_push_function(struct pkpy_vm_wrapper* w, pkpy_function f) { NativeFunc nf = NativeFunc(c_function_wrapper, -1, 0); nf._lua_f = (LuaStyleFuncC) f; - w->c_data->push(py_var(w->vm, nf)); + vm->c_data->push(py_var(vm, nf)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_int(struct pkpy_vm_wrapper* w, int value) { +bool pkpy_push_int(pkpy_vm* vm_handle, int value) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - w->c_data->push(py_var(w->vm, value)); + vm->c_data->push(py_var(vm, value)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_float(struct pkpy_vm_wrapper* w, double value) { +bool pkpy_push_float(pkpy_vm* vm_handle, double value) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - w->c_data->push(py_var(w->vm, value)); + vm->c_data->push(py_var(vm, value)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_bool(struct pkpy_vm_wrapper* w, bool value) { +bool pkpy_push_bool(pkpy_vm* vm_handle, bool value) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - w->c_data->push(py_var(w->vm, value)); + vm->c_data->push(py_var(vm, value)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_string(struct pkpy_vm_wrapper* w, const char* value) { +bool pkpy_push_string(pkpy_vm* vm_handle, const char* value) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - w->c_data->push(py_var(w->vm, value)); + vm->c_data->push(py_var(vm, value)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_stringn(struct pkpy_vm_wrapper* w, const char* value, int length) { +bool pkpy_push_stringn(pkpy_vm* vm_handle, const char* value, int length) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN Str s = Str(value, length); - w->c_data->push(py_var(w->vm, s)); + vm->c_data->push(py_var(vm, s)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_voidp(struct pkpy_vm_wrapper* w, void* value) { +bool pkpy_push_voidp(pkpy_vm* vm_handle, void* value) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - w->c_data->push(py_var(w->vm, value)); + vm->c_data->push(py_var(vm, value)); return true; ERRHANDLER_CLOSE } -bool pkpy_push_none(struct pkpy_vm_wrapper* w) { +bool pkpy_push_none(pkpy_vm* vm_handle) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - w->c_data->push(w->vm->None); + vm->c_data->push(vm->None); return true; ERRHANDLER_CLOSE @@ -254,77 +262,81 @@ bool pkpy_push_none(struct pkpy_vm_wrapper* w) { -bool pkpy_set_global(struct pkpy_vm_wrapper* w, const char* name) { +bool pkpy_set_global(pkpy_vm* vm_handle, const char* name) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - w->vm->_main->attr().set(name, w->c_data->top()); + vm->_main->attr().set(name, vm->c_data->top()); - w->c_data->pop(); + vm->c_data->pop(); return true; ERRHANDLER_CLOSE } //get global will also get bulitins -bool pkpy_get_global(struct pkpy_vm_wrapper* w, const char* name) { +bool pkpy_get_global(pkpy_vm* vm_handle, const char* name) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - PyObject* o = w->vm->_main->attr().try_get(name); + PyObject* o = vm->_main->attr().try_get(name); if (o == nullptr) { - o = w->vm->builtins->attr().try_get(name); + o = vm->builtins->attr().try_get(name); if (o == nullptr) throw Exception("AttributeError", "could not find requested global"); } - w->c_data->push(o); + vm->c_data->push(o); return true; ERRHANDLER_CLOSE } -bool pkpy_call(struct pkpy_vm_wrapper* w, int argc) { +bool pkpy_call(pkpy_vm* vm_handle, int argc) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - int callable_index = w->c_data->size() - argc - 1; + int callable_index = vm->c_data->size() - argc - 1; - PyObject* callable = w->c_data->begin()[callable_index]; + PyObject* callable = vm->c_data->begin()[callable_index]; - w->vm->s_data.push(callable); - w->vm->s_data.push(PY_NULL); + vm->s_data.push(callable); + vm->s_data.push(PY_NULL); for (int i = 0; i < argc; i++) - w->vm->s_data.push(w->c_data->begin()[callable_index + i + 1]); + vm->s_data.push(vm->c_data->begin()[callable_index + i + 1]); - PyObject* o = w->vm->vectorcall(argc); + PyObject* o = vm->vectorcall(argc); - w->c_data->shrink(argc + 1); + vm->c_data->shrink(argc + 1); - unpack_return(w, o); + unpack_return(vm, o); return true; ERRHANDLER_CLOSE } -bool pkpy_call_method(struct pkpy_vm_wrapper* w, const char* name, int argc) { +bool pkpy_call_method(pkpy_vm* vm_handle, const char* name, int argc) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - int self_index = w->c_data->size() - argc - 1; - PyObject* self = w->c_data->begin()[self_index]; + int self_index = vm->c_data->size() - argc - 1; + PyObject* self = vm->c_data->begin()[self_index]; - PyObject* callable = w->vm->get_unbound_method(self, name, &self); + PyObject* callable = vm->get_unbound_method(self, name, &self); - w->vm->s_data.push(callable); - w->vm->s_data.push(self); + vm->s_data.push(callable); + vm->s_data.push(self); for (int i = 0; i < argc; i++) - w->vm->s_data.push(w->c_data->begin()[self_index + i + 1]); + vm->s_data.push(vm->c_data->begin()[self_index + i + 1]); - PyObject* o = w->vm->vectorcall(argc); + PyObject* o = vm->vectorcall(argc); - w->c_data->shrink(argc + 1); + vm->c_data->shrink(argc + 1); - unpack_return(w, o); + unpack_return(vm, o); return true; ERRHANDLER_CLOSE @@ -338,116 +350,146 @@ static int lua_to_cstack_index(int index, int size) { return index; } -bool pkpy_to_int(struct pkpy_vm_wrapper* w, int index, int* ret) { +bool pkpy_to_int(pkpy_vm* vm_handle, int index, int* ret) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - index = lua_to_cstack_index(index, w->c_data->size()); + index = lua_to_cstack_index(index, vm->c_data->size()); - PyObject* o = w->c_data->begin()[index]; + PyObject* o = vm->c_data->begin()[index]; if (ret != nullptr) - *ret = py_cast(w->vm, o); + *ret = py_cast(vm, o); return true; ERRHANDLER_CLOSE } -bool pkpy_to_float(struct pkpy_vm_wrapper* w, int index, double* ret) { +bool pkpy_to_float(pkpy_vm* vm_handle, int index, double* ret) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - index = lua_to_cstack_index(index, w->c_data->size()); + index = lua_to_cstack_index(index, vm->c_data->size()); - PyObject* o = w->c_data->begin()[index]; + PyObject* o = vm->c_data->begin()[index]; if (ret != nullptr) - *ret = py_cast(w->vm, o); + *ret = py_cast(vm, o); return true; ERRHANDLER_CLOSE } -bool pkpy_to_bool(struct pkpy_vm_wrapper* w, int index, bool* ret) { +bool pkpy_to_bool(pkpy_vm* vm_handle, int index, bool* ret) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - index = lua_to_cstack_index(index, w->c_data->size()); + index = lua_to_cstack_index(index, vm->c_data->size()); - PyObject* o = w->c_data->begin()[index]; + PyObject* o = vm->c_data->begin()[index]; if (ret != nullptr) - *ret = py_cast(w->vm, o); + *ret = py_cast(vm, o); return true; ERRHANDLER_CLOSE } -bool pkpy_to_voidp(pkpy_vm_wrapper* w, int index, void** ret) { +bool pkpy_to_voidp(pkpy_vm* vm_handle, int index, void** ret) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - index = lua_to_cstack_index(index, w->c_data->size()); + index = lua_to_cstack_index(index, vm->c_data->size()); - PyObject* o = w->c_data->begin()[index]; + PyObject* o = vm->c_data->begin()[index]; if (ret != nullptr) - *ret = py_cast(w->vm, o); + *ret = py_cast(vm, o); return true; ERRHANDLER_CLOSE } -bool pkpy_to_string(struct pkpy_vm_wrapper* w, int index, char** ret) { +bool pkpy_to_string(pkpy_vm* vm_handle, int index, char** ret) { + CVM* vm = (CVM*) vm_handle; ERRHANDLER_OPEN - index = lua_to_cstack_index(index, w->c_data->size()); + index = lua_to_cstack_index(index, vm->c_data->size()); - PyObject* o = w->c_data->begin()[index]; + PyObject* o = vm->c_data->begin()[index]; if (ret != nullptr) { - Str& s = py_cast(w->vm, o); - *ret = manage_string(w, s.c_str_dup()); + Str& s = py_cast(vm, o); + *ret = s.c_str_dup(); } return true; ERRHANDLER_CLOSE } -bool pkpy_is_int(struct pkpy_vm_wrapper* w, int index) { - index = lua_to_cstack_index(index, w->c_data->size()); - PyObject* o = w->c_data->begin()[index]; +bool pkpy_to_stringn(pkpy_vm* vm_handle, int index, const char** ret, int* size) { + CVM* vm = (CVM*) vm_handle; + ERRHANDLER_OPEN - return is_type(o, w->vm->tp_int); -} -bool pkpy_is_float(struct pkpy_vm_wrapper* w, int index) { - index = lua_to_cstack_index(index, w->c_data->size()); - PyObject* o = w->c_data->begin()[index]; + index = lua_to_cstack_index(index, vm->c_data->size()); - return is_type(o, w->vm->tp_float); -} -bool pkpy_is_bool(struct pkpy_vm_wrapper* w, int index) { - index = lua_to_cstack_index(index, w->c_data->size()); - PyObject* o = w->c_data->begin()[index]; + PyObject* o = vm->c_data->begin()[index]; + if (ret != nullptr) { + std::string_view sv = py_cast(vm, o).sv(); + *ret = sv.data(); + *size = sv.size(); + } - return is_type(o, w->vm->tp_bool); -} -bool pkpy_is_string(struct pkpy_vm_wrapper* w, int index) { - index = lua_to_cstack_index(index, w->c_data->size()); - PyObject* o = w->c_data->begin()[index]; - - return is_type(o, w->vm->tp_str); -} -bool pkpy_is_voidp(struct pkpy_vm_wrapper* w, int index) { - index = lua_to_cstack_index(index, w->c_data->size()); - PyObject* o = w->c_data->begin()[index]; - - return is_type(o, VoidP::_type(w->vm)); + return true; + ERRHANDLER_CLOSE } -bool pkpy_is_none(struct pkpy_vm_wrapper* w, int index) { - index = lua_to_cstack_index(index, w->c_data->size()); - PyObject* o = w->c_data->begin()[index]; - return o == w->vm->None; +bool pkpy_is_int(pkpy_vm* vm_handle, int index) { + CVM* vm = (CVM*) vm_handle; + index = lua_to_cstack_index(index, vm->c_data->size()); + PyObject* o = vm->c_data->begin()[index]; + + return is_type(o, vm->tp_int); +} +bool pkpy_is_float(pkpy_vm* vm_handle, int index) { + CVM* vm = (CVM*) vm_handle; + index = lua_to_cstack_index(index, vm->c_data->size()); + PyObject* o = vm->c_data->begin()[index]; + + return is_type(o, vm->tp_float); +} +bool pkpy_is_bool(pkpy_vm* vm_handle, int index) { + CVM* vm = (CVM*) vm_handle; + index = lua_to_cstack_index(index, vm->c_data->size()); + PyObject* o = vm->c_data->begin()[index]; + + return is_type(o, vm->tp_bool); +} +bool pkpy_is_string(pkpy_vm* vm_handle, int index) { + CVM* vm = (CVM*) vm_handle; + index = lua_to_cstack_index(index, vm->c_data->size()); + PyObject* o = vm->c_data->begin()[index]; + + return is_type(o, vm->tp_str); +} +bool pkpy_is_voidp(pkpy_vm* vm_handle, int index) { + CVM* vm = (CVM*) vm_handle; + index = lua_to_cstack_index(index, vm->c_data->size()); + PyObject* o = vm->c_data->begin()[index]; + + return is_type(o, VoidP::_type(vm)); } -bool pkpy_check_global(pkpy_vm_wrapper* w, const char* name) { +bool pkpy_is_none(pkpy_vm* vm_handle, int index) { + CVM* vm = (CVM*) vm_handle; + index = lua_to_cstack_index(index, vm->c_data->size()); + PyObject* o = vm->c_data->begin()[index]; + + return o == vm->None; +} + +bool pkpy_check_global(pkpy_vm* vm_handle, const char* name) { + CVM* vm = (CVM*) vm_handle; SAFEGUARD_OPEN - PyObject* o = w->vm->_main->attr().try_get(name); + PyObject* o = vm->_main->attr().try_get(name); if (o == nullptr) { - o = w->vm->builtins->attr().try_get(name); + o = vm->builtins->attr().try_get(name); if (o == nullptr) return false; } @@ -457,15 +499,18 @@ bool pkpy_check_global(pkpy_vm_wrapper* w, const char* name) { } -bool pkpy_check_stack(struct pkpy_vm_wrapper* w, int free) { - return free + w->c_data->size() <= PKPY_STACK_SIZE; +bool pkpy_check_stack(pkpy_vm* vm_handle, int free) { + CVM* vm = (CVM*) vm_handle; + return free + vm->c_data->size() <= PKPY_STACK_SIZE; } -int pkpy_stack_size(struct pkpy_vm_wrapper* w) { - return w->c_data->size(); +int pkpy_stack_size(pkpy_vm* vm_handle) { + CVM* vm = (CVM*) vm_handle; + return vm->c_data->size(); } -bool pkpy_pop(struct pkpy_vm_wrapper* w, int n) { - w->c_data->shrink(n); +bool pkpy_pop(pkpy_vm* vm_handle, int n) { + CVM* vm = (CVM*) vm_handle; + vm->c_data->shrink(n); return true; } diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index 42a75c15..7aa0d017 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -8,7 +8,7 @@ extern "C" { #include #include -typedef struct pkpy_vm_wrapper pkpy_vm; +typedef struct pkpy_vm_handle pkpy_vm; //we we take a lot of inspiration from the lua api for these bindings //the key difference being most methods return a bool, @@ -20,7 +20,7 @@ typedef struct pkpy_vm_wrapper pkpy_vm; //it will provide a string summary of the error in the message parameter (if it is not NULL) //if null is passed in as message, and it will just print the message to stderr bool pkpy_clear_error(pkpy_vm*, char** message); -//the message pointer is only valid until the next api call, so copy it if you want it +//NOTE you are responsible for freeing message pkpy_vm* pkpy_vm_create(bool use_stdio, bool enable_os); bool pkpy_vm_run(pkpy_vm*, const char* source); @@ -62,8 +62,16 @@ bool pkpy_to_int(pkpy_vm*, int index, int* ret); bool pkpy_to_float(pkpy_vm*, int index, double* ret); bool pkpy_to_bool(pkpy_vm*, int index, bool* ret); bool pkpy_to_voidp(pkpy_vm*, int index, void** ret); + +//this method provides a strong reference, you are responsible for freeing the +//string when you are done with it bool pkpy_to_string(pkpy_vm*, int index, char** ret); -//the ret string pointer is only valid until the next api call, so copy it if you want it + +//this method provides a weak reference, it is only valid until the +//next api call +//it is not null terminated +bool pkpy_to_stringn(pkpy_vm*, int index, const char** ret, int* size); + //these do not follow the same error semantics as above, their return values //just say whether the check succeeded or not, or else return the value asked for diff --git a/c_bindings/test.c b/c_bindings/test.c index 4d237011..953a1a15 100644 --- a/c_bindings/test.c +++ b/c_bindings/test.c @@ -14,6 +14,7 @@ void check_impl(pkpy_vm* vm, bool result, int lineno) { } printf("%s\n", message); + free(message); exit(1); } } @@ -26,6 +27,7 @@ void fail_impl(pkpy_vm* vm, bool result, int lineno) { char* message; if (pkpy_clear_error(vm, &message)) { printf("actually errored!\n"); + free(message); exit(1); } } @@ -42,6 +44,7 @@ void error_impl(pkpy_vm* vm, bool result, int lineno) { else { printf("successfully errored with this message: \n"); printf("%s\n", message); + free(message); } } } @@ -142,6 +145,11 @@ int main(int argc, char** argv) { check(pkpy_is_string(vm, -1)); check(pkpy_to_string(vm, -1, &r_string)); printf("%s\n", r_string); + free(r_string); + const char* r_stringn; + int r_size; + check(pkpy_to_stringn(vm, -1, &r_stringn, &r_size)); + printf("%.*s\n", r_size, r_stringn); fail(pkpy_is_int(vm, -1)); fail(pkpy_is_float(vm, -1)); fail(pkpy_is_bool(vm, -1)); diff --git a/c_bindings/test_answers.txt b/c_bindings/test_answers.txt index 7a6c586e..6a90c3ff 100644 --- a/c_bindings/test_answers.txt +++ b/c_bindings/test_answers.txt @@ -18,6 +18,7 @@ False testing string methods hello! hello +hello testing None methods None From 2c96dbe7ea0fe3e712cf6f3997e595cafc5d8d74 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Tue, 2 May 2023 22:18:03 -0600 Subject: [PATCH 19/26] added hook into garbage collector --- c_bindings/pocketpy_c.cpp | 6 ++++++ run_c_binding_test.sh | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index fdbace28..526910dc 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -44,6 +44,7 @@ class CVM : public VM { } ~CVM() { + c_data->clear(); delete c_data; } }; @@ -95,9 +96,14 @@ bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) { SAFEGUARD_CLOSE } +void gc_marker_ex(CVM* vm) { + for(PyObject* obj: *vm->c_data) if(obj!=nullptr) OBJ_MARK(obj); +} + pkpy_vm* pkpy_vm_create(bool use_stdio, bool enable_os) { CVM* vm = new CVM(use_stdio, enable_os); vm->c_data = new ValueStackImpl(); + vm->_gc_marker_ex = (void (*)(VM*)) gc_marker_ex; return (pkpy_vm*) vm; } diff --git a/run_c_binding_test.sh b/run_c_binding_test.sh index 33018288..4afb34cd 100644 --- a/run_c_binding_test.sh +++ b/run_c_binding_test.sh @@ -12,7 +12,8 @@ echo "compiling c executable" clang -c -o test.o c_bindings/test.c -Wfatal-errors -O2 -Wall -Wno-sign-compare -Wno-unused-variable -I src/ -fsanitize=address -g echo "linking" clang++ -o c_binding_test test.o pocketpy_c.o -stdlib=libc++ -fsanitize=address -g -echo "running, no weird output should show up" +echo "running, leaksanitizer is finding a false postive leak in the CVM constructor" +echo "ignore that but pay attention to anything else" ./c_binding_test > binding_test_scratch echo "checking results (they should be identical)" diff -q -s binding_test_scratch c_bindings/test_answers.txt From ec7fefe3026a37fceb272fdb7ac74ac466f6e71c Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Tue, 2 May 2023 22:23:25 -0600 Subject: [PATCH 20/26] cleanup line endings --- src/cffi.h | 2 +- src/gc.h | 2 +- src/obj.h | 2 +- src/tuplelist.h | 2 +- src/vm.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cffi.h b/src/cffi.h index fbcc48aa..6f97688b 100644 --- a/src/cffi.h +++ b/src/cffi.h @@ -81,4 +81,4 @@ T py_pointer_cast(VM* vm, PyObject* var){ VoidP& p = CAST(VoidP&, var); return reinterpret_cast(p.ptr); } -} // namespace pkpy +} // namespace pkpy \ No newline at end of file diff --git a/src/gc.h b/src/gc.h index 64e86064..07567b16 100644 --- a/src/gc.h +++ b/src/gc.h @@ -116,4 +116,4 @@ inline void FuncDecl::_gc_mark() const{ for(int i=0; i final: PyObject { }; -} // namespace pkpy +} // namespace pkpy \ No newline at end of file diff --git a/src/tuplelist.h b/src/tuplelist.h index 77fe62b9..1403c504 100644 --- a/src/tuplelist.h +++ b/src/tuplelist.h @@ -93,4 +93,4 @@ struct ArgsView{ return ret; } }; -} // namespace pkpy +} // namespace pkpy \ No newline at end of file diff --git a/src/vm.h b/src/vm.h index 55209a24..f4e88bc3 100644 --- a/src/vm.h +++ b/src/vm.h @@ -1202,4 +1202,4 @@ inline Str obj_type_name(VM *vm, Type type){ #undef PY_VAR_INT #undef PY_VAR_FLOAT -} // namespace pkpy +} // namespace pkpy \ No newline at end of file From cbe99e608966837dfb14f2fbec8eeddd1a8676a5 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Tue, 2 May 2023 22:29:06 -0600 Subject: [PATCH 21/26] set the void* in LuaStyleFuncC back to a VM* --- src/obj.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obj.h b/src/obj.h index 69dd6e51..a504b597 100644 --- a/src/obj.h +++ b/src/obj.h @@ -12,7 +12,7 @@ struct Function; class VM; typedef PyObject* (*NativeFuncC)(VM*, ArgsView); -typedef int (*LuaStyleFuncC)(void*); +typedef int (*LuaStyleFuncC)(VM*); struct NativeFunc { NativeFuncC f; From 3d8627410f05ce16042daac03be9cbebd7d7be96 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Tue, 2 May 2023 23:11:41 -0600 Subject: [PATCH 22/26] added a function to check for errors without clearing them --- c_bindings/pocketpy_c.cpp | 9 +++++++++ c_bindings/pocketpy_c.h | 3 +++ c_bindings/test.c | 2 ++ run_c_binding_test.sh | 2 +- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 526910dc..0b196a0e 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -504,6 +504,15 @@ bool pkpy_check_global(pkpy_vm* vm_handle, const char* name) { SAFEGUARD_CLOSE } +bool pkpy_check_error(pkpy_vm* vm_handle) { + CVM* vm = (CVM*) vm_handle; + SAFEGUARD_OPEN + if (vm->c_data->size() > 0 && vm->c_data->top() == nullptr) + return true; + return false; + SAFEGUARD_CLOSE +} + bool pkpy_check_stack(pkpy_vm* vm_handle, int free) { CVM* vm = (CVM*) vm_handle; diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h index 7aa0d017..7924d75c 100644 --- a/c_bindings/pocketpy_c.h +++ b/c_bindings/pocketpy_c.h @@ -87,6 +87,9 @@ bool pkpy_is_none(pkpy_vm*, int index); //will return true if global exists bool pkpy_check_global(pkpy_vm*, const char* name); +//will return true if the vm is currently in an error state +bool pkpy_check_error(pkpy_vm*); + //will return true if at least free empty slots remain on the stack bool pkpy_check_stack(pkpy_vm*, int free); diff --git a/c_bindings/test.c b/c_bindings/test.c index 953a1a15..86adf20e 100644 --- a/c_bindings/test.c +++ b/c_bindings/test.c @@ -293,6 +293,8 @@ int main(int argc, char** argv) { check(pkpy_check_global(vm, "test_error_propagate")); fail(pkpy_check_global(vm, "nonexistant")); + pkpy_vm_run(vm, "test_error_propagate()"); + check(pkpy_check_error(vm)); return 0; } diff --git a/run_c_binding_test.sh b/run_c_binding_test.sh index 4afb34cd..b3078724 100644 --- a/run_c_binding_test.sh +++ b/run_c_binding_test.sh @@ -19,7 +19,7 @@ echo "checking results (they should be identical)" diff -q -s binding_test_scratch c_bindings/test_answers.txt echo "cleaning up" -#rm pocketpy_c.o +rm pocketpy_c.o rm test.o rm binding_test_scratch From f4d2c401f76b6fe114b9715a4e98667edd35b011 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Fri, 5 May 2023 18:46:20 -0600 Subject: [PATCH 23/26] adjusted to change in vm constructor --- c_bindings/pocketpy_c.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 0b196a0e..1c249633 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -39,7 +39,7 @@ class CVM : public VM { public : ValueStackImpl* c_data; - CVM(bool use_stdio = true, bool enable_os=true) : VM(use_stdio, enable_os) { + CVM(bool enable_os=true) : VM(enable_os) { c_data = new ValueStackImpl(); } @@ -100,10 +100,23 @@ void gc_marker_ex(CVM* vm) { for(PyObject* obj: *vm->c_data) if(obj!=nullptr) OBJ_MARK(obj); } + +static void noop_output_handler(VM* vm, const Str& str) { + (void) vm; + (void) str; +} + pkpy_vm* pkpy_vm_create(bool use_stdio, bool enable_os) { - CVM* vm = new CVM(use_stdio, enable_os); + + CVM* vm = new CVM(enable_os); vm->c_data = new ValueStackImpl(); vm->_gc_marker_ex = (void (*)(VM*)) gc_marker_ex; + + if (!use_stdio) { + vm->_stdout = noop_output_handler; + vm->_stderr = noop_output_handler; + } + return (pkpy_vm*) vm; } From dd2212fbadab9977d06cc026a35efd9a69fd1be9 Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Sat, 6 May 2023 19:23:41 -0600 Subject: [PATCH 24/26] switched to using vm->AttributeError for missing global --- c_bindings/pocketpy_c.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 1c249633..e906c9f5 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -302,7 +302,7 @@ bool pkpy_get_global(pkpy_vm* vm_handle, const char* name) { if (o == nullptr) { o = vm->builtins->attr().try_get(name); if (o == nullptr) - throw Exception("AttributeError", "could not find requested global"); + vm->AttributeError("could not find requested global"); } vm->c_data->push(o); From 7de39b0cdaeb8fa50dfeeb07a72dc1b32fbe9c3e Mon Sep 17 00:00:00 2001 From: Kolten Pearson Date: Sat, 6 May 2023 19:31:48 -0600 Subject: [PATCH 25/26] switched to NameError for the type of error to use when a global can't be found --- c_bindings/pocketpy_c.cpp | 2 +- c_bindings/test_answers.txt | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index e906c9f5..fd64ca6e 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -302,7 +302,7 @@ bool pkpy_get_global(pkpy_vm* vm_handle, const char* name) { if (o == nullptr) { o = vm->builtins->attr().try_get(name); if (o == nullptr) - vm->AttributeError("could not find requested global"); + vm->NameError("could not find requested global"); } vm->c_data->push(o); diff --git a/c_bindings/test_answers.txt b/c_bindings/test_answers.txt index 6a90c3ff..19d195d5 100644 --- a/c_bindings/test_answers.txt +++ b/c_bindings/test_answers.txt @@ -1,7 +1,6 @@ hello world! successfully errored with this message: -Traceback (most recent call last): -AttributeError: could not find requested global +NameError: name 'could not find requested global' is not defined testing int methods 11 @@ -50,6 +49,3 @@ TypeError: expected 2 positional arguments, but got 0 (x) testing pushing functions 12 -successfully errored with this message: -Traceback (most recent call last): -AttributeError: could not find requested global From 805a7ca01f96a8b2c2ff0fd3c44ef86e0cca714a Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 7 May 2023 19:57:27 +0800 Subject: [PATCH 26/26] a tiny change --- c_bindings/pocketpy_c.cpp | 4 ++-- run_c_binding_test.sh | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index fd64ca6e..2e88fa11 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -167,7 +167,7 @@ PyObject* c_function_wrapper(VM* vm, ArgsView args) { CVM* cvm = (CVM*) vm; //setup c stack - ValueStackImpl local_stack = ValueStackImpl(); + ValueStackImpl local_stack; for (int i = 0; i < args.size(); i++) local_stack.push(args[i]); @@ -185,7 +185,7 @@ PyObject* c_function_wrapper(VM* vm, ArgsView args) { if (retc == 1) ret = local_stack.top(); else if (retc > 1) { - Tuple t = Tuple(retc); + Tuple t(retc); for (int i = 0; i < retc; i++) { int stack_index = (local_stack.size() - retc) + i; diff --git a/run_c_binding_test.sh b/run_c_binding_test.sh index b3078724..172e1660 100644 --- a/run_c_binding_test.sh +++ b/run_c_binding_test.sh @@ -22,4 +22,5 @@ echo "cleaning up" rm pocketpy_c.o rm test.o rm binding_test_scratch +rm c_binding_test