blueloveTH 2 years ago
parent
commit
3de3e625ee
5 changed files with 115 additions and 119 deletions
  1. 4 7
      c_bindings/pocketpy_c.cpp
  2. 2 3
      src/ceval.h
  3. 1 1
      src/common.h
  4. 26 28
      src/obj.h
  5. 82 80
      src/vm.h

+ 4 - 7
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<NativeFunc&>(vm, args[-1])._lua_f;
-    } else {
-        f = _py_cast<NativeFunc&>(vm, args[-2])._lua_f;
-    }
+    LuaStyleFuncC f = lambda_get_userdata<LuaStyleFuncC>(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

+ 2 - 3
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();

+ 1 - 1
src/common.h

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

+ 26 - 28
src/obj.h

@@ -17,14 +17,29 @@ using NativeFuncC = std::function<PyObject*(VM*, ArgsView)>;
 typedef PyObject* (*NativeFuncC)(VM*, ArgsView);
 #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 {
     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;
-};
-
-
-struct NativeFuncEx{
-    NativeFuncC f;
-};
-
-typedef shared_ptr<CodeObject> CodeObject_;
+    NativeFunc(NativeFuncC f, FuncDecl_ decl){
+        this->f = f;
+        this->argc = -1;
+        this->decl = decl;
+        _has_userdata = false;
+    }
 
-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;
+    void check_size(VM* vm, ArgsView args) const;
+    PyObject* call(VM* vm, ArgsView args) const;
 };
 
-using FuncDecl_ = shared_ptr<FuncDecl>;
-
 struct Function{
     FuncDecl_ decl;
-    bool is_simple;
     PyObject* _module;
     NameDict_ _closure;
 };

+ 82 - 80
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; j<co_nlocals; j++) buffer[j] = PY_NULL;
+    // prepare kwdefaults
+    for(auto& kv: decl->kwargs) 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; j<kwargs.size(); j+=2){
+        StrName key(CAST(int, kwargs[j]));
+        int index = co->varnames_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; 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;
     }
 
-    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 Function& fn = PK_OBJ_GET(Function, callable);
         const FuncDecl_& decl = fn.decl;
-        int decl_argc = decl->args.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; 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;
-        static THREAD_LOCAL PyObject* buffer[PK_MAX_CO_VARNAMES];
-
-        // prepare args
-        for(int index: decl->args) buffer[index] = args[i++];
-        // set extra varnames to nullptr
-        for(int j=i; j<co_nlocals; j++) buffer[j] = PY_NULL;
-        // prepare kwdefaults
-        for(auto& kv: decl->kwargs) 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; j<kwargs.size(); j+=2){
-            StrName key(CAST(int, kwargs[j]));
-            int index = co->varnames_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);