BLUELOVETH hace 2 años
padre
commit
349048d456

+ 13 - 7
docs/LuaC-API/introduction.md

@@ -14,20 +14,26 @@ Special thanks for [@koltenpearson](https://github.com/koltenpearson) for bringi
 
 ## Basic Functions
 
-#### `pkpy_vm* pkpy_vm_create(bool use_stdio, bool enable_os)`
+#### `pkpy_vm* pkpy_new_vm(bool enable_os)`
 
-Creates a new Lua Style VM.
+Create a new VM.
 
-+ `use_stdio`: if true, the VM will use stdout and stderr
 + `enable_os`: if true, the VM will have access to the os library
 
-#### `bool pkpy_vm_run(pkpy_vm*, const char* source)`
+#### `bool pkpy_vm_run(pkpy_vm* vm_handle, const char* source)`
 
-Runs the given source code in the VM.
+Run the given source code in the VM.
 
 + `source`: the source code to run
 
-#### `void pkpy_vm_destroy(pkpy_vm*)`
+#### `void pkpy_delete_vm(pkpy_vm* vm_handle)`
 
-Destroys the VM.
+Dispose the VM.
 
+#### `bool pkpy_vm_exec(pkpy_vm* vm_handle, const char* source)`
+
+A wrapper of `vm->exec(...)`.
+
+#### `bool pkpy_vm_exec_2(pkpy_vm* vm_handle, const char* source, const char* filename, int mode, const char* module)`
+
+A wrapper of `vm->exec_2(...)`.

+ 31 - 21
include/pocketpy/codeobject.h

@@ -128,6 +128,29 @@ struct FuncDecl {
     void _gc_mark() const;
 };
 
+struct UserData{
+    char data[16];
+    bool empty;
+
+    UserData(): empty(true) {}
+    template<typename T>
+    UserData(T t): empty(false){
+        static_assert(std::is_trivially_copyable_v<T>);
+        static_assert(sizeof(T) <= sizeof(data));
+        memcpy(data, &t, sizeof(T));
+    }
+
+    template <typename T>
+    T get() const{
+        static_assert(std::is_trivially_copyable_v<T>);
+        static_assert(sizeof(T) <= sizeof(data));
+#if PK_DEBUG_EXTRA_CHECK
+        PK_ASSERT(!empty);
+#endif
+        return reinterpret_cast<const T&>(data);
+    }
+};
+
 struct NativeFunc {
     NativeFuncC f;
 
@@ -137,29 +160,16 @@ struct NativeFunc {
     // new style decl-based call
     FuncDecl_ decl;
 
-    using UserData = char[32];
     UserData _userdata;
-    bool _has_userdata;
 
-    template <typename T>
-    void set_userdata(T data) {
-        static_assert(std::is_trivially_copyable_v<T>);
-        static_assert(sizeof(T) <= sizeof(UserData));
-        if(_has_userdata) throw std::runtime_error("userdata already set");
-        _has_userdata = true;
-        memcpy(_userdata, &data, sizeof(T));
+    void set_userdata(UserData data) {
+        if(!_userdata.empty && !data.empty){
+            // override is not supported
+            throw std::runtime_error("userdata already set");
+        }
+        _userdata = data;
     }
 
-    template <typename T>
-    T get_userdata() const {
-        static_assert(std::is_trivially_copyable_v<T>);
-        static_assert(sizeof(T) <= sizeof(UserData));
-#if PK_DEBUG_EXTRA_CHECK
-        if(!_has_userdata) throw std::runtime_error("userdata not set");
-#endif
-        return reinterpret_cast<const T&>(_userdata);
-    }
-    
     NativeFunc(NativeFuncC f, int argc, bool method);
     NativeFunc(NativeFuncC f, FuncDecl_ decl);
 
@@ -201,8 +211,8 @@ struct Py_<NativeFunc> final: PyObject {
 
 template<typename T>
 T lambda_get_userdata(PyObject** p){
-    if(p[-1] != PY_NULL) return PK_OBJ_GET(NativeFunc, p[-1]).get_userdata<T>();
-    else return PK_OBJ_GET(NativeFunc, p[-2]).get_userdata<T>();
+    if(p[-1] != PY_NULL) return PK_OBJ_GET(NativeFunc, p[-1])._userdata.get<T>();
+    else return PK_OBJ_GET(NativeFunc, p[-2])._userdata.get<T>();
 }
 
 } // namespace pkpy

+ 3 - 0
include/pocketpy/frame.h

@@ -62,6 +62,9 @@ struct ValueStackImpl {
     }
     void clear() { _sp = _begin; }
     bool is_overflow() const { return _sp >= _max_end; }
+
+    PyObject* operator[](int i) const { return _begin[i]; }
+    PyObject*& operator[](int i) { return _begin[i]; }
     
     ValueStackImpl(const ValueStackImpl&) = delete;
     ValueStackImpl(ValueStackImpl&&) = delete;

+ 25 - 69
include/pocketpy/pocketpy_c.h

@@ -14,37 +14,15 @@ typedef struct pkpy_vm_handle pkpy_vm;
 typedef int (*pkpy_function)(pkpy_vm*); 
 
 /* Basic Functions */
-PK_EXPORT pkpy_vm* pkpy_vm_create(bool use_stdio, bool enable_os);
-PK_EXPORT void pkpy_vm_destroy(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, 
-//true if it succeeded false if it did not
-
-//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)
-//if null is passed in as message, and it will just print the message to stderr
-PK_EXPORT bool pkpy_clear_error(pkpy_vm*, char** message);
-//NOTE you are responsible for freeing message 
-
-//this will cause the vm to enter an error state and report the given message
-//when queried
-//note that at the moment this is more like a panic than throwing an error
-//the user will not be able to catch it with python code
-PK_EXPORT bool pkpy_error(pkpy_vm*, const char* name, const char* message);
-
-
-
-
+PK_EXPORT pkpy_vm* pkpy_new_vm(bool enable_os);
+PK_EXPORT void pkpy_delete_vm(pkpy_vm* vm);
+PK_EXPORT bool pkpy_vm_exec(pkpy_vm* vm, const char* source);
+PK_EXPORT bool pkpy_vm_exec_2(pkpy_vm* vm, const char* source, const char* filename, int mode, const char* module);
 
+/* Stack Manipulation */
 PK_EXPORT bool pkpy_pop(pkpy_vm*, int n);
-
-//push the item at index onto the top of the stack (as well as leaving it where
-//it is on the stack)
-PK_EXPORT bool pkpy_push(pkpy_vm*, int index);
-
+PK_EXPORT bool pkpy_dup_top(pkpy_vm*);
+PK_EXPORT bool pkpy_rot_two(pkpy_vm*);
 PK_EXPORT bool pkpy_push_function(pkpy_vm*, pkpy_function, int);
 PK_EXPORT bool pkpy_push_int(pkpy_vm*, int);
 PK_EXPORT bool pkpy_push_float(pkpy_vm*, double);
@@ -53,26 +31,32 @@ PK_EXPORT bool pkpy_push_string(pkpy_vm*, const char*);
 PK_EXPORT bool pkpy_push_stringn(pkpy_vm*, const char*, int length);
 PK_EXPORT bool pkpy_push_voidp(pkpy_vm*, void*);
 PK_EXPORT bool pkpy_push_none(pkpy_vm*);
+PK_EXPORT bool pkpy_push_eval(pkpy_vm*, const char* source);
+PK_EXPORT bool pkpy_push_module(pkpy_vm*, const char* name);
+
+/* Error Handling */
+
+PK_EXPORT bool pkpy_clear_error(pkpy_vm*, char** message);
+PK_EXPORT bool pkpy_error(pkpy_vm*, const char* name, const char* message);
+//will return true if the vm is currently in an error state
+PK_EXPORT bool pkpy_check_error(pkpy_vm*);
+
+/* Variables */
 
 PK_EXPORT bool pkpy_set_global(pkpy_vm*, const char* name);
 PK_EXPORT bool pkpy_get_global(pkpy_vm*, const char* name);
+//will return true if global exists
+PK_EXPORT bool pkpy_check_global(pkpy_vm*, const char* name);
+PK_EXPORT bool pkpy_getattr(pkpy_vm*, const char* name);
+PK_EXPORT bool pkpy_setattr(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)
-PK_EXPORT bool pkpy_call(pkpy_vm*, int argc);
+/* Callables */
 
-//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
+PK_EXPORT bool pkpy_call(pkpy_vm*, int argc);
 PK_EXPORT bool pkpy_call_method(pkpy_vm*, const char* name, int argc);
 
+/* Types */
 
-//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
 PK_EXPORT bool pkpy_to_int(pkpy_vm*, int index, int* ret);
 PK_EXPORT bool pkpy_to_float(pkpy_vm*, int index, double* ret);
 PK_EXPORT bool pkpy_to_bool(pkpy_vm*, int index, bool* ret);
@@ -97,34 +81,6 @@ PK_EXPORT bool pkpy_is_string(pkpy_vm*, int index);
 PK_EXPORT bool pkpy_is_voidp(pkpy_vm*, int index);
 PK_EXPORT bool pkpy_is_none(pkpy_vm*, int index);
 
-//will return true if global exists
-PK_EXPORT bool pkpy_check_global(pkpy_vm*, const char* name);
-
-//will return true if the vm is currently in an error state
-PK_EXPORT bool pkpy_check_error(pkpy_vm*);
-
-//will return true if at least free empty slots remain on the stack
-PK_EXPORT bool pkpy_check_stack(pkpy_vm*, int free);
-
-//returns the number of elements on the stack
-PK_EXPORT int pkpy_stack_size(pkpy_vm*);
-
-PK_EXPORT bool pkpy_getattr(pkpy_vm*, const char* name);
-PK_EXPORT bool pkpy_setattr(pkpy_vm*, const char* name);
-PK_EXPORT bool pkpy_eval(pkpy_vm*, const char* source);
-
-// create a new native module with the given name and push it onto the stack
-PK_EXPORT bool pkpy_new_module(void* vm, const char* name);
-
-
-/* vm api */
-
-// for backwards compatibility
-#define pkpy_vm_run(vm, source) pkpy_vm_exec(vm, source)
-
-PK_EXPORT bool pkpy_vm_exec(pkpy_vm* vm, const char* source);
-PK_EXPORT bool pkpy_vm_exec_2(pkpy_vm* vm, const char* source, const char* filename, int mode, const char* module);
-
 /* special api */
 
 // free a pointer allocated from pkpy's heap

+ 2 - 5
include/pocketpy/vm.h

@@ -117,9 +117,6 @@ public:
     NameDict _modules;                                 // loaded modules
     std::map<StrName, Str> _lazy_modules;              // lazy loaded modules
 
-    PyObject* _reg[32];     // registers for user purpose, also used by C-API
-    static constexpr int REG_COUNT = sizeof(_reg) / sizeof(void*);
-
     PyObject* None;
     PyObject* True;
     PyObject* False;
@@ -456,8 +453,8 @@ public:
     PyObject* _py_generator(Frame&& frame, ArgsView buffer);
     void _prepare_py_call(PyObject**, ArgsView, ArgsView, const FuncDecl_&);
     // new style binding api
-    PyObject* bind(PyObject*, const char*, const char*, NativeFuncC, void* userdata=nullptr);
-    PyObject* bind(PyObject*, const char*, NativeFuncC, void* userdata=nullptr);
+    PyObject* bind(PyObject*, const char*, const char*, NativeFuncC, UserData userdata={});
+    PyObject* bind(PyObject*, const char*, NativeFuncC, UserData userdata={});
 };
 
 DEF_NATIVE_2(Str, tp_str)

+ 56 - 128
src/pocketpy_c.cpp

@@ -1,77 +1,60 @@
 #include "pocketpy.h"
+#include "pocketpy/tuplelist.h"
 #include "pocketpy_c.h"
 
 using namespace pkpy;
 
 typedef int (*LuaStyleFuncC)(VM*);
 
-struct LuaStack: public ValueStackImpl<32>{
-    PyObject*& at(int i) {
-        if(i < 0 || i >= size()){
-            throw std::runtime_error("lua stack index out of range");
-        }
-        return _begin[i];
-    }
-    PyObject* const& at(int i) const {
-        if(i < 0 || i >= size()){
-            throw std::runtime_error("lua stack index out of range");
-        }
-        return _begin[i];
-    }
-
-    void safe_push(PyObject* obj){
-        if(size() >= max_size()) throw std::runtime_error("lua stack overflow");
-        push(obj);
-    }
-
-    void safe_pop(){
-        if(size() == 0) throw std::runtime_error("lua stack is empty");
-        pop();
-    }
-
-    PyObject*& safe_top(){
-        if(size() == 0) throw std::runtime_error("lua stack is empty");
-        return top();
-    }
-};
-
 #define ERRHANDLER_OPEN \
-    if (vm->error != nullptr) \
+    if (vm->_c.error != nullptr) \
         return false; \
     try {
 
 #define ERRHANDLER_CLOSE \
     } catch(Exception& e ) { \
-        vm->error = py_var(vm, e); \
+        vm->_c.error = py_var(vm, e); \
         return false; \
     } catch(const std::exception& re){ \
         auto e = Exception("std::exception", re.what()); \
-        vm->error = py_var(vm, e); \
+        vm->_c.error = py_var(vm, e); \
         return false; \
     }
 
+pkpy_vm* pkpy_new_vm(bool enable_os){
+    return (pkpy_vm*)new VM(enable_os);
+}
 
-class CVM: public VM {
-public:
-    LuaStack c_data;        // operation stack
-    PyObject* error;
-
-    CVM(bool use_stdio, bool enable_os) : VM(enable_os) {
-        error = nullptr;
-        heap._gc_marker_ex = [](VM* vm_) {
-            CVM* vm = (CVM*)vm_;
-            for(PyObject* obj: vm->c_data) if(obj!=nullptr) PK_OBJ_MARK(obj);
-            if(vm->error != nullptr) PK_OBJ_MARK(vm->error);
-        };
-
-        if (!use_stdio) {
-            _stdout = _stderr = [](VM* vm, const Str& s){
-                PK_UNUSED(vm);
-                PK_UNUSED(s);
-            };
-        }
+void pkpy_delete_vm(pkpy_vm* vm){
+    return delete (VM*)vm;
+}
+
+bool pkpy_vm_exec(pkpy_vm* vm_handle, const char* source) {
+    VM* vm = (VM*) vm_handle;
+    PyObject* res;
+    ERRHANDLER_OPEN
+    CodeObject_ code = vm->compile(source, "main.py", EXEC_MODE);
+    res = vm->_exec(code, vm->_main);
+    ERRHANDLER_CLOSE
+    return res != nullptr;
+}
+
+bool pkpy_vm_exec_2(pkpy_vm* vm_handle, const char* source, const char* filename, int mode, const char* module){
+    VM* vm = (VM*) vm_handle;
+    PyObject* res;
+    PyObject* mod;
+    ERRHANDLER_OPEN
+    if(module == nullptr){
+        mod = vm->_main;
+    }else{
+        mod = vm->_modules[module];     // may raise
     }
-};
+    CodeObject_ code = vm->compile(source, filename, (CompileMode)mode);
+    res = vm->_exec(code, mod);
+    ERRHANDLER_CLOSE
+    return res != nullptr;
+}
+
 
 
 //for now I will unpack a tuple automatically, we may not want to handle
@@ -114,89 +97,34 @@ bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) {
     return true;
 }
 
-pkpy_vm* pkpy_vm_create(bool use_stdio, bool enable_os) {
-    CVM* vm = new CVM(use_stdio, enable_os);
-    return (pkpy_vm*) vm;
-}
-
-bool pkpy_vm_exec(pkpy_vm* vm_handle, const char* source) {
-    CVM* vm = (CVM*) vm_handle;
-    PyObject* res;
-    ERRHANDLER_OPEN
-    CodeObject_ code = vm->compile(source, "main.py", EXEC_MODE);
-    res = vm->_exec(code, vm->_main);
-    ERRHANDLER_CLOSE
-    return res != nullptr;
-}
-
-bool pkpy_vm_exec_2(pkpy_vm* vm_handle, const char* source, const char* filename, int mode, const char* module){
-    CVM* vm = (CVM*) vm_handle;
-    PyObject* res;
-    PyObject* mod;
-    ERRHANDLER_OPEN
-    if(module == nullptr){
-        mod = vm->_main;
-    }else{
-        mod = vm->_modules[module];     // may raise
-    }
-    CodeObject_ code = vm->compile(source, filename, (CompileMode)mode);
-    res = vm->_exec(code, mod);
-    ERRHANDLER_CLOSE
-    return res != nullptr;
-}
-
-void pkpy_vm_destroy(pkpy_vm* vm_handle) {
-    CVM* vm = (CVM*) vm_handle;
-    delete vm;
-}
-
 PyObject* c_function_wrapper(VM* vm, ArgsView args) {
     LuaStyleFuncC f = lambda_get_userdata<LuaStyleFuncC>(args.begin());
-    CVM* cvm = (CVM*) vm;
-
-    //setup c stack
-    LuaStack local_stack;
-
-    for (int i = 0; i < args.size(); i++)
-        local_stack.safe_push(args[i]);
-    
-    // tmp is controlled by RAII
-    auto tmp = CVM::TempStack(cvm, &local_stack);
-    int retc = f(cvm);
-
+    PyObject** curr_sp = &vm->s_data.top();
+    int retc = f(vm);
     // propagate_if_errored
-    if (cvm->error != nullptr){
-        Exception e = _py_cast<Exception&>(vm, cvm->error);
-        cvm->error = nullptr;
-        tmp.restore();
+    if (vm->_c.error != nullptr){
+        Exception e = _py_cast<Exception&>(vm, vm->_c.error);
+        vm->_c.error = nullptr;
         vm->_error(e);
     }
-    tmp.restore();
-
-    PyObject* ret = cvm->None;
-
-    if (retc == 1) 
-        ret = local_stack.safe_top();
-    else if (retc > 1) {
-        Tuple t(retc);
-
-        for (int i = 0; i < retc; i++)  {
-            int stack_index = (local_stack.size() - retc) + i;
-            t[i] = local_stack.at(stack_index);
-        }
-
-        ret = py_var(cvm, t);
-    }
-
-    return ret;
+    PK_ASSERT(retc == vm->s_data._sp-curr_sp);
+    if(retc == 0) return vm->None;
+    if (retc == 1) return vm->s_data.popx();
+    ArgsView ret_view(curr_sp, vm->s_data._sp);
+    return py_var(vm, ret_view.to_tuple());
 }
 
-bool pkpy_push_function(pkpy_vm* vm_handle, pkpy_function f, int argc) {
-    CVM* vm = (CVM*) vm_handle;
-    NativeFunc nf = NativeFunc(c_function_wrapper, argc, false);
-    nf.set_userdata(f);
+bool pkpy_push_function(pkpy_vm* vm_handle, const char* sig, pkpy_function f) {
+    VM* vm = (VM*) vm_handle;
     ERRHANDLER_OPEN
-    vm->c_data->safe_push(py_var(vm, nf));
+    PyObject* f_obj = vm->bind(
+        nullptr,
+        sig,
+        nullptr,
+        c_function_wrapper,
+        f
+    );
+    vm->s_data.push(f_obj);
     ERRHANDLER_CLOSE
     return true;
 }

+ 4 - 10
src/vm.cpp

@@ -4,7 +4,6 @@ namespace pkpy{
 
     VM::VM(bool enable_os) : heap(this), enable_os(enable_os) {
         this->vm = this;
-        for(int i=0; i<REG_COUNT; i++) _reg[i] = nullptr;
         _stdout = [](VM* vm, const Str& s) {
             PK_UNUSED(vm);
             std::cout << s;
@@ -957,11 +956,11 @@ void VM::setattr(PyObject* obj, StrName name, PyObject* value){
     obj->attr().set(name, value);
 }
 
-PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn, void* userdata){
+PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn, UserData userdata){
     return bind(obj, sig, nullptr, fn, userdata);
 }
 
-PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn, void* userdata){
+PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn, UserData userdata){
     CodeObject_ co;
     try{
         // fn(a, b, *c, d=1) -> None
@@ -978,10 +977,8 @@ PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Native
         decl->docstring = Str(docstring).strip();
     }
     PyObject* f_obj = VAR(NativeFunc(fn, decl));
-    if(userdata != nullptr){
-        PK_OBJ_GET(NativeFunc, f_obj).set_userdata(userdata);
-    }
-    obj->attr().set(decl->code->name, f_obj);
+    PK_OBJ_GET(NativeFunc, f_obj).set_userdata(userdata);
+    if(obj != nullptr) obj->attr().set(decl->code->name, f_obj);
     return f_obj;
 }
 
@@ -1000,9 +997,6 @@ void ManagedHeap::mark() {
     for(PyObject* obj: vm->s_data) PK_OBJ_MARK(obj);
     if(_gc_marker_ex) _gc_marker_ex(vm);
     if(vm->_last_exception) PK_OBJ_MARK(vm->_last_exception);
-    for(int i=0; i<vm->REG_COUNT; i++){
-        if(vm->_reg[i] != nullptr) PK_OBJ_MARK(vm->_reg[i]);
-    }
 }
 
 Str obj_type_name(VM *vm, Type type){