This commit is contained in:
blueloveTH 2023-06-29 19:12:30 +08:00
parent 22e74a16a3
commit 3de3e625ee
5 changed files with 116 additions and 120 deletions

View File

@ -3,6 +3,8 @@
using namespace pkpy; using namespace pkpy;
typedef int (*LuaStyleFuncC)(VM*);
struct LuaStack: public ValueStackImpl<32>{ struct LuaStack: public ValueStackImpl<32>{
PyObject*& at(int i) { PyObject*& at(int i) {
if(i < 0 || i >= size()){ if(i < 0 || i >= size()){
@ -177,12 +179,7 @@ void pkpy_vm_destroy(pkpy_vm* vm_handle) {
} }
PyObject* c_function_wrapper(VM* vm, ArgsView args) { PyObject* c_function_wrapper(VM* vm, ArgsView args) {
LuaStyleFuncC f; LuaStyleFuncC f = lambda_get_userdata<LuaStyleFuncC>(args.begin());
if(args[-1] != PY_NULL){
f = _py_cast<NativeFunc&>(vm, args[-1])._lua_f;
} else {
f = _py_cast<NativeFunc&>(vm, args[-2])._lua_f;
}
CVM* cvm = (CVM*) vm; CVM* cvm = (CVM*) vm;
//setup c stack //setup c stack
@ -225,7 +222,7 @@ PyObject* c_function_wrapper(VM* vm, ArgsView args) {
bool pkpy_push_function(pkpy_vm* vm_handle, pkpy_function f, int argc) { bool pkpy_push_function(pkpy_vm* vm_handle, pkpy_function f, int argc) {
CVM* vm = (CVM*) vm_handle; CVM* vm = (CVM*) vm_handle;
NativeFunc nf = NativeFunc(c_function_wrapper, argc, false); NativeFunc nf = NativeFunc(c_function_wrapper, argc, false);
nf._lua_f = (LuaStyleFuncC) f; nf.set_userdata(f);
ERRHANDLER_OPEN ERRHANDLER_OPEN
vm->c_data->safe_push(py_var(vm, nf)); vm->c_data->safe_push(py_var(vm, nf));
ERRHANDLER_CLOSE ERRHANDLER_CLOSE

View File

@ -98,14 +98,13 @@ __NEXT_STEP:;
TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH(); TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH();
TARGET(LOAD_FUNCTION) { TARGET(LOAD_FUNCTION) {
FuncDecl_ decl = co->func_decls[byte.arg]; FuncDecl_ decl = co->func_decls[byte.arg];
bool is_simple = decl->starred_kwarg==-1 && decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator;
PyObject* obj; PyObject* obj;
if(decl->nested){ if(decl->nested){
NameDict_ captured = frame->_locals.to_namedict(); NameDict_ captured = frame->_locals.to_namedict();
obj = VAR(Function({decl, is_simple, frame->_module, captured})); obj = VAR(Function({decl, frame->_module, captured}));
captured->set(decl->code->name, obj); captured->set(decl->code->name, obj);
}else{ }else{
obj = VAR(Function({decl, is_simple, frame->_module})); obj = VAR(Function({decl, frame->_module}));
} }
PUSH(obj); PUSH(obj);
} DISPATCH(); } DISPATCH();

View File

@ -20,7 +20,7 @@
#include <variant> #include <variant>
#include <type_traits> #include <type_traits>
#define PK_VERSION "1.0.5" #define PK_VERSION "1.0.7"
#include "config.h" #include "config.h"

View File

@ -17,14 +17,29 @@ using NativeFuncC = std::function<PyObject*(VM*, ArgsView)>;
typedef PyObject* (*NativeFuncC)(VM*, ArgsView); typedef PyObject* (*NativeFuncC)(VM*, ArgsView);
#endif #endif
typedef int (*LuaStyleFuncC)(VM*); typedef shared_ptr<CodeObject> CodeObject_;
struct FuncDecl {
struct KwArg {
int key; // index in co->varnames
PyObject* value; // default value
};
CodeObject_ code; // code object of this function
pod_vector<int> args; // indices in co->varnames
pod_vector<KwArg> kwargs; // indices in co->varnames
int starred_arg = -1; // index in co->varnames, -1 if no *arg
int starred_kwarg = -1; // index in co->varnames, -1 if no **kwarg
bool nested = false; // whether this function is nested
void _gc_mark() const;
};
using FuncDecl_ = shared_ptr<FuncDecl>;
struct NativeFunc { struct NativeFunc {
NativeFuncC f; NativeFuncC f;
int argc; int argc;
// this is designed for lua style C bindings FuncDecl_ decl; // if this is not null, use ex call
LuaStyleFuncC _lua_f;
using UserData = char[32]; using UserData = char[32];
UserData _userdata; UserData _userdata;
@ -53,39 +68,22 @@ struct NativeFunc {
this->f = f; this->f = f;
this->argc = argc; this->argc = argc;
if(argc != -1) this->argc += (int)method; if(argc != -1) this->argc += (int)method;
_lua_f = nullptr;
_has_userdata = false; _has_userdata = false;
} }
PyObject* operator()(VM* vm, ArgsView args) const; NativeFunc(NativeFuncC f, FuncDecl_ decl){
this->f = f;
this->argc = -1;
this->decl = decl;
_has_userdata = false;
}
void check_size(VM* vm, ArgsView args) const;
PyObject* call(VM* vm, ArgsView args) const;
}; };
struct NativeFuncEx{
NativeFuncC f;
};
typedef shared_ptr<CodeObject> CodeObject_;
struct FuncDecl {
struct KwArg {
int key; // index in co->varnames
PyObject* value; // default value
};
CodeObject_ code; // code object of this function
pod_vector<int> args; // indices in co->varnames
pod_vector<KwArg> kwargs; // indices in co->varnames
int starred_arg = -1; // index in co->varnames, -1 if no *arg
int starred_kwarg = -1; // index in co->varnames, -1 if no **kwarg
bool nested = false; // whether this function is nested
void _gc_mark() const;
};
using FuncDecl_ = shared_ptr<FuncDecl>;
struct Function{ struct Function{
FuncDecl_ decl; FuncDecl_ decl;
bool is_simple;
PyObject* _module; PyObject* _module;
NameDict_ _closure; NameDict_ _closure;
}; };

126
src/vm.h
View File

@ -690,15 +690,16 @@ public:
PyObject* _py_generator(Frame&& frame, ArgsView buffer); PyObject* _py_generator(Frame&& frame, ArgsView buffer);
// new style binding api // new style binding api
PyObject* bind(PyObject*, const Str&, NativeFuncC); PyObject* bind(PyObject*, const Str&, NativeFuncC);
void _prepare_py_call(PyObject**, ArgsView, ArgsView, const FuncDecl_&);
}; };
inline PyObject* NativeFunc::operator()(VM* vm, ArgsView args) const{ inline void NativeFunc::check_size(VM* vm, ArgsView args) const{
if(args.size() != argc && argc != -1) { if(args.size() != argc && argc != -1) {
vm->TypeError(fmt("expected ", argc, " arguments, got ", args.size())); vm->TypeError(fmt("expected ", argc, " arguments, got ", args.size()));
} }
#if PK_DEBUG_EXTRA_CHECK }
if(f == nullptr) FATAL_ERROR();
#endif inline PyObject* NativeFunc::call(VM *vm, ArgsView args) const {
return f(vm, args); return f(vm, args);
} }
@ -1238,52 +1239,10 @@ inline void VM::_unpack_as_dict(ArgsView args, Dict& dict){
} }
} }
inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ inline void VM::_prepare_py_call(PyObject** buffer, ArgsView args, ArgsView kwargs, const FuncDecl_& decl){
PyObject** p1 = s_data._sp - KWARGC*2; const CodeObject* co = decl->code.get();
PyObject** p0 = p1 - ARGC - 2;
// [callable, <self>, args..., kwargs...]
// ^p0 ^p1 ^_sp
PyObject* callable = p1[-(ARGC + 2)];
bool method_call = p1[-(ARGC + 1)] != PY_NULL;
// handle boundmethod, do a patch
if(is_non_tagged_type(callable, tp_bound_method)){
if(method_call) FATAL_ERROR();
auto& bm = CAST(BoundMethod&, callable);
callable = bm.func; // get unbound method
p1[-(ARGC + 2)] = bm.func;
p1[-(ARGC + 1)] = bm.self;
method_call = true;
// [unbound, self, args..., kwargs...]
}
ArgsView args(p1 - ARGC - int(method_call), p1);
if(is_non_tagged_type(callable, tp_native_func)){
const auto& f = PK_OBJ_GET(NativeFunc, callable);
if(KWARGC != 0) TypeError("native_func does not accept keyword arguments");
PyObject* ret = f(this, args);
s_data.reset(p0);
return ret;
}
ArgsView kwargs(p1, s_data._sp);
if(false){ // native_func_ex
}
if(is_non_tagged_type(callable, tp_function)){
/*****************_py_call*****************/
// callable must be a `function` object
if(s_data.is_overflow()) StackOverflowError();
const Function& fn = CAST(Function&, callable);
const FuncDecl_& decl = fn.decl;
int decl_argc = decl->args.size();
const CodeObject* co = fn.decl->code.get();
int co_nlocals = co->varnames.size(); int co_nlocals = co->varnames.size();
int decl_argc = decl->args.size();
if(args.size() < decl_argc){ if(args.size() < decl_argc){
vm->TypeError(fmt( vm->TypeError(fmt(
@ -1292,20 +1251,7 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
)); ));
} }
// if this function is simple, a.k.a, no kwargs and no *args and not a generator
// we can use a fast path to avoid using buffer copy
if(fn.is_simple){
if(args.size() > decl_argc) TypeError("too many positional arguments");
int spaces = co_nlocals - decl_argc;
for(int j=0; j<spaces; j++) PUSH(PY_NULL);
callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin()));
if(op_call) return PY_OP_CALL;
return _run_top_frame();
}
int i = 0; int i = 0;
static THREAD_LOCAL PyObject* buffer[PK_MAX_CO_VARNAMES];
// prepare args // prepare args
for(int index: decl->args) buffer[index] = args[i++]; for(int index: decl->args) buffer[index] = args[i++];
// set extra varnames to nullptr // set extra varnames to nullptr
@ -1349,6 +1295,62 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
buffer[index] = kwargs[j+1]; buffer[index] = kwargs[j+1];
} }
} }
}
inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
PyObject** p1 = s_data._sp - KWARGC*2;
PyObject** p0 = p1 - ARGC - 2;
// [callable, <self>, args..., kwargs...]
// ^p0 ^p1 ^_sp
PyObject* callable = p1[-(ARGC + 2)];
bool method_call = p1[-(ARGC + 1)] != PY_NULL;
// handle boundmethod, do a patch
if(is_non_tagged_type(callable, tp_bound_method)){
if(method_call) FATAL_ERROR();
auto& bm = CAST(BoundMethod&, callable);
callable = bm.func; // get unbound method
p1[-(ARGC + 2)] = bm.func;
p1[-(ARGC + 1)] = bm.self;
method_call = true;
// [unbound, self, args..., kwargs...]
}
ArgsView args(p1 - ARGC - int(method_call), p1);
ArgsView kwargs(p1, s_data._sp);
static THREAD_LOCAL PyObject* buffer[PK_MAX_CO_VARNAMES];
if(is_non_tagged_type(callable, tp_native_func)){
const auto& f = PK_OBJ_GET(NativeFunc, callable);
PyObject* ret;
if(f.decl != nullptr){
int co_nlocals = f.decl->code->varnames.size();
_prepare_py_call(buffer, args, kwargs, f.decl);
// copy buffer back to stack
s_data.reset(args.begin());
for(int j=0; j<co_nlocals; j++) PUSH(buffer[j]);
ret = f.call(vm, ArgsView(s_data._sp - co_nlocals, s_data._sp));
}else{
if(KWARGC != 0) TypeError("old-style native_func does not accept keyword arguments");
f.check_size(this, args);
ret = f.call(this, args);
}
s_data.reset(p0);
return ret;
}
if(is_non_tagged_type(callable, tp_function)){
/*****************_py_call*****************/
// callable must be a `function` object
if(s_data.is_overflow()) StackOverflowError();
const Function& fn = PK_OBJ_GET(Function, callable);
const FuncDecl_& decl = fn.decl;
const CodeObject* co = decl->code.get();
int co_nlocals = co->varnames.size();
_prepare_py_call(buffer, args, kwargs, decl);
if(co->is_generator){ if(co->is_generator){
s_data.reset(p0); s_data.reset(p0);