diff --git a/.gitignore b/.gitignore index d33a5917..c59d8f10 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,5 @@ pocketpy.lib APPS build -pocketpy.dSYM \ No newline at end of file +pocketpy.dSYM +main diff --git a/CMakeLists.txt b/CMakeLists.txt index cdffae6c..61f8c4b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,31 +26,25 @@ else() endif() endif() -option(PK_EXPORT_C_API "Export C API" ON) +option(PK_EXPORT_CXX_API "Export C++ API" OFF) include_directories(${CMAKE_CURRENT_LIST_DIR}/include) -if(PK_EXPORT_C_API) - message(STATUS "Exporting C API") - include_directories(${CMAKE_CURRENT_LIST_DIR}/c_bindings) - set(PK_LIB_CPP ${CMAKE_CURRENT_LIST_DIR}/c_bindings/pocketpy_c.cpp) -else() - set(PK_LIB_CPP ${CMAKE_CURRENT_LIST_DIR}/src2/lib.cpp) -endif() - aux_source_directory(${CMAKE_CURRENT_LIST_DIR}/src POCKETPY_SRC) option(PK_BUILD_SHARED_LIB "Build shared library" OFF) option(PK_BUILD_STATIC_LIB "Build static library" OFF) -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +if (PK_EXPORT_CXX_API) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif() if(PK_BUILD_SHARED_LIB) - add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC} ${PK_LIB_CPP}) + add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC}) elseif(PK_BUILD_STATIC_LIB) - add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC} ${PK_LIB_CPP}) + add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC}) else() - add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC} ${PK_LIB_CPP}) + add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC}) set(PROJECT_EXE_NAME main) add_executable(${PROJECT_EXE_NAME} src2/main.cpp) target_link_libraries(${PROJECT_EXE_NAME} ${PROJECT_NAME}) diff --git a/amalgamate.py b/amalgamate.py index e02d002c..96e5e62f 100644 --- a/amalgamate.py +++ b/amalgamate.py @@ -88,28 +88,12 @@ if sys.platform in ['linux', 'darwin']: print("amalgamated/pocketpy.h") -content = [] -for i in ["include/pocketpy/export.h", "c_bindings/pocketpy_c.h", "c_bindings/pocketpy_c.cpp"]: - with open(i, "rt", encoding='utf-8') as g: - content.append(g.read()) - -with open("amalgamated/pocketpy.cpp", "wt", encoding='utf-8') as f: - content = '\n\n'.join(content) - content = content.replace('#include "pocketpy/export.h"', '') - content = content.replace('#include "pocketpy_c.h"', '') - f.write(content) - - shutil.copy("amalgamated/pocketpy.h", "plugins/flutter/src/pocketpy.h") -shutil.copy("amalgamated/pocketpy.cpp", "plugins/flutter/src/pocketpy.cpp") - shutil.copy("amalgamated/pocketpy.h", "plugins/macos/pocketpy/pocketpy.h") -shutil.copy("amalgamated/pocketpy.cpp", "plugins/macos/pocketpy/pocketpy.cpp") # unity plugin unity_ios_root = 'plugins/unity/PocketPyUnityPlugin/Assets/PocketPython/Plugins/iOS' if os.path.exists(unity_ios_root): shutil.copy("amalgamated/pocketpy.h", unity_ios_root) - shutil.copy("amalgamated/pocketpy.cpp", unity_ios_root) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp deleted file mode 100644 index c0f821e0..00000000 --- a/c_bindings/pocketpy_c.cpp +++ /dev/null @@ -1,554 +0,0 @@ -#include "pocketpy.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) \ - return false; \ - try { - -#define ERRHANDLER_CLOSE \ - } catch(Exception& e ) { \ - vm->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); \ - return false; \ - } - - -class CVM : public VM { - public : - - LuaStack* c_data; - PyObject* error; - - CVM(bool enable_os=true) : VM(enable_os) { - c_data = new LuaStack(); - error = nullptr; - } - - ~CVM() { - c_data->clear(); - delete c_data; - } - - struct TempStack{ - CVM* cvm; - LuaStack* prev; - TempStack(CVM* cvm, LuaStack* new_data) : cvm(cvm) { - prev = cvm->c_data; - cvm->c_data = new_data; - } - - ~TempStack() { restore(); } - - void restore(){ - if(prev == nullptr) return; - cvm->c_data = prev; - prev = nullptr; - } - }; -}; - - -//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(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++) - 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 to - } else - vm->c_data->push(ret); - -} - - -bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) { - CVM* vm = (CVM*) vm_handle; - // no error - if (vm->error == nullptr) return false; - Exception& e = _py_cast(vm, vm->error); - if (message != nullptr) - *message = e.summary().c_str_dup(); - else - std::cerr << "ERROR: " << e.summary() << "\n"; - vm->error = nullptr; - vm->c_data->clear(); - vm->callstack.clear(); - vm->s_data.clear(); - return true; -} - -void gc_marker_ex(CVM* vm) { - for(PyObject* obj: *vm->c_data) if(obj!=nullptr) PK_OBJ_MARK(obj); - if(vm->error != nullptr) PK_OBJ_MARK(vm->error); -} - -static OutputHandler stdout_handler = nullptr; -static OutputHandler stderr_handler = nullptr; - -void pkpy_set_output_handlers(pkpy_vm*, OutputHandler stdout_handler, OutputHandler stderr_handler){ - ::stdout_handler = stdout_handler; - ::stderr_handler = stderr_handler; -} - -pkpy_vm* pkpy_vm_create(bool use_stdio, bool enable_os) { - CVM* vm = new CVM(enable_os); - vm->c_data = new LuaStack(); - vm->heap._gc_marker_ex = (void (*)(VM*)) gc_marker_ex; - - if (!use_stdio) { - vm->_stdout = [](VM* vm, const Str& s){ - std::string str = s.str(); - if (stdout_handler != nullptr) stdout_handler((pkpy_vm*)vm, str.c_str()); - }; - vm->_stderr = [](VM* vm, const Str& s){ - std::string str = s.str(); - if (stderr_handler != nullptr) stderr_handler((pkpy_vm*)vm, str.c_str()); - }; - } - - return (pkpy_vm*) vm; -} - -bool pkpy_vm_run(pkpy_vm* vm_handle, const char* source) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - CodeObject_ code = vm->compile(source, "", EXEC_MODE); - vm->_exec(code, vm->_main); - ERRHANDLER_CLOSE - - //unpack_return(w, result); - //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 - - return true; -} - -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(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); - - // propagate_if_errored - if (cvm->error != nullptr){ - Exception e = _py_cast(vm, cvm->error); - cvm->error = nullptr; - tmp.restore(); - 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; -} - -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); - ERRHANDLER_OPEN - vm->c_data->safe_push(py_var(vm, nf)); - ERRHANDLER_CLOSE - return true; -} - -bool pkpy_push_int(pkpy_vm* vm_handle, int value) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - vm->c_data->safe_push(py_var(vm, value)); - ERRHANDLER_CLOSE - return true; -} - -bool pkpy_push_float(pkpy_vm* vm_handle, double value) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - vm->c_data->safe_push(py_var(vm, value)); - ERRHANDLER_CLOSE - return true; -} - -bool pkpy_push_bool(pkpy_vm* vm_handle, bool value) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - vm->c_data->safe_push(py_var(vm, value)); - ERRHANDLER_CLOSE - return true; -} - -bool pkpy_push_string(pkpy_vm* vm_handle, const char* value) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - vm->c_data->safe_push(py_var(vm, value)); - ERRHANDLER_CLOSE - return true; -} - -bool pkpy_push_stringn(pkpy_vm* vm_handle, const char* value, int length) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - vm->c_data->safe_push(py_var(vm, Str(value, length))); - ERRHANDLER_CLOSE - return true; -} - -bool pkpy_push_voidp(pkpy_vm* vm_handle, void* value) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - vm->c_data->safe_push(py_var(vm, value)); - ERRHANDLER_CLOSE - return true; -} - -bool pkpy_push_none(pkpy_vm* vm_handle) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - vm->c_data->safe_push(vm->None); - ERRHANDLER_CLOSE - return true; -} - - - -bool pkpy_set_global(pkpy_vm* vm_handle, const char* name) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - vm->_main->attr().set(name, vm->c_data->safe_top()); - vm->c_data->safe_pop(); - ERRHANDLER_CLOSE - return true; -} - -//get global will also get bulitins -bool pkpy_get_global(pkpy_vm* vm_handle, const char* name) { - CVM* vm = (CVM*) 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("NameError", name); - } - vm->c_data->safe_push(o); - ERRHANDLER_CLOSE - return true; -} - - -bool pkpy_call(pkpy_vm* vm_handle, int argc) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - int callable_index = vm->c_data->size() - argc - 1; - PyObject* callable = vm->c_data->at(callable_index); - vm->s_data.push(callable); - vm->s_data.push(PY_NULL); - - for (int i = 0; i < argc; i++) - vm->s_data.push(vm->c_data->at(callable_index + i + 1)); - - PyObject* o = vm->vectorcall(argc); - - vm->c_data->shrink(argc + 1); - - unpack_return(vm, o); - ERRHANDLER_CLOSE - - return true; -} - -bool pkpy_call_method(pkpy_vm* vm_handle, const char* name, int argc) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - - int self_index = vm->c_data->size() - argc - 1; - PyObject* self = vm->c_data->at(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->at(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; - return index; -} - -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, vm->c_data->size()); - - PyObject* o = vm->c_data->at(index); - if (ret != nullptr) *ret = py_cast(vm, o); - - return true; - ERRHANDLER_CLOSE -} - -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, vm->c_data->size()); - PyObject* o = vm->c_data->at(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) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - index = lua_to_cstack_index(index, vm->c_data->size()); - PyObject* o = vm->c_data->at(index); - if (ret != nullptr) *ret = py_cast(vm, o); - return true; - ERRHANDLER_CLOSE -} - -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, vm->c_data->size()); - PyObject* o = vm->c_data->at(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) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - index = lua_to_cstack_index(index, vm->c_data->size()); - PyObject* o = vm->c_data->at(index); - if (ret != nullptr) { - *ret = py_cast(vm, o).c_str_dup(); - } - return true; - ERRHANDLER_CLOSE -} - -bool pkpy_to_stringn(pkpy_vm* vm_handle, int index, const char** ret, int* size) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - index = lua_to_cstack_index(index, vm->c_data->size()); - PyObject* o = vm->c_data->at(index); - if (ret != nullptr) { - std::string_view sv = py_cast(vm, o).sv(); - *ret = sv.data(); - *size = sv.size(); - } - return true; - ERRHANDLER_CLOSE -} - - -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->at(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->at(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->at(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->at(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->at(index); - return is_type(o, VoidP::_type(vm)); -} - -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->at(index); - return o == vm->None; -} - -bool pkpy_check_global(pkpy_vm* vm_handle, const char* name) { - CVM* vm = (CVM*) vm_handle; - PyObject* o = vm->_main->attr().try_get(name); - if (o == nullptr) { - o = vm->builtins->attr().try_get(name); - if (o == nullptr) - return false; - } - return true; -} - -bool pkpy_check_error(pkpy_vm* vm_handle) { - CVM* vm = (CVM*) vm_handle; - return vm->error != nullptr; -} - - -bool pkpy_check_stack(pkpy_vm* vm_handle, int free) { - CVM* vm = (CVM*) vm_handle; - return free + vm->c_data->size() <= LuaStack::max_size(); -} - -int pkpy_stack_size(pkpy_vm* vm_handle) { - CVM* vm = (CVM*) vm_handle; - return vm->c_data->size(); -} - -bool pkpy_pop(pkpy_vm* vm_handle, int n) { - CVM* vm = (CVM*) vm_handle; - vm->c_data->shrink(n); - return true; -} - - -bool pkpy_push(pkpy_vm* vm_handle, int index) { - CVM* vm = (CVM*) vm_handle; - index = lua_to_cstack_index(index, vm->c_data->size()); - vm->c_data->safe_push(vm->c_data->at(index)); - return true; -} - - -bool pkpy_error(pkpy_vm* vm_handle, const char* name, const char* message) { - CVM* vm = (CVM*) vm_handle; - // already in error state - if (vm->error != nullptr) return false; - vm->error = py_var(vm, Exception(name, message)); - return false; -} - -bool pkpy_getattr(pkpy_vm* vm_handle, const char* name) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - PyObject* o = vm->c_data->safe_top(); - PyObject* ret = vm->getattr(o, name, false); - if(ret == nullptr) return false; - vm->c_data->top() = ret; - ERRHANDLER_CLOSE - return true; -} - -bool pkpy_setattr(pkpy_vm* vm_handle, const char* name) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - if(vm->c_data->size() < 2){ - throw std::runtime_error("not enough arguments"); - } - PyObject* a = vm->c_data->top(); - PyObject* val = vm->c_data->second(); - vm->setattr(a, name, val); - vm->c_data->shrink(2); - ERRHANDLER_CLOSE - return true; -} - -bool pkpy_eval(pkpy_vm* vm_handle, const char* code) { - CVM* vm = (CVM*) vm_handle; - ERRHANDLER_OPEN - CodeObject_ co = vm->compile(code, "", EVAL_MODE); - PyObject* ret = vm->_exec(co, vm->_main); - vm->c_data->safe_push(ret); - ERRHANDLER_CLOSE - return true; -} diff --git a/c_bindings/pocketpy_c.h b/c_bindings/pocketpy_c.h deleted file mode 100644 index 697364b2..00000000 --- a/c_bindings/pocketpy_c.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef POCKETPY_C_H -#define POCKETPY_C_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#include "pocketpy/export.h" - -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, -//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_vm_create(bool use_stdio, bool enable_os); -PK_EXPORT bool pkpy_vm_run(pkpy_vm*, const char* source); -PK_EXPORT void pkpy_vm_destroy(pkpy_vm*); - -typedef int (*pkpy_function)(pkpy_vm*); - -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_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); -PK_EXPORT bool pkpy_push_bool(pkpy_vm*, bool); -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_set_global(pkpy_vm*, const char* name); -PK_EXPORT 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) -PK_EXPORT 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 -PK_EXPORT 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 -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); -PK_EXPORT 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 -PK_EXPORT bool pkpy_to_string(pkpy_vm*, int index, char** ret); - -//this method provides a weak reference, it is only valid until the -//next api call -//it is not null terminated -PK_EXPORT 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 - -PK_EXPORT bool pkpy_is_int(pkpy_vm*, int index); -PK_EXPORT bool pkpy_is_float(pkpy_vm*, int index); -PK_EXPORT bool pkpy_is_bool(pkpy_vm*, int index); -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*); - -typedef void (*OutputHandler)(pkpy_vm*, const char*); -PK_EXPORT void pkpy_set_output_handlers(pkpy_vm*, OutputHandler stdout_handler, OutputHandler stderr_handler); - -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); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/docs/LuaC-API/introduction.md b/docs/LuaC-API/introduction.md index 38b9322d..5c2fede1 100644 --- a/docs/LuaC-API/introduction.md +++ b/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(...)`. \ No newline at end of file diff --git a/include/pocketpy/codeobject.h b/include/pocketpy/codeobject.h index 5d782c46..78230ee9 100644 --- a/include/pocketpy/codeobject.h +++ b/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 + UserData(T t): empty(false){ + static_assert(std::is_trivially_copyable_v); + static_assert(sizeof(T) <= sizeof(data)); + memcpy(data, &t, sizeof(T)); + } + + template + T get() const{ + static_assert(std::is_trivially_copyable_v); + static_assert(sizeof(T) <= sizeof(data)); +#if PK_DEBUG_EXTRA_CHECK + PK_ASSERT(!empty); +#endif + return reinterpret_cast(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 - void set_userdata(T data) { - static_assert(std::is_trivially_copyable_v); - 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 - T get_userdata() const { - static_assert(std::is_trivially_copyable_v); - 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(_userdata); - } - NativeFunc(NativeFuncC f, int argc, bool method); NativeFunc(NativeFuncC f, FuncDecl_ decl); @@ -201,8 +211,8 @@ struct Py_ final: PyObject { template T lambda_get_userdata(PyObject** p){ - if(p[-1] != PY_NULL) return PK_OBJ_GET(NativeFunc, p[-1]).get_userdata(); - else return PK_OBJ_GET(NativeFunc, p[-2]).get_userdata(); + if(p[-1] != PY_NULL) return PK_OBJ_GET(NativeFunc, p[-1])._userdata.get(); + else return PK_OBJ_GET(NativeFunc, p[-2])._userdata.get(); } } // namespace pkpy \ No newline at end of file diff --git a/include/pocketpy/common.h b/include/pocketpy/common.h index 4d83561b..ca6cd3ac 100644 --- a/include/pocketpy/common.h +++ b/include/pocketpy/common.h @@ -20,7 +20,7 @@ #include #include -#define PK_VERSION "1.0.9" +#define PK_VERSION "1.1.0" #include "config.h" #include "export.h" @@ -149,26 +149,4 @@ inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011; #define ADD_MODULE_PLACEHOLDER(name) namespace pkpy { inline void add_module_##name(void* vm) { (void)vm; } } -} // namespace pkpy - -#ifdef _WIN32 - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#include - -#elif __EMSCRIPTEN__ - -#include - -#elif __unix__ - -#include - -#endif \ No newline at end of file +} // namespace pkpy \ No newline at end of file diff --git a/include/pocketpy/export.h b/include/pocketpy/export.h index 2877b7ef..39a253c4 100644 --- a/include/pocketpy/export.h +++ b/include/pocketpy/export.h @@ -1,19 +1,44 @@ -#ifndef PK_EXPORT +#pragma once -#ifdef _WIN32 -#define PK_EXPORT __declspec(dllexport) +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) + //define something for Windows (32-bit and 64-bit, this part is common) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + + #ifndef NOMINMAX + #define NOMINMAX + #endif + + #include + + #define PK_EXPORT __declspec(dllexport) + #define PK_SUPPORT_DYLIB 1 #elif __EMSCRIPTEN__ -#include -#define PK_EXPORT EMSCRIPTEN_KEEPALIVE + #include + #define PK_EXPORT EMSCRIPTEN_KEEPALIVE + #define PK_SUPPORT_DYLIB 0 +#elif __APPLE__ + #include + #include + #define PK_SUPPORT_DYLIB 2 + #if TARGET_IPHONE_SIMULATOR + // iOS, tvOS, or watchOS Simulator + #elif TARGET_OS_MACCATALYST + // Mac's Catalyst (ports iOS API into Mac, like UIKit). + #elif TARGET_OS_IPHONE + // iOS, tvOS, or watchOS device + #elif TARGET_OS_MAC + // Other kinds of Apple platforms + #else + # error "Unknown Apple platform" + #endif + #define PK_EXPORT __attribute__((visibility("default"))) +#elif __linux__ + #include + #define PK_SUPPORT_DYLIB 2 + #define PK_EXPORT __attribute__((visibility("default"))) #else -#define PK_EXPORT __attribute__((visibility("default"))) -#endif - -#define PK_INLINE_EXPORT PK_EXPORT inline - -#endif - -#ifdef PK_SHARED_MODULE -#undef PK_INLINE_EXPORT -#define PK_INLINE_EXPORT inline -#endif + #define PK_EXPORT + #define PK_SUPPORT_DYLIB 0 +#endif \ No newline at end of file diff --git a/include/pocketpy/frame.h b/include/pocketpy/frame.h index 95df9bcc..294cbe9a 100644 --- a/include/pocketpy/frame.h +++ b/include/pocketpy/frame.h @@ -24,6 +24,9 @@ struct FastLocals{ PyObject** try_get_name(StrName name); NameDict_ to_namedict(); + + PyObject** begin() const { return a; } + PyObject** end() const { return a + size(); } }; template @@ -62,6 +65,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; diff --git a/include/pocketpy/memory.h b/include/pocketpy/memory.h index 51d54d17..54a1a905 100644 --- a/include/pocketpy/memory.h +++ b/include/pocketpy/memory.h @@ -246,8 +246,8 @@ struct MemoryPool{ } }; -PK_EXPORT inline MemoryPool<64> pool64; -PK_EXPORT inline MemoryPool<128> pool128; +inline MemoryPool<64> pool64; +inline MemoryPool<128> pool128; template struct shared_ptr { diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 1f4ca685..8674d7f0 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -94,83 +94,4 @@ void add_module_dis(VM* vm); void add_module_traceback(VM* vm); void add_module_gc(VM* vm); -} // namespace pkpy - -/*************************GLOBAL NAMESPACE*************************/ -extern "C" { - PK_INLINE_EXPORT - void pkpy_free(void* p){ - free(p); - } - - PK_INLINE_EXPORT - void pkpy_vm_exec(pkpy::VM* vm, const char* source){ - vm->exec(source, "main.py", pkpy::EXEC_MODE); - } - - PK_INLINE_EXPORT - void pkpy_vm_exec_2(pkpy::VM* vm, const char* source, const char* filename, int mode, const char* module){ - pkpy::PyObject* mod; - if(module == nullptr) mod = vm->_main; - else{ - mod = vm->_modules.try_get(module); - if(mod == nullptr) return; - } - vm->exec(source, filename, (pkpy::CompileMode)mode, mod); - } - - PK_INLINE_EXPORT - void pkpy_vm_compile(pkpy::VM* vm, const char* source, const char* filename, int mode, bool* ok, char** res){ - try{ - pkpy::CodeObject_ code = vm->compile(source, filename, (pkpy::CompileMode)mode); - *res = code->serialize(vm).c_str_dup(); - *ok = true; - }catch(pkpy::Exception& e){ - *ok = false; - *res = e.summary().c_str_dup(); - }catch(std::exception& e){ - *ok = false; - *res = strdup(e.what()); - }catch(...){ - *ok = false; - *res = strdup("unknown error"); - } - } - - PK_INLINE_EXPORT - pkpy::REPL* pkpy_new_repl(pkpy::VM* vm){ - pkpy::REPL* p = new pkpy::REPL(vm); - return p; - } - - PK_INLINE_EXPORT - bool pkpy_repl_input(pkpy::REPL* r, const char* line){ - return r->input(line); - } - - PK_INLINE_EXPORT - void pkpy_vm_add_module(pkpy::VM* vm, const char* name, const char* source){ - vm->_lazy_modules[name] = source; - } - - PK_INLINE_EXPORT - pkpy::VM* pkpy_new_vm(bool enable_os=true){ - pkpy::VM* p = new pkpy::VM(enable_os); - return p; - } - - PK_INLINE_EXPORT - void pkpy_delete_vm(pkpy::VM* vm){ - delete vm; - } - - PK_INLINE_EXPORT - void pkpy_delete_repl(pkpy::REPL* repl){ - delete repl; - } - - PK_INLINE_EXPORT - void pkpy_vm_gc_on_delete(pkpy::VM* vm, void (*f)(pkpy::VM *, pkpy::PyObject *)){ - vm->heap._gc_on_delete = f; - } -} +} // namespace pkpy \ No newline at end of file diff --git a/include/pocketpy/pocketpy_c.h b/include/pocketpy/pocketpy_c.h new file mode 100644 index 00000000..94a72427 --- /dev/null +++ b/include/pocketpy/pocketpy_c.h @@ -0,0 +1,103 @@ +#ifndef POCKETPY_C_H +#define POCKETPY_C_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "export.h" + +typedef struct pkpy_vm_handle pkpy_vm; +typedef int (*pkpy_CFunction)(pkpy_vm*); +typedef int pkpy_CName; +typedef int pkpy_CType; + +typedef struct{ + const char* data; + int size; +}pkpy_CString; + +/* Basic Functions */ +PK_EXPORT pkpy_vm* pkpy_new_vm(bool enable_os); +PK_EXPORT void pkpy_delete_vm(pkpy_vm*); +PK_EXPORT bool pkpy_exec(pkpy_vm*, const char* source); +PK_EXPORT bool pkpy_exec_2(pkpy_vm*, const char* source, const char* filename, int mode, const char* module); + +/* Stack Manipulation */ +PK_EXPORT bool pkpy_pop(pkpy_vm*, int); +PK_EXPORT bool pkpy_pop_top(pkpy_vm*); +PK_EXPORT bool pkpy_dup_top(pkpy_vm*); +PK_EXPORT bool pkpy_rot_two(pkpy_vm*); +PK_EXPORT int pkpy_stack_size(pkpy_vm*); + +// int +PK_EXPORT bool pkpy_push_int(pkpy_vm*, int); +PK_EXPORT bool pkpy_is_int(pkpy_vm*, int i); +PK_EXPORT bool pkpy_to_int(pkpy_vm*, int i, int* out); + +// float +PK_EXPORT bool pkpy_push_float(pkpy_vm*, float); +PK_EXPORT bool pkpy_is_float(pkpy_vm*, int i); +PK_EXPORT bool pkpy_to_float(pkpy_vm*, int i, float* out); + +// bool +PK_EXPORT bool pkpy_push_bool(pkpy_vm*, bool); +PK_EXPORT bool pkpy_is_bool(pkpy_vm*, int i); +PK_EXPORT bool pkpy_to_bool(pkpy_vm*, int i, bool* out); + +// string +PK_EXPORT bool pkpy_push_string(pkpy_vm*, const char*); +PK_EXPORT bool pkpy_is_string(pkpy_vm*, int i); +PK_EXPORT bool pkpy_to_string(pkpy_vm*, int i, pkpy_CString* out); + +// void_p +PK_EXPORT bool pkpy_push_voidp(pkpy_vm*, void*); +PK_EXPORT bool pkpy_is_voidp(pkpy_vm*, int i); +PK_EXPORT bool pkpy_to_voidp(pkpy_vm*, int i, void** out); + +// none +PK_EXPORT bool pkpy_push_none(pkpy_vm*); +PK_EXPORT bool pkpy_is_none(pkpy_vm*, int i); + +// null +PK_EXPORT bool pkpy_push_null(pkpy_vm*); + +// special push +PK_EXPORT bool pkpy_push_function(pkpy_vm*, const char*, pkpy_CFunction); +PK_EXPORT bool pkpy_push_module(pkpy_vm*, const char*); + +// some opt +PK_EXPORT bool pkpy_load_attr(pkpy_vm*, pkpy_CName); +PK_EXPORT bool pkpy_store_attr(pkpy_vm*, pkpy_CName); +PK_EXPORT bool pkpy_load_global(pkpy_vm*, pkpy_CName); +PK_EXPORT bool pkpy_store_global(pkpy_vm*, pkpy_CName); +PK_EXPORT bool pkpy_eval(pkpy_vm*, const char* source); +PK_EXPORT bool pkpy_unpack_sequence(pkpy_vm*, int size); +PK_EXPORT bool pkpy_get_unbound_method(pkpy_vm*, pkpy_CName); + +/* Error Handling */ +PK_EXPORT bool pkpy_error(pkpy_vm*, const char* name, const char* message); +PK_EXPORT bool pkpy_check_error(pkpy_vm*); +PK_EXPORT bool pkpy_clear_error(pkpy_vm*, char** message); + +/* Callables */ +PK_EXPORT bool pkpy_vectorcall(pkpy_vm*, int argc); + +/* Special APIs */ +PK_EXPORT void pkpy_free(void* p); +PK_EXPORT pkpy_CName pkpy_name(const char*); +PK_EXPORT void pkpy_compile_to_string(pkpy_vm*, const char* source, const char* filename, int mode, bool* ok, char** out); + +/* REPL */ +PK_EXPORT void* pkpy_new_repl(pkpy_vm* vm); +PK_EXPORT bool pkpy_repl_input(void* r, const char* line); +PK_EXPORT void pkpy_delete_repl(void* repl); +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/include/pocketpy/repl.h b/include/pocketpy/repl.h index 399efa14..4c02083a 100644 --- a/include/pocketpy/repl.h +++ b/include/pocketpy/repl.h @@ -4,9 +4,7 @@ #include "vm.h" namespace pkpy{ - -std::string platform_getline(bool* eof=nullptr); - + class REPL { protected: int need_more_lines = 0; diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index f8ccefa8..44e1b756 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -117,6 +117,10 @@ public: NameDict _modules; // loaded modules std::map _lazy_modules; // lazy loaded modules + struct{ + PyObject* error; + } _c; + PyObject* None; PyObject* True; PyObject* False; @@ -453,8 +457,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) diff --git a/include/pocketpy_c.h b/include/pocketpy_c.h new file mode 100644 index 00000000..9e662f8e --- /dev/null +++ b/include/pocketpy_c.h @@ -0,0 +1,3 @@ +#pragma once + +#include "pocketpy/pocketpy_c.h" \ No newline at end of file diff --git a/src/ceval.cpp b/src/ceval.cpp index 35fbe982..559aba2f 100644 --- a/src/ceval.cpp +++ b/src/ceval.cpp @@ -108,7 +108,7 @@ __NEXT_STEP:; TARGET(LOAD_FAST) { heap._auto_collect(); _0 = frame->_locals[byte.arg]; - if(_0 == PY_NULL) vm->NameError(co->varnames[byte.arg]); + if(_0 == PY_NULL) vm->UnboundLocalError(co->varnames[byte.arg]); PUSH(_0); } DISPATCH(); TARGET(LOAD_NAME) { @@ -201,7 +201,7 @@ __NEXT_STEP:; DISPATCH(); TARGET(DELETE_FAST) _0 = frame->_locals[byte.arg]; - if(_0 == PY_NULL) vm->NameError(co->varnames[byte.arg]); + if(_0 == PY_NULL) vm->UnboundLocalError(co->varnames[byte.arg]); frame->_locals[byte.arg] = PY_NULL; DISPATCH(); TARGET(DELETE_NAME) diff --git a/src/codeobject.cpp b/src/codeobject.cpp index 95a8f3d4..65ce9286 100644 --- a/src/codeobject.cpp +++ b/src/codeobject.cpp @@ -158,14 +158,12 @@ void CodeObjectSerializer::write_code(VM* vm, const CodeObject* co){ this->f = f; this->argc = argc; if(argc != -1) this->argc += (int)method; - _has_userdata = false; } NativeFunc::NativeFunc(NativeFuncC f, FuncDecl_ decl){ this->f = f; this->argc = -1; this->decl = decl; - _has_userdata = false; } } // namespace pkpy \ No newline at end of file diff --git a/src/frame.cpp b/src/frame.cpp index 0b4cea45..5df2957c 100644 --- a/src/frame.cpp +++ b/src/frame.cpp @@ -58,7 +58,7 @@ namespace pkpy{ if(_next_ip >= co->codes.size()){ while(i>=0) i = _exit_block(i); }else{ - // BUG!!! + // BUG (solved) // for i in range(4): // _ = 0 // # if there is no op here, the block check will fail diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index b9d368a9..c5e4604b 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -6,22 +6,38 @@ using dylib_entry_t = PyObject*(*)(VM*, const char*); #if PK_ENABLE_OS -#if _WIN32 +#if PK_SUPPORT_DYLIB == 1 static dylib_entry_t load_dylib(const char* path){ std::error_code ec; auto p = std::filesystem::absolute(path, ec); if(ec) return nullptr; - HMODULE handle = LoadLibraryA((LPCSTR)p.c_str()); + HMODULE handle = LoadLibraryA((LPCSTR)"test.dll"); + // get last error + // Get the last error code + SetErrorMode(SEM_FAILCRITICALERRORS); + DWORD errorCode = GetLastError(); + + // Convert the error code to text + LPSTR errorMessage = nullptr; + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&errorMessage, + 0, + nullptr + ); + + // Print the error message to stdout + printf("%lu: %s\n", errorCode, errorMessage); + + // Free the message buffer + LocalFree(errorMessage); if(!handle) return nullptr; return (dylib_entry_t)GetProcAddress(handle, "platform_module__init__"); } -#elif __EMSCRIPTEN__ - -static dylib_entry_t load_dylib(const char* path){ - return nullptr; -} - -#elif __unix__ +#elif PK_SUPPORT_DYLIB == 2 static dylib_entry_t load_dylib(const char* path){ std::error_code ec; diff --git a/src/pocketpy_c.cpp b/src/pocketpy_c.cpp new file mode 100644 index 00000000..2580c916 --- /dev/null +++ b/src/pocketpy_c.cpp @@ -0,0 +1,520 @@ +#include "pocketpy.h" +#include "pocketpy/obj.h" +#include "pocketpy/pocketpy_c.h" +#include "pocketpy/tuplelist.h" +#include "pocketpy_c.h" + +using namespace pkpy; + +typedef int (*LuaStyleFuncC)(VM*); + +#define C_API_ASSERT(x) if(!(x)) { pkpy_error(vm_handle, "AssertionError", #x); return false; } + +#define PK_ASSERT_N_EXTRA_ELEMENTS(n) \ + if(!has_n_extra_elements(vm, n)){ \ + pkpy_error(vm_handle, "StackError", "not enough elements on stack"); \ + return false; \ + } + +#define PK_ASSERT_NO_ERROR() \ + if(vm->_c.error != nullptr) \ + return false; + +static int has_n_extra_elements(VM* vm, int n){ + if(vm->callstack.empty()){ + return vm->s_data.size() >= n; + } + PyObject** base = vm->top_frame()->_locals.end(); + return vm->s_data._sp - base >= n; +} + +static PyObject* stack_item(VM* vm, int index){ + PyObject** begin; + PyObject** end; + if(vm->callstack.empty()){ + begin = vm->s_data.begin(); + end = vm->s_data.end(); + }else{ + Frame* frame = vm->top_frame().get(); + begin = frame->_locals.begin(); + end = frame->_locals.end(); + } + // may raise + index = vm->normalized_index(index, end-begin); + return begin[index]; +} + +#define PK_PROTECTED(__B) \ + try{ __B } \ + catch(Exception& e ) { \ + vm->_c.error = py_var(vm, e); \ + return false; \ + } catch(const std::exception& re){ \ + auto e = Exception("std::exception", re.what()); \ + vm->_c.error = py_var(vm, e); \ + return false; \ + } + +pkpy_vm* pkpy_new_vm(bool enable_os){ + return (pkpy_vm*)new VM(enable_os); +} + +void pkpy_delete_vm(pkpy_vm* vm){ + return delete (VM*)vm; +} + +bool pkpy_exec(pkpy_vm* vm_handle, const char* source) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PyObject* res; + PK_PROTECTED( + CodeObject_ code = vm->compile(source, "main.py", EXEC_MODE); + res = vm->_exec(code, vm->_main); + ) + return res != nullptr; +} + +bool pkpy_exec_2(pkpy_vm* vm_handle, const char* source, const char* filename, int mode, const char* module){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PyObject* res; + PyObject* mod; + PK_PROTECTED( + 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); + ) + return res != nullptr; +} + +bool pkpy_pop(pkpy_vm* vm_handle, int n){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_ASSERT_N_EXTRA_ELEMENTS(n) + vm->s_data.shrink(n); + return true; +} + +bool pkpy_pop_top(pkpy_vm* vm_handle){ + VM* vm = (VM*)vm_handle; + PK_ASSERT_NO_ERROR() + PK_ASSERT_N_EXTRA_ELEMENTS(1) + vm->s_data.pop(); + return true; +} + +bool pkpy_dup_top(pkpy_vm* vm_handle){ + VM* vm = (VM*)vm_handle; + PK_ASSERT_NO_ERROR() + PK_ASSERT_N_EXTRA_ELEMENTS(1) + vm->s_data.push(vm->s_data.top()); + return true; +} + +bool pkpy_rot_two(pkpy_vm* vm_handle){ + VM* vm = (VM*)vm_handle; + PK_ASSERT_NO_ERROR() + PK_ASSERT_N_EXTRA_ELEMENTS(2) + std::swap(vm->s_data.top(), vm->s_data.second()); + return true; +} + +int pkpy_stack_size(pkpy_vm* vm_handle){ + VM* vm = (VM*)vm_handle; + PK_ASSERT_NO_ERROR() + if(vm->callstack.empty()){ + return vm->s_data.size(); + } + return vm->top_frame()->stack_size(); +} + +// int +bool pkpy_push_int(pkpy_vm* vm_handle, int value) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PyObject* res; + PK_PROTECTED( + // int may overflow so we should protect it + res = py_var(vm, value); + ) + vm->s_data.push(res); + return true; +} + +bool pkpy_is_int(pkpy_vm* vm_handle, int i){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + return is_int(stack_item(vm, i)); + ) +} + +bool pkpy_to_int(pkpy_vm* vm_handle, int i, int* out){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* item = stack_item(vm, i); + *out = py_cast(vm, item); + ) + return true; +} + +// float +bool pkpy_push_float(pkpy_vm* vm_handle, float value) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PyObject* res = py_var(vm, value); + vm->s_data.push(res); + return true; +} + +bool pkpy_is_float(pkpy_vm* vm_handle, int i){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* item = stack_item(vm, i); + return is_float(item); + ) +} + +bool pkpy_to_float(pkpy_vm* vm_handle, int i, float* out){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* item = stack_item(vm, i); + *out = py_cast(vm, item); + ) + return true; +} + +// bool +bool pkpy_push_bool(pkpy_vm* vm_handle, bool value) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + vm->s_data.push(value ? vm->True : vm->False); + return true; +} + +bool pkpy_is_bool(pkpy_vm* vm_handle, int i){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* item = stack_item(vm, i); + return is_non_tagged_type(item, vm->tp_bool); + ) +} + +bool pkpy_to_bool(pkpy_vm* vm_handle, int i, bool* out){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* item = stack_item(vm, i); + *out = py_cast(vm, item); + ) + return true; +} + +// string +bool pkpy_push_string(pkpy_vm* vm_handle, const char* value) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PyObject* res = py_var(vm, value); + vm->s_data.push(res); + return true; +} + +bool pkpy_is_string(pkpy_vm* vm_handle, int i){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* item = stack_item(vm, i); + return is_non_tagged_type(item, vm->tp_str); + ) +} + +bool pkpy_to_string(pkpy_vm* vm_handle, int i, pkpy_CString* out){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* item = stack_item(vm, i); + const Str& s = py_cast(vm, item); + out->data = s.data; + out->size = s.size; + ) + return true; +} + +// void_p +bool pkpy_push_voidp(pkpy_vm* vm_handle, void* value) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PyObject* res = py_var(vm, value); + vm->s_data.push(res); + return true; +} + +bool pkpy_is_voidp(pkpy_vm* vm_handle, int i){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* item = stack_item(vm, i); + return is_non_tagged_type(item, VoidP::_type(vm)); + ) +} + +bool pkpy_to_voidp(pkpy_vm* vm_handle, int i, void** out){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* item = stack_item(vm, i); + VoidP& vp = py_cast(vm, item); + *out = vp.ptr; + ) + return true; +} + +// none +bool pkpy_push_none(pkpy_vm* vm_handle) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + vm->s_data.push(vm->None); + return true; +} + +bool pkpy_is_none(pkpy_vm* vm_handle, int i){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* item = stack_item(vm, i); + return item == vm->None; + ) +} + +// null +bool pkpy_push_null(pkpy_vm* vm_handle) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + vm->s_data.push(PY_NULL); + return true; +} + +// function +static PyObject* c_function_wrapper(VM* vm, ArgsView args) { + LuaStyleFuncC f = lambda_get_userdata(args.begin()); + PyObject** curr_sp = &vm->s_data.top(); + int retc = f(vm); + // propagate_if_errored + if (vm->_c.error != nullptr){ + Exception e = _py_cast(vm, vm->_c.error); + vm->_c.error = nullptr; + vm->_error(e); + } + 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, const char* sig, pkpy_CFunction f) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PyObject* f_obj; + PK_PROTECTED( + f_obj = vm->bind( + nullptr, + sig, + nullptr, + c_function_wrapper, + f + ); + ) + vm->s_data.push(f_obj); + return true; +} + +// special push +bool pkpy_push_module(pkpy_vm* vm_handle, const char* name) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyObject* module = vm->new_module(name); + vm->s_data.push(module); + ) + return true; +} + +// some opt +bool pkpy_load_attr(pkpy_vm* vm_handle, pkpy_CName name) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_ASSERT_N_EXTRA_ELEMENTS(1) + PyObject* o = vm->s_data.top(); + PK_PROTECTED( + o = vm->getattr(o, StrName(name)); + ) + vm->s_data.top() = o; + return true; +} + +bool pkpy_store_attr(pkpy_vm* vm_handle, pkpy_CName name) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_ASSERT_N_EXTRA_ELEMENTS(2) + PyObject* a = vm->s_data.top(); + PyObject* val = vm->s_data.second(); + PK_PROTECTED( + vm->setattr(a, StrName(name), val); + ) + vm->s_data.shrink(2); + return true; +} + +//get global will also get bulitins +bool pkpy_load_global(pkpy_vm* vm_handle, const char* name) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PyObject* o = vm->_main->attr().try_get(name); + if (o == nullptr) { + o = vm->builtins->attr().try_get(name); + if (o == nullptr){ + pkpy_error(vm_handle, "NameError", name); + return false; + } + } + vm->s_data.push(o); + return true; +} + +bool pkpy_store_global(pkpy_vm* vm_handle, const char* name) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_ASSERT_N_EXTRA_ELEMENTS(1) + vm->_main->attr().set(name, vm->s_data.popx()); + return true; +} + +bool pkpy_eval(pkpy_vm* vm_handle, const char* source) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + CodeObject_ co = vm->compile(source, "", EVAL_MODE); + PyObject* ret = vm->_exec(co, vm->_main); + vm->s_data.push(ret); + ) + return true; +} + +bool pkpy_unpack_sequence(pkpy_vm* vm_handle, int n) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_ASSERT_N_EXTRA_ELEMENTS(1) + auto _lock = vm->heap.gc_scope_lock(); + PK_PROTECTED( + PyObject* _0 = vm->py_iter(vm->s_data.popx()); + for(int i=0; ipy_next(_0); + if(_1 == vm->StopIteration) vm->ValueError("not enough values to unpack"); + vm->s_data.push(_1); + } + if(vm->py_next(_0) != vm->StopIteration) vm->ValueError("too many values to unpack"); + ) + return true; +} + +bool pkpy_get_unbound_method(pkpy_vm* vm_handle, pkpy_CName name){ + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_ASSERT_N_EXTRA_ELEMENTS(1) + PyObject* o = vm->s_data.top(); + PyObject* self; + PK_PROTECTED( + o = vm->get_unbound_method(o, StrName(name), &self); + ) + vm->s_data.shrink(2); + vm->s_data.push(o); + vm->s_data.push(self); + return true; +} + +/* Error Handling */ +bool pkpy_error(pkpy_vm* vm_handle, const char* name, const char* message) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + vm->_c.error = py_var(vm, Exception(name, message)); + return false; +} + +bool pkpy_check_error(pkpy_vm* vm_handle) { + VM* vm = (VM*) vm_handle; + return vm->_c.error != nullptr; +} + +bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) { + VM* vm = (VM*) vm_handle; + // no error + if (vm->_c.error == nullptr) return false; + Exception& e = _py_cast(vm, vm->_c.error); + if (message != nullptr) + *message = e.summary().c_str_dup(); + else + std::cerr << "ERROR: " << e.summary() << "\n"; + vm->_c.error = nullptr; + // clear the whole stack?? + vm->callstack.clear(); + vm->s_data.clear(); + return true; +} + +bool pkpy_vectorcall(pkpy_vm* vm_handle, int argc) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_N_EXTRA_ELEMENTS(argc + 2) + PyObject* res; + PK_PROTECTED( + res = vm->vectorcall(argc); + ) + vm->s_data.push(res); + return true; +} +/*****************************************************************/ +void pkpy_free(void* p){ + free(p); +} + +pkpy_CName pkpy_name(const char* name){ + return StrName(name).index; +} + +void pkpy_compile_to_string(pkpy_vm* vm_handle, const char* source, const char* filename, int mode, bool* ok, char** out){ + VM* vm = (VM*) vm_handle; + try{ + CodeObject_ code = vm->compile(source, filename, (CompileMode)mode); + *out = code->serialize(vm).c_str_dup(); + *ok = true; + }catch(Exception& e){ + *ok = false; + *out = e.summary().c_str_dup(); + }catch(std::exception& e){ + *ok = false; + *out = strdup(e.what()); + }catch(...){ + *ok = false; + *out = strdup("unknown error"); + } +} + +void* pkpy_new_repl(pkpy_vm* vm_handle){ + return new REPL((VM*)vm_handle); +} + +bool pkpy_repl_input(void* r, const char* line){ + return ((REPL*)r)->input(line); +} + +void pkpy_delete_repl(void* repl){ + delete (REPL*)repl; +} \ No newline at end of file diff --git a/src/repl.cpp b/src/repl.cpp index e82184c0..815f98c3 100644 --- a/src/repl.cpp +++ b/src/repl.cpp @@ -1,39 +1,6 @@ #include "pocketpy/repl.h" namespace pkpy { - -#ifdef _WIN32 - -std::string platform_getline(bool* eof){ - HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); - std::wstringstream wss; - WCHAR buf; - DWORD read; - while (ReadConsoleW(hStdin, &buf, 1, &read, NULL) && buf != L'\n') { - if(eof && buf == L'\x1A') *eof = true; // Ctrl+Z - wss << buf; - } - std::wstring wideInput = wss.str(); - int length = WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(), (int)wideInput.length(), NULL, 0, NULL, NULL); - std::string output; - output.resize(length); - WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(), (int)wideInput.length(), &output[0], length, NULL, NULL); - if(!output.empty() && output.back() == '\r') output.pop_back(); - return output; -} - -#else - -std::string platform_getline(bool* eof){ - std::string line; - if(!std::getline(std::cin, line)){ - if(eof) *eof = true; - } - return line; -} - -#endif - REPL::REPL(VM* vm) : vm(vm){ vm->_stdout(vm, "pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") "); vm->_stdout(vm, fmt("[", sizeof(void*)*8, " bit]" "\n")); diff --git a/src/vm.cpp b/src/vm.cpp index fed28252..d2c3fea3 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -956,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 @@ -977,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; } diff --git a/src2/lib.cpp b/src2/lib.cpp deleted file mode 100644 index a11446e0..00000000 --- a/src2/lib.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pocketpy.h" \ No newline at end of file diff --git a/src2/main.cpp b/src2/main.cpp index 1e3b8cad..392c3272 100644 --- a/src2/main.cpp +++ b/src2/main.cpp @@ -1,31 +1,64 @@ #include #include +#include -#include "pocketpy.h" +#include "pocketpy_c.h" -std::string f_input(){ - return pkpy::platform_getline(); + +#ifdef _WIN32 + +std::string pkpy_platform_getline(bool* eof){ + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + std::wstringstream wss; + WCHAR buf; + DWORD read; + while (ReadConsoleW(hStdin, &buf, 1, &read, NULL) && buf != L'\n') { + if(eof && buf == L'\x1A') *eof = true; // Ctrl+Z + wss << buf; + } + std::wstring wideInput = wss.str(); + int length = WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(), (int)wideInput.length(), NULL, 0, NULL, NULL); + std::string output; + output.resize(length); + WideCharToMultiByte(CP_UTF8, 0, wideInput.c_str(), (int)wideInput.length(), &output[0], length, NULL, NULL); + if(!output.empty() && output.back() == '\r') output.pop_back(); + return output; } +#else + +std::string pkpy_platform_getline(bool* eof){ + std::string output; + if(!std::getline(std::cin, output)){ + if(eof) *eof = true; + } + return output; +} + +#endif + +// std::string f_input(){ +// return pkpy::platform_getline(); +// } + int main(int argc, char** argv){ #if _WIN32 - SetConsoleOutputCP(CP_UTF8); // implicitly load pocketpy.dll in current directory #elif __linux__ dlopen("libpocketpy.so", RTLD_NOW | RTLD_GLOBAL); #elif __APPLE__ dlopen("libpocketpy.dylib", RTLD_NOW | RTLD_GLOBAL); #endif - pkpy::VM* vm = pkpy_new_vm(); - pkpy::_bind(vm, vm->builtins, "input() -> str", &f_input); + pkpy_vm* vm = pkpy_new_vm(true); + // pkpy::_bind(vm, vm->builtins, "input() -> str", &f_input); if(argc == 1){ - pkpy::REPL* repl = pkpy_new_repl(vm); + void* repl = pkpy_new_repl(vm); bool need_more_lines = false; while(true){ - vm->_stdout(vm, need_more_lines ? "... " : ">>> "); + std::cout << (need_more_lines ? "... " : ">>> "); bool eof = false; - std::string line = pkpy::platform_getline(&eof); + std::string line = pkpy_platform_getline(&eof); if(eof) break; need_more_lines = pkpy_repl_input(repl, line.c_str()); } @@ -54,10 +87,10 @@ int main(int argc, char** argv){ // set parent path as cwd std::filesystem::current_path(filepath.parent_path()); - pkpy::PyObject* ret = nullptr; - ret = vm->exec(src.c_str(), filepath.filename().string(), pkpy::EXEC_MODE); + pkpy_exec_2(vm, src.c_str(), filepath.filename().string().c_str(), 0, NULL); pkpy_delete_vm(vm); - return ret != nullptr ? 0 : 1; + // return ret != nullptr ? 0 : 1; + return 0; } __HELP: diff --git a/tests/dylib/test.cpp b/tests/dylib/test.cpp index 29d2ce4c..1bda2734 100644 --- a/tests/dylib/test.cpp +++ b/tests/dylib/test.cpp @@ -1,4 +1,3 @@ -#define PK_SHARED_MODULE #include "pocketpy.h" using namespace pkpy; @@ -15,15 +14,4 @@ extern "C" { }); return mod; } - -#if _WIN32 -BOOL WINAPI DllMain( - HINSTANCE hinstDLL, // handle to DLL module - DWORD fdwReason, // reason for calling function - LPVOID lpvReserved ) // reserved -{ - return TRUE; // Successful DLL_PROCESS_ATTACH. -} -#endif - } \ No newline at end of file