From 3de3e625eeb74c60a78e85936a99812f5611c3a5 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Thu, 29 Jun 2023 19:12:30 +0800 Subject: [PATCH] ... --- c_bindings/pocketpy_c.cpp | 11 +-- src/ceval.h | 5 +- src/common.h | 2 +- src/obj.h | 56 +++++++------ src/vm.h | 162 +++++++++++++++++++------------------- 5 files changed, 116 insertions(+), 120 deletions(-) diff --git a/c_bindings/pocketpy_c.cpp b/c_bindings/pocketpy_c.cpp index 6abb7ef9..c0f821e0 100644 --- a/c_bindings/pocketpy_c.cpp +++ b/c_bindings/pocketpy_c.cpp @@ -3,6 +3,8 @@ using namespace pkpy; +typedef int (*LuaStyleFuncC)(VM*); + struct LuaStack: public ValueStackImpl<32>{ PyObject*& at(int i) { 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) { - LuaStyleFuncC f; - if(args[-1] != PY_NULL){ - f = _py_cast(vm, args[-1])._lua_f; - } else { - f = _py_cast(vm, args[-2])._lua_f; - } + LuaStyleFuncC f = lambda_get_userdata(args.begin()); CVM* cvm = (CVM*) vm; //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) { CVM* vm = (CVM*) vm_handle; NativeFunc nf = NativeFunc(c_function_wrapper, argc, false); - nf._lua_f = (LuaStyleFuncC) f; + nf.set_userdata(f); ERRHANDLER_OPEN vm->c_data->safe_push(py_var(vm, nf)); ERRHANDLER_CLOSE diff --git a/src/ceval.h b/src/ceval.h index 016839e8..4285235d 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -98,14 +98,13 @@ __NEXT_STEP:; TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH(); TARGET(LOAD_FUNCTION) { 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; if(decl->nested){ 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); }else{ - obj = VAR(Function({decl, is_simple, frame->_module})); + obj = VAR(Function({decl, frame->_module})); } PUSH(obj); } DISPATCH(); diff --git a/src/common.h b/src/common.h index bf1be086..4933205b 100644 --- a/src/common.h +++ b/src/common.h @@ -20,7 +20,7 @@ #include #include -#define PK_VERSION "1.0.5" +#define PK_VERSION "1.0.7" #include "config.h" diff --git a/src/obj.h b/src/obj.h index 549a2b5a..4a4d2356 100644 --- a/src/obj.h +++ b/src/obj.h @@ -17,14 +17,29 @@ using NativeFuncC = std::function; typedef PyObject* (*NativeFuncC)(VM*, ArgsView); #endif -typedef int (*LuaStyleFuncC)(VM*); +typedef shared_ptr CodeObject_; + +struct FuncDecl { + struct KwArg { + int key; // index in co->varnames + PyObject* value; // default value + }; + CodeObject_ code; // code object of this function + pod_vector args; // indices in co->varnames + pod_vector 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; struct NativeFunc { NativeFuncC f; int argc; - // this is designed for lua style C bindings - LuaStyleFuncC _lua_f; + FuncDecl_ decl; // if this is not null, use ex call using UserData = char[32]; UserData _userdata; @@ -53,39 +68,22 @@ struct NativeFunc { this->f = f; this->argc = argc; if(argc != -1) this->argc += (int)method; - _lua_f = nullptr; _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_; - -struct FuncDecl { - struct KwArg { - int key; // index in co->varnames - PyObject* value; // default value - }; - CodeObject_ code; // code object of this function - pod_vector args; // indices in co->varnames - pod_vector 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; - struct Function{ FuncDecl_ decl; - bool is_simple; PyObject* _module; NameDict_ _closure; }; diff --git a/src/vm.h b/src/vm.h index 0181cdf5..ddb478af 100644 --- a/src/vm.h +++ b/src/vm.h @@ -690,15 +690,16 @@ public: PyObject* _py_generator(Frame&& frame, ArgsView buffer); // new style binding api 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) { 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); } @@ -1238,6 +1239,64 @@ inline void VM::_unpack_as_dict(ArgsView args, Dict& dict){ } } +inline void VM::_prepare_py_call(PyObject** buffer, ArgsView args, ArgsView kwargs, const FuncDecl_& decl){ + const CodeObject* co = decl->code.get(); + int co_nlocals = co->varnames.size(); + int decl_argc = decl->args.size(); + + if(args.size() < decl_argc){ + vm->TypeError(fmt( + "expected ", decl_argc, " positional arguments, got ", args.size(), + " (", co->name, ')' + )); + } + + int i = 0; + // prepare args + for(int index: decl->args) buffer[index] = args[i++]; + // set extra varnames to nullptr + for(int j=i; jkwargs) buffer[kv.key] = kv.value; + + // handle *args + if(decl->starred_arg != -1){ + ArgsView vargs(args.begin() + i, args.end()); + buffer[decl->starred_arg] = VAR(vargs.to_tuple()); + i += vargs.size(); + }else{ + // kwdefaults override + for(auto& kv: decl->kwargs){ + if(i >= args.size()) break; + buffer[kv.key] = args[i++]; + } + if(i < args.size()) TypeError(fmt("too many arguments", " (", decl->code->name, ')')); + } + + PyObject* vkwargs; + if(decl->starred_kwarg != -1){ + vkwargs = VAR(Dict(this)); + buffer[decl->starred_kwarg] = vkwargs; + }else{ + vkwargs = nullptr; + } + + for(int j=0; jvarnames_inv.try_get(key); + if(index < 0){ + if(vkwargs == nullptr){ + TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()")); + }else{ + Dict& dict = _CAST(Dict&, vkwargs); + dict.set(VAR(key.sv()), kwargs[j+1]); + } + }else{ + 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; @@ -1258,97 +1317,40 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ } 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); - if(KWARGC != 0) TypeError("native_func does not accept keyword arguments"); - PyObject* ret = f(this, args); + 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; jargs.size(); - const CodeObject* co = fn.decl->code.get(); + const CodeObject* co = decl->code.get(); int co_nlocals = co->varnames.size(); - if(args.size() < decl_argc){ - vm->TypeError(fmt( - "expected ", decl_argc, " positional arguments, got ", args.size(), - " (", co->name, ')' - )); - } - - // 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; jargs) buffer[index] = args[i++]; - // set extra varnames to nullptr - for(int j=i; jkwargs) buffer[kv.key] = kv.value; - - // handle *args - if(decl->starred_arg != -1){ - ArgsView vargs(args.begin() + i, args.end()); - buffer[decl->starred_arg] = VAR(vargs.to_tuple()); - i += vargs.size(); - }else{ - // kwdefaults override - for(auto& kv: decl->kwargs){ - if(i >= args.size()) break; - buffer[kv.key] = args[i++]; - } - if(i < args.size()) TypeError(fmt("too many arguments", " (", decl->code->name, ')')); - } - - PyObject* vkwargs; - if(decl->starred_kwarg != -1){ - vkwargs = VAR(Dict(this)); - buffer[decl->starred_kwarg] = vkwargs; - }else{ - vkwargs = nullptr; - } - - for(int j=0; jvarnames_inv.try_get(key); - if(index < 0){ - if(vkwargs == nullptr){ - TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()")); - }else{ - Dict& dict = _CAST(Dict&, vkwargs); - dict.set(VAR(key.sv()), kwargs[j+1]); - } - }else{ - buffer[index] = kwargs[j+1]; - } - } + _prepare_py_call(buffer, args, kwargs, decl); if(co->is_generator){ s_data.reset(p0);