diff --git a/CMakeLists.txt b/CMakeLists.txt index c406d834..844c414a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ endif() if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR- /EHsc /utf-8 /O2") else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fexceptions -O2") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fexceptions -O2 -ldl") endif() option(PK_EXPORT_C_API "Export C API" ON) diff --git a/amalgamate.py b/amalgamate.py index 08265c58..31cd9aa9 100644 --- a/amalgamate.py +++ b/amalgamate.py @@ -6,11 +6,11 @@ with open("include/pocketpy/opcodes.h", "rt", encoding='utf-8') as f: OPCODES_TEXT = '\n' + f.read() + '\n' pipeline = [ - ["config.h", "common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h", "lexer.h"], + ["config.h", "export.h", "common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h", "lexer.h"], ["obj.h", "dict.h", "codeobject.h", "frame.h"], ["gc.h", "vm.h", "ceval.h", "expr.h", "compiler.h", "repl.h"], ["_generated.h", "cffi.h", "bindings.h", "iter.h", "base64.h", "random.h", "re.h", "linalg.h", "easing.h", "io.h"], - ["export.h", "pocketpy.h"] + ["pocketpy.h"] ] copied = set() diff --git a/build.sh b/build.sh index faa91232..9bfa7d49 100644 --- a/build.sh +++ b/build.sh @@ -1,3 +1,3 @@ python3 prebuild.py SRC=$(find src/ -name "*.cpp") -clang++ -std=c++17 -fno-rtti -O2 -stdlib=libc++ -Wfatal-errors -o pocketpy src2/main.cpp $SRC -Iinclude \ No newline at end of file +clang++ -std=c++17 -fno-rtti -O2 -stdlib=libc++ -Wfatal-errors -o pocketpy src2/main.cpp $SRC -Iinclude -ldl \ No newline at end of file diff --git a/include/pocketpy/bindings.h b/include/pocketpy/bindings.h index e3cce869..2398a026 100644 --- a/include/pocketpy/bindings.h +++ b/include/pocketpy/bindings.h @@ -122,4 +122,10 @@ struct OpaquePointer{ return VAR(self->get_##NAME()); \ })); +#define PK_REGISTER_CONSTRUCTOR(T, T0) \ + vm->bind_constructor<2>(type, [](VM* vm, ArgsView args){ \ + void* p = CAST(void*, args[0]); \ + return VAR_T(T, (T0*)p); \ + }); + } // namespace pkpy \ No newline at end of file diff --git a/include/pocketpy/common.h b/include/pocketpy/common.h index f9417faa..a8df520d 100644 --- a/include/pocketpy/common.h +++ b/include/pocketpy/common.h @@ -23,6 +23,7 @@ #define PK_VERSION "1.0.9" #include "config.h" +#include "export.h" /*******************************************************************************/ #if PK_ENABLE_STD_FUNCTION @@ -34,7 +35,7 @@ #define THREAD_LOCAL thread_local #include -struct GIL { +struct PK_EXPORT GIL { inline static std::mutex _mutex; explicit GIL() { _mutex.lock(); } ~GIL() { _mutex.unlock(); } @@ -58,7 +59,7 @@ template struct NumberTraits; template <> -struct NumberTraits<4> { +struct PK_EXPORT NumberTraits<4> { using int_t = int32_t; using float_t = float; @@ -73,7 +74,7 @@ struct NumberTraits<4> { }; template <> -struct NumberTraits<8> { +struct PK_EXPORT NumberTraits<8> { using int_t = int64_t; using float_t = double; @@ -95,13 +96,13 @@ static_assert(sizeof(i64) == sizeof(void*)); static_assert(sizeof(f64) == sizeof(void*)); static_assert(std::numeric_limits::is_iec559); -struct Dummy { }; -struct DummyInstance { }; -struct DummyModule { }; -struct NoReturn { }; -struct Discarded { }; +struct PK_EXPORT Dummy { }; +struct PK_EXPORT DummyInstance { }; +struct PK_EXPORT DummyModule { }; +struct PK_EXPORT NoReturn { }; +struct PK_EXPORT Discarded { }; -struct Type { +struct PK_EXPORT Type { int index; Type(): index(-1) {} Type(int index): index(index) {} @@ -124,30 +125,32 @@ struct Type { struct PyObject; #define PK_BITS(p) (reinterpret_cast(p)) -inline bool is_tagged(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) != 0b00; } -inline bool is_int(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b01; } -inline bool is_float(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b10; } -inline bool is_special(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b11; } +PK_EXPORT inline bool is_tagged(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) != 0b00; } +PK_EXPORT inline bool is_int(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b01; } +PK_EXPORT inline bool is_float(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b10; } +PK_EXPORT inline bool is_special(PyObject* p) noexcept { return (PK_BITS(p) & 0b11) == 0b11; } -inline bool is_both_int_or_float(PyObject* a, PyObject* b) noexcept { +PK_EXPORT inline bool is_both_int_or_float(PyObject* a, PyObject* b) noexcept { return is_tagged(a) && is_tagged(b); } -inline bool is_both_int(PyObject* a, PyObject* b) noexcept { +PK_EXPORT inline bool is_both_int(PyObject* a, PyObject* b) noexcept { return is_int(a) && is_int(b); } -inline bool is_both_float(PyObject* a, PyObject* b) noexcept { +PK_EXPORT inline bool is_both_float(PyObject* a, PyObject* b) noexcept { return is_float(a) && is_float(b); } // special singals, is_tagged() for them is true -inline PyObject* const PY_NULL = (PyObject*)0b000011; // tagged null -inline PyObject* const PY_OP_CALL = (PyObject*)0b100011; -inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011; +PK_EXPORT inline PyObject* const PY_NULL = (PyObject*)0b000011; // tagged null +PK_EXPORT inline PyObject* const PY_OP_CALL = (PyObject*)0b100011; +PK_EXPORT 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 @@ -159,6 +162,12 @@ inline PyObject* const PY_OP_YIELD = (PyObject*)0b110011; #endif #include -#endif +#elif __unix__ -} // namespace pkpy \ No newline at end of file +#include + +#elif __EMSCRIPTEN__ + +#include + +#endif \ No newline at end of file diff --git a/include/pocketpy/export.h b/include/pocketpy/export.h index f885c6bd..f23a769e 100644 --- a/include/pocketpy/export.h +++ b/include/pocketpy/export.h @@ -6,9 +6,9 @@ #include #define PK_EXPORT EMSCRIPTEN_KEEPALIVE #else -#define PK_EXPORT __attribute__((visibility("default"))) __attribute__((used)) +#define PK_EXPORT __attribute__((visibility("default"))) #endif -#define PK_LEGACY_EXPORT PK_EXPORT inline +#define PK_INLINE_EXPORT PK_EXPORT inline #endif diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 92921813..1f4ca685 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -10,7 +10,6 @@ #include "easing.h" #include "io.h" #include "_generated.h" -#include "export.h" #include "vm.h" #include "re.h" #include "random.h" @@ -99,17 +98,17 @@ void add_module_gc(VM* vm); /*************************GLOBAL NAMESPACE*************************/ extern "C" { - PK_LEGACY_EXPORT + PK_INLINE_EXPORT void pkpy_free(void* p){ free(p); } - PK_LEGACY_EXPORT + PK_INLINE_EXPORT void pkpy_vm_exec(pkpy::VM* vm, const char* source){ vm->exec(source, "main.py", pkpy::EXEC_MODE); } - PK_LEGACY_EXPORT + 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; @@ -120,7 +119,7 @@ extern "C" { vm->exec(source, filename, (pkpy::CompileMode)mode, mod); } - PK_LEGACY_EXPORT + 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); @@ -138,39 +137,39 @@ extern "C" { } } - PK_LEGACY_EXPORT + PK_INLINE_EXPORT pkpy::REPL* pkpy_new_repl(pkpy::VM* vm){ pkpy::REPL* p = new pkpy::REPL(vm); return p; } - PK_LEGACY_EXPORT + PK_INLINE_EXPORT bool pkpy_repl_input(pkpy::REPL* r, const char* line){ return r->input(line); } - PK_LEGACY_EXPORT + PK_INLINE_EXPORT void pkpy_vm_add_module(pkpy::VM* vm, const char* name, const char* source){ vm->_lazy_modules[name] = source; } - PK_LEGACY_EXPORT + PK_INLINE_EXPORT pkpy::VM* pkpy_new_vm(bool enable_os=true){ pkpy::VM* p = new pkpy::VM(enable_os); return p; } - PK_LEGACY_EXPORT + PK_INLINE_EXPORT void pkpy_delete_vm(pkpy::VM* vm){ delete vm; } - PK_LEGACY_EXPORT + PK_INLINE_EXPORT void pkpy_delete_repl(pkpy::REPL* repl){ delete repl; } - PK_LEGACY_EXPORT + PK_INLINE_EXPORT void pkpy_vm_gc_on_delete(pkpy::VM* vm, void (*f)(pkpy::VM *, pkpy::PyObject *)){ vm->heap._gc_on_delete = f; } diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index 68e4ef4b..30c0b7d1 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -2,6 +2,38 @@ namespace pkpy{ +using dylib_entry_t = PyObject*(*)(VM*, const char*); + +#if PK_ENABLE_OS + +#if _WIN32 +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(p.c_str()); + if(!handle) return nullptr; + return (dylib_entry_t)GetProcAddress(handle, "platform_module__init__"); +} +#else +static dylib_entry_t load_dylib(const char* path){ + std::error_code ec; + auto p = std::filesystem::absolute(path, ec); + if(ec) return nullptr; + void* handle = dlopen(p.c_str(), RTLD_LAZY); + if(!handle) return nullptr; + return (dylib_entry_t)dlsym(handle, "platform_module__init__"); +} +#endif + +#else +static dylib_entry_t load_dylib(const char* path){ + PK_UNUSED(path); + return nullptr; +} +#endif + + void init_builtins(VM* _vm) { #define BIND_NUM_ARITH_OPT(name, op) \ _vm->bind##name(_vm->tp_int, [](VM* vm, PyObject* lhs, PyObject* rhs) { \ @@ -106,7 +138,19 @@ void init_builtins(VM* _vm) { }); _vm->bind_builtin_func<1>("__import__", [](VM* vm, ArgsView args) { - return vm->py_import(CAST(Str&, args[0])); + const Str& name = CAST(Str&, args[0]); + auto dot = name.sv().find_first_of("."); + if(dot != std::string_view::npos){ + auto ext = name.sv().substr(dot); + if(ext == ".so" || ext == ".dll" || ext == ".dylib" || ext == ".pyd"){ + dylib_entry_t entry = load_dylib(name.c_str()); + if(!entry){ + vm->_error("ImportError", "cannot load dynamic library: " + name.escape()); + } + return entry(vm, PK_VERSION); + } + } + return vm->py_import(name); }); _vm->bind_builtin_func<2>("divmod", [](VM* vm, ArgsView args) { diff --git a/tests/dylib/build.bat b/tests/dylib/build.bat new file mode 100644 index 00000000..e69de29b diff --git a/tests/dylib/build.sh b/tests/dylib/build.sh new file mode 100644 index 00000000..5fd6f97e --- /dev/null +++ b/tests/dylib/build.sh @@ -0,0 +1,2 @@ +SRC=$(find ../../src/ -name "*.cpp") +clang++ -std=c++17 -fno-rtti -O2 -stdlib=libc++ -Wfatal-errors -o libtest.so test.cpp -I../../include -fPIC -shared \ No newline at end of file diff --git a/tests/dylib/main.py b/tests/dylib/main.py new file mode 100644 index 00000000..4f737a99 --- /dev/null +++ b/tests/dylib/main.py @@ -0,0 +1,6 @@ +import os + +print(os.getcwd()) +test = __import__('libtest.so') + +test.hello() \ No newline at end of file diff --git a/tests/dylib/test.cpp b/tests/dylib/test.cpp new file mode 100644 index 00000000..983b4b6e --- /dev/null +++ b/tests/dylib/test.cpp @@ -0,0 +1,18 @@ +#include "pocketpy.h" + +using namespace pkpy; + +extern "C" { + PK_EXPORT + PyObject* platform_module__init__(VM* vm, const char* version){ + PyObject* mod = vm->new_module("test"); + vm->_stdout(vm, "Hello from dylib!\n"); + + vm->bind(mod, "hello()", [](VM* vm, ArgsView args){ + vm->_stdout(vm, "Hello from dylib!\n"); + return vm->None; + }); + return mod; + } + +} \ No newline at end of file