Compare commits
4 commits
master
...
python_mod
Author | SHA1 | Date | |
---|---|---|---|
86ae68dd50 | |||
dd259ef5bb | |||
ce6f67422f | |||
33b74fc7e4 |
6 changed files with 249 additions and 33 deletions
|
@ -105,6 +105,7 @@ namespace wren {
|
||||||
bool has_user_data() const;
|
bool has_user_data() const;
|
||||||
|
|
||||||
std::size_t dynafunc_byte_size() const;
|
std::size_t dynafunc_byte_size() const;
|
||||||
|
void reset (Configuration* conf);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct LocalData;
|
struct LocalData;
|
||||||
|
|
8
links.txt
Normal file
8
links.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
https://stackoverflow.com/questions/36093158/how-does-string-conversion-between-pyunicode-string-and-c-string-work
|
||||||
|
https://stackoverflow.com/questions/8001923/python-extension-module-with-variable-number-of-arguments
|
||||||
|
https://stackoverflow.com/questions/27451743/how-can-i-print-the-type-of-a-pyobject-in-an-error-message-for-an-embedded-pytho/27452057
|
||||||
|
|
||||||
|
https://docs.python.org/3.8/c-api/unicode.html#creating-and-accessing-unicode-strings
|
||||||
|
https://docs.python.org/3/extending/extending.html
|
||||||
|
|
||||||
|
https://stackoverflow.com/questions/28263177/should-tp-alloc-tp-dealloc-and-tp-new-tp-free-be-considered-as-pairs
|
20
meson.build
20
meson.build
|
@ -4,6 +4,8 @@ project('wrenpp', 'cpp',
|
||||||
default_options: ['buildtype=release', 'cpp_std=c++17', 'b_ndebug=if-release'],
|
default_options: ['buildtype=release', 'cpp_std=c++17', 'b_ndebug=if-release'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
pymod = import('python')
|
||||||
|
|
||||||
wren_dep = dependency('wren', version: '>=0.2.0',
|
wren_dep = dependency('wren', version: '>=0.2.0',
|
||||||
fallback: ['wren', 'wren_dep'],
|
fallback: ['wren', 'wren_dep'],
|
||||||
default_options: [
|
default_options: [
|
||||||
|
@ -14,6 +16,10 @@ wren_dep = dependency('wren', version: '>=0.2.0',
|
||||||
],
|
],
|
||||||
static: true,
|
static: true,
|
||||||
)
|
)
|
||||||
|
python = pymod.find_installation('python3',
|
||||||
|
required: get_option('wrenpp_with_python'),
|
||||||
|
disabler: true
|
||||||
|
)
|
||||||
public_incl = include_directories('include')
|
public_incl = include_directories('include')
|
||||||
|
|
||||||
os = host_machine.system()
|
os = host_machine.system()
|
||||||
|
@ -65,6 +71,20 @@ wrenpp_dep = declare_dependency(
|
||||||
include_directories: public_incl,
|
include_directories: public_incl,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
python.extension_module('py' + meson.project_name(),
|
||||||
|
sources: [
|
||||||
|
'src/pywrenppmodule.cpp',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
python.dependency(
|
||||||
|
version: '>=3.6.0',
|
||||||
|
required: get_option('wrenpp_with_python'),
|
||||||
|
),
|
||||||
|
wrenpp_dep,
|
||||||
|
],
|
||||||
|
install: true,
|
||||||
|
)
|
||||||
|
|
||||||
if get_option('build_examples')
|
if get_option('build_examples')
|
||||||
subdir('examples')
|
subdir('examples')
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -2,3 +2,4 @@ option('build_testing', type: 'boolean', value: false)
|
||||||
option('build_examples', type: 'boolean', value: false)
|
option('build_examples', type: 'boolean', value: false)
|
||||||
option('wren_with_rand', type: 'boolean', value: false, yield: true)
|
option('wren_with_rand', type: 'boolean', value: false, yield: true)
|
||||||
option('wren_with_meta', type: 'boolean', value: false, yield: true)
|
option('wren_with_meta', type: 'boolean', value: false, yield: true)
|
||||||
|
option('wrenpp_with_python', type: 'feature', value: 'disabled')
|
||||||
|
|
178
src/pywrenppmodule.cpp
Normal file
178
src/pywrenppmodule.cpp
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include <Python.h>
|
||||||
|
#include "wrenpp/vm.hpp"
|
||||||
|
#include "wrenpp/vm_fun.hpp"
|
||||||
|
#include "wrenpp/def_configuration.hpp"
|
||||||
|
#include "config.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <string_view>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define CURR_MODULE_NAME "py" WRENPP_NAME
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct VMObject;
|
||||||
|
int vm_init (VMObject* self, PyObject*, PyObject*);
|
||||||
|
PyObject* vm_new (PyTypeObject* type, PyObject*, PyObject*);
|
||||||
|
void vm_dealloc (VMObject* self);
|
||||||
|
PyObject* vm_interpret (VMObject* self, PyObject* args);
|
||||||
|
PyObject* vm_call (VMObject* self, PyObject* args);
|
||||||
|
|
||||||
|
class VMConfig : public wren::DefConfiguration {
|
||||||
|
public:
|
||||||
|
wren::foreign_method_t foreign_method_fn (
|
||||||
|
wren::VM* vm,
|
||||||
|
const char* module_ptr,
|
||||||
|
const char* class_name_ptr,
|
||||||
|
bool is_static,
|
||||||
|
const char* signature_ptr
|
||||||
|
) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PyMethodDef WrenppMethods[] = {
|
||||||
|
//{"eval_script", &eval_script, METH_VARARGS, "Evaluate a Wren script"},
|
||||||
|
{nullptr, nullptr, 0, nullptr}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PyModuleDef WrenppModule = {
|
||||||
|
PyModuleDef_HEAD_INIT,
|
||||||
|
CURR_MODULE_NAME, //module name
|
||||||
|
"Python bindings for Wrenpp", //module documentation
|
||||||
|
-1, //size of per-interpreter state of the module, or -1 if the module keeps state in global variables
|
||||||
|
WrenppMethods
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VMObject {
|
||||||
|
PyObject_HEAD
|
||||||
|
wren::VM vm;
|
||||||
|
};
|
||||||
|
//PyMemberDef VMProperties[] = {
|
||||||
|
// {nullptr}
|
||||||
|
//};
|
||||||
|
PyMethodDef VMMethods[] = {
|
||||||
|
{
|
||||||
|
"interpret",
|
||||||
|
reinterpret_cast<PyCFunction>(vm_interpret),
|
||||||
|
METH_VARARGS,
|
||||||
|
"Interpret a string script"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"call",
|
||||||
|
reinterpret_cast<PyCFunction>(vm_call),
|
||||||
|
METH_VARARGS,
|
||||||
|
"Call a method from a previously parsed script"
|
||||||
|
},
|
||||||
|
{nullptr}
|
||||||
|
};
|
||||||
|
|
||||||
|
PyTypeObject VMType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
.tp_name = CURR_MODULE_NAME ".VM",
|
||||||
|
.tp_basicsize = sizeof(VMObject),
|
||||||
|
.tp_itemsize = 0,
|
||||||
|
.tp_dealloc = reinterpret_cast<destructor>(vm_dealloc),
|
||||||
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||||
|
.tp_doc = "Wrenpp VM",
|
||||||
|
.tp_methods = VMMethods,
|
||||||
|
.tp_members = nullptr, //VMProperties,
|
||||||
|
.tp_init = reinterpret_cast<initproc>(vm_init),
|
||||||
|
.tp_new = vm_new,
|
||||||
|
};
|
||||||
|
|
||||||
|
PyObject* vm_new (PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwds*/) {
|
||||||
|
VMObject* const self = reinterpret_cast<VMObject*>(type->tp_alloc(type, 0));
|
||||||
|
if (self) {
|
||||||
|
try {
|
||||||
|
VMConfig config;
|
||||||
|
new(&self->vm) wren::VM(&config, nullptr);
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
Py_DECREF(self);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reinterpret_cast<PyObject*>(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vm_dealloc (VMObject* self) {
|
||||||
|
self->vm.~VM();
|
||||||
|
}
|
||||||
|
|
||||||
|
int vm_init (VMObject* self, PyObject* /*args*/, PyObject* /*kwds*/) {
|
||||||
|
VMConfig config;
|
||||||
|
self->vm.reset(&config);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* vm_interpret (VMObject* self, PyObject* args) {
|
||||||
|
const char* module_name;
|
||||||
|
const char* script;
|
||||||
|
if (!PyArg_ParseTuple(args, "ss", &module_name, &script))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
self->vm.interpret(module_name, script);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* vm_call (VMObject* self, PyObject* args) {
|
||||||
|
const Py_ssize_t argc = PyTuple_Size(args);
|
||||||
|
std::vector<PyObject*> objects;
|
||||||
|
objects.reserve(argc);
|
||||||
|
|
||||||
|
self->vm.ensure_slots(static_cast<std::size_t>(argc) + 1);
|
||||||
|
variable(self->vm, wren::ModuleAndName{"main", "the_user"}, 0);
|
||||||
|
|
||||||
|
//TODO: missing error checks, see
|
||||||
|
//https://stackoverflow.com/questions/8001923/python-extension-module-with-variable-number-of-arguments
|
||||||
|
|
||||||
|
std::string func_sig;
|
||||||
|
{
|
||||||
|
Py_ssize_t sz;
|
||||||
|
PyObject* const obj = PyTuple_GetItem(args, 0);
|
||||||
|
const char* const buf = PyUnicode_AsUTF8AndSize(obj, &sz);
|
||||||
|
func_sig.reserve(sz + 3);
|
||||||
|
func_sig.append(buf, sz);
|
||||||
|
func_sig.append(1, '(');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Py_ssize_t z = 1; z < argc; ++z) {
|
||||||
|
PyObject* const obj = PyTuple_GetItem(args, z);
|
||||||
|
if (not obj)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const std::string_view type_name(Py_TYPE(obj)->tp_name);
|
||||||
|
//std::cout << "vm_call, parameter " << z+1 << " has type \"" << type_name << "\"\n";
|
||||||
|
if (type_name == "str") {
|
||||||
|
const char* const buf = PyUnicode_AsUTF8(obj);
|
||||||
|
self->vm.set_slot_string(static_cast<std::size_t>(z), buf);
|
||||||
|
}
|
||||||
|
func_sig += "_,";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (func_sig.back() == ',')
|
||||||
|
func_sig.resize(func_sig.size() - 1);
|
||||||
|
func_sig += ")";
|
||||||
|
auto handle = self->vm.make_call_handle(func_sig.c_str());
|
||||||
|
self->vm.call(handle);
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
} //unnamed namespace
|
||||||
|
|
||||||
|
PyMODINIT_FUNC PyInit_pywrenpp(void) {
|
||||||
|
if (PyType_Ready(&VMType) < 0)
|
||||||
|
return nullptr;
|
||||||
|
PyObject* const m = PyModule_Create(&WrenppModule);
|
||||||
|
if (not m)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Py_INCREF(&VMType);
|
||||||
|
if (PyModule_AddObject(m, "VM", reinterpret_cast<PyObject*>(&VMType)) < 0) {
|
||||||
|
Py_DECREF(&VMType);
|
||||||
|
Py_DECREF(m);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
74
src/vm.cpp
74
src/vm.cpp
|
@ -137,39 +137,7 @@ namespace wren {
|
||||||
VM::VM (Configuration* conf, const detail::Callbacks& cb, void* user_data, std::uint32_t user_data_type) :
|
VM::VM (Configuration* conf, const detail::Callbacks& cb, void* user_data, std::uint32_t user_data_type) :
|
||||||
m_local(std::make_unique<LocalData>(cb))
|
m_local(std::make_unique<LocalData>(cb))
|
||||||
{
|
{
|
||||||
WrenConfiguration wconf;
|
this->reset(conf);
|
||||||
wrenInitConfiguration(&wconf);
|
|
||||||
wconf.userData = static_cast<void*>(&m_local->callbacks);
|
|
||||||
|
|
||||||
wconf.initialHeapSize = conf->initial_heap_size();
|
|
||||||
wconf.minHeapSize = conf->min_heap_size();
|
|
||||||
wconf.heapGrowthPercent = conf->heap_growth_percent();
|
|
||||||
|
|
||||||
if (cb.write_fn)
|
|
||||||
wconf.writeFn = &write_fn;
|
|
||||||
|
|
||||||
if (cb.error_fn)
|
|
||||||
wconf.errorFn = &error_fn;
|
|
||||||
|
|
||||||
if (cb.reallocate_fn)
|
|
||||||
wconf.reallocateFn = cb.reallocate_fn;
|
|
||||||
|
|
||||||
if (cb.resolve_module_fn)
|
|
||||||
wconf.resolveModuleFn = &resolve_module_fn;
|
|
||||||
|
|
||||||
if (cb.load_module_fn)
|
|
||||||
wconf.loadModuleFn = &load_module_fn;
|
|
||||||
|
|
||||||
if (cb.foreign_method_fn)
|
|
||||||
wconf.bindForeignMethodFn = &foreign_method_fn;
|
|
||||||
|
|
||||||
if (cb.foreign_class_fn)
|
|
||||||
wconf.bindForeignClassFn = &foreign_class_fn;
|
|
||||||
|
|
||||||
m_local->wvm = wrenNewVM(&wconf);
|
|
||||||
if (not m_local->wvm)
|
|
||||||
throw std::runtime_error("Failed to initialize Wren VM");
|
|
||||||
|
|
||||||
m_local->user_data = user_data;
|
m_local->user_data = user_data;
|
||||||
m_local->user_data_type = user_data_type;
|
m_local->user_data_type = user_data_type;
|
||||||
}
|
}
|
||||||
|
@ -275,6 +243,46 @@ namespace wren {
|
||||||
return m_local->dynafunc.total_memory();
|
return m_local->dynafunc.total_memory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VM::reset (Configuration* conf) {
|
||||||
|
WrenConfiguration wconf;
|
||||||
|
wrenInitConfiguration(&wconf);
|
||||||
|
wconf.userData = static_cast<void*>(&m_local->callbacks);
|
||||||
|
|
||||||
|
wconf.initialHeapSize = conf->initial_heap_size();
|
||||||
|
wconf.minHeapSize = conf->min_heap_size();
|
||||||
|
wconf.heapGrowthPercent = conf->heap_growth_percent();
|
||||||
|
|
||||||
|
auto& cb = m_local->callbacks;
|
||||||
|
if (cb.write_fn)
|
||||||
|
wconf.writeFn = &write_fn;
|
||||||
|
|
||||||
|
if (cb.error_fn)
|
||||||
|
wconf.errorFn = &error_fn;
|
||||||
|
|
||||||
|
if (cb.reallocate_fn)
|
||||||
|
wconf.reallocateFn = cb.reallocate_fn;
|
||||||
|
|
||||||
|
if (cb.resolve_module_fn)
|
||||||
|
wconf.resolveModuleFn = &resolve_module_fn;
|
||||||
|
|
||||||
|
if (cb.load_module_fn)
|
||||||
|
wconf.loadModuleFn = &load_module_fn;
|
||||||
|
|
||||||
|
if (cb.foreign_method_fn)
|
||||||
|
wconf.bindForeignMethodFn = &foreign_method_fn;
|
||||||
|
|
||||||
|
if (cb.foreign_class_fn)
|
||||||
|
wconf.bindForeignClassFn = &foreign_class_fn;
|
||||||
|
|
||||||
|
if (m_local->wvm)
|
||||||
|
wrenFreeVM(m_local->wvm);
|
||||||
|
m_local->wvm = wrenNewVM(&wconf);
|
||||||
|
if (not m_local->wvm)
|
||||||
|
throw std::runtime_error("Failed to initialize Wren VM");
|
||||||
|
|
||||||
|
m_local->dynafunc.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void VM::ensure_slots (int num_slots) {
|
void VM::ensure_slots (int num_slots) {
|
||||||
wrenEnsureSlots(m_local->wvm, num_slots);
|
wrenEnsureSlots(m_local->wvm, num_slots);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue