blueloveTH 1 tahun lalu
induk
melakukan
8ae999ecdf
5 mengubah file dengan 236 tambahan dan 38 penghapusan
  1. 2 0
      include/pocketpy/interpreter/vm.h
  2. 39 22
      include/pocketpy/pocketpy.h
  3. 13 12
      src/interpreter/ceval.c
  4. 175 2
      src/interpreter/vm.c
  5. 7 2
      src/public/vm.c

+ 2 - 0
include/pocketpy/interpreter/vm.h

@@ -87,6 +87,8 @@ py_Type pk_VM__new_type(pk_VM* self,
                         const py_TValue* module,
                         bool subclass_enabled);
 
+pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t argc, uint16_t kwargc, bool opcall);
+
 // type registration
 py_Type pk_str__register();
 py_Type pk_bytes__register();

+ 39 - 22
include/pocketpy/pocketpy.h

@@ -128,11 +128,11 @@ py_GlobalRef py_tpmagic(py_Type type, py_Name name);
 // new style decl-based bindings
 py_TmpRef py_bind(py_Ref obj, const char* sig, py_CFunction f);
 py_TmpRef py_bind2(py_Ref obj,
-                const char* sig,
-                py_CFunction f,
-                BindType bt,
-                const char* docstring,
-                const py_Ref upvalue);
+                   const char* sig,
+                   py_CFunction f,
+                   BindType bt,
+                   const char* docstring,
+                   const py_Ref upvalue);
 // old style argc-based bindings
 void py_bindmethod(py_Type type, const char* name, py_CFunction f);
 void py_bindmethod2(py_Type type, const char* name, py_CFunction f, BindType bt);
@@ -253,7 +253,7 @@ bool py_isidentical(const py_Ref, const py_Ref);
 /// It assumes `argc + kwargc` arguments are already pushed to the stack.
 /// The result will be set to `py_retval()`.
 /// The stack size will be reduced by `argc + kwargc`.
-bool pk_vectorcall(int argc, int kwargc, bool op_call);
+bool py_vectorcall(uint16_t argc, uint16_t kwargc);
 /// Call a function.
 /// It prepares the stack and then performs a `vectorcall(argc, 0, false)`.
 /// The result will be set to `py_retval()`.
@@ -323,26 +323,43 @@ bool py_checktype(const py_Ref self, py_Type type);
 /// %t: py_Type
 /// %n: py_Name
 
-
-enum py_MagicNames{
+enum py_MagicNames {
     py_MagicNames__NULL,  // 0 is reserved
-    #define MAGIC_METHOD(x) x,
-    #include "pocketpy/xmacros/magics.h"
-    #undef MAGIC_METHOD
+
+#define MAGIC_METHOD(x) x,
+#include "pocketpy/xmacros/magics.h"
+#undef MAGIC_METHOD
 };
 
-enum py_PredefinedTypes{
-    tp_object = 1, tp_type,
-    tp_int, tp_float, tp_bool, tp_str,
-    tp_list, tp_tuple,
-    tp_slice, tp_range, tp_module,
-    tp_function, tp_nativefunc, tp_bound_method,
-    tp_super, tp_exception, tp_bytes, tp_mappingproxy,
-    tp_dict, tp_property, tp_star_wrapper,
-    tp_staticmethod, tp_classmethod,
-    tp_none_type, tp_not_implemented_type,
+enum py_PredefinedTypes {
+    tp_object = 1,
+    tp_type,
+    tp_int,
+    tp_float,
+    tp_bool,
+    tp_str,
+    tp_list,
+    tp_tuple,
+    tp_slice,
+    tp_range,
+    tp_module,
+    tp_function,
+    tp_nativefunc,
+    tp_bound_method,
+    tp_super,
+    tp_exception,
+    tp_bytes,
+    tp_mappingproxy,
+    tp_dict,
+    tp_property,
+    tp_star_wrapper,
+    tp_staticmethod,
+    tp_classmethod,
+    tp_none_type,
+    tp_not_implemented_type,
     tp_ellipsis,
-    tp_syntax_error, tp_stop_iteration
+    tp_syntax_error,
+    tp_stop_iteration
 };
 
 #ifdef __cplusplus

+ 13 - 12
src/interpreter/ceval.c

@@ -49,9 +49,9 @@ static bool stack_binaryop(pk_VM* self, py_Name op, py_Name rop);
         *SECOND() = *THIRD();                                                                      \
     } while(0)
 
-#define vectorcall_opcall(n)                                                                       \
+#define vectorcall_opcall(argc, kwargc)                                                            \
     do {                                                                                           \
-        pk_FrameResult res = pk_vectorcall(n, 0, true);                                            \
+        pk_FrameResult res = pk_VM__vectorcall(self, (argc), (kwargc), true);                                \
         switch(res) {                                                                              \
             case RES_RETURN: PUSH(&self->last_retval); break;                                      \
             case RES_CALL:                                                                         \
@@ -269,7 +269,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                     } else {
                         INSERT_THIRD();     // [?, a, b]
                         *THIRD() = *magic;  // [__getitem__, a, b]
-                        vectorcall_opcall(2);
+                        vectorcall_opcall(2, 0);
                     }
                     DISPATCH();
                 }
@@ -322,7 +322,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                     } else {
                         INSERT_THIRD();      // [?, a, b]
                         *FOURTH() = *magic;  // [__selitem__, a, b, val]
-                        vectorcall_opcall(3);
+                        vectorcall_opcall(3, 0);
                         POP();  // discard retval
                     }
                     DISPATCH();
@@ -392,7 +392,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                     } else {
                         INSERT_THIRD();     // [?, a, b]
                         *THIRD() = *magic;  // [__delitem__, a, b]
-                        vectorcall_opcall(2);
+                        vectorcall_opcall(2, 0);
                         POP();  // discard retval
                     }
                     DISPATCH();
@@ -416,11 +416,11 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 py_Ref f = py_getdict(&self->builtins, py_name("complex"));
                 assert(f != NULL);
                 py_TValue tmp = *TOP();
-                *TOP() = *f;           // [complex]
-                py_newnull(SP()++);    // [complex, NULL]
-                py_newint(SP()++, 0);  // [complex, NULL, 0]
-                *SP()++ = tmp;         // [complex, NULL, 0, x]
-                vectorcall_opcall(2);  // [complex(x)]
+                *TOP() = *f;              // [complex]
+                py_newnull(SP()++);       // [complex, NULL]
+                py_newint(SP()++, 0);     // [complex, NULL, 0]
+                *SP()++ = tmp;            // [complex, NULL, 0, x]
+                vectorcall_opcall(2, 0);  // [complex(x)]
                 DISPATCH();
             }
             case OP_BUILD_BYTES: {
@@ -527,7 +527,7 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                     } else {
                         INSERT_THIRD();     // [?, b, a]
                         *THIRD() = *magic;  // [__contains__, a, b]
-                        vectorcall_opcall(2);
+                        vectorcall_opcall(2, 0);
                     }
                     bool res = py_tobool(TOP());
                     if(byte.arg) py_newbool(TOP(), !res);
@@ -612,7 +612,8 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 assert(false);
             }
             case OP_CALL: {
-                assert(false);
+                pk_ManagedHeap__collect_if_needed(&self->heap);
+                vectorcall_opcall(byte.arg & 0xFF, byte.arg >> 8);
             }
             case OP_CALL_VARGS: {
                 assert(false);

+ 175 - 2
src/interpreter/vm.c

@@ -1,10 +1,13 @@
 #include "pocketpy/interpreter/vm.h"
 #include "pocketpy/common/memorypool.h"
 #include "pocketpy/common/sstream.h"
+#include "pocketpy/common/utils.h"
 #include "pocketpy/objects/base.h"
 #include "pocketpy/pocketpy.h"
 
+#include <assert.h>
 #include <stdarg.h>
+#include <stdbool.h>
 
 static unsigned char* pk_default_import_file(const char* path) { return NULL; }
 
@@ -69,7 +72,7 @@ void pk_VM__ctor(pk_VM* self) {
 
     self->last_retval = PY_NULL;
     self->has_error = false;
-    
+
     self->__curr_class = PY_NULL;
     self->__cached_object_new = PY_NULL;
     self->__dynamic_func_decl = NULL;
@@ -196,6 +199,176 @@ py_Type pk_VM__new_type(pk_VM* self,
     return index;
 }
 
+pk_FrameResult pk_VM__vectorcall(pk_VM* self, uint16_t ARGC, uint16_t KWARGC, bool opcall) {
+    py_Ref p1 = self->stack.sp - KWARGC * 2;
+    py_Ref p0 = p1 - ARGC - 2;
+    // [callable, <self>, args..., kwargs...]
+    //      ^p0                    ^p1      ^_sp
+
+    // handle boundmethod, do a patch
+    if(p0->type == tp_bound_method) {
+        assert(false);
+        assert(py_isnull(p0+1));   // self must be NULL
+        // BoundMethod& bm = PK_OBJ_GET(BoundMethod, callable);
+        // callable = bm.func;  // get unbound method
+        // callable_t = _tp(callable);
+        // p1[-(ARGC + 2)] = bm.func;
+        // p1[-(ARGC + 1)] = bm.self;
+        // [unbound, self, args..., kwargs...]
+    }
+
+    // PyVar* _base = args.begin();
+    py_Ref argv = py_isnull(p0+1) ? p0+2 : p0+1;
+
+#if 0
+    if(callable_t == tp_function) {
+        /*****************_py_call*****************/
+        // check stack overflow
+        if(self->stack.sp > self->stack.end){
+            StackOverflowError();
+            return RES_ERROR;
+        }
+
+        const Function& fn = PK_OBJ_GET(Function, callable);
+        const CodeObject* co = fn.decl->code;
+
+        switch(fn.decl->type) {
+            case FuncType_NORMAL:
+                __prepare_py_call(__vectorcall_buffer, args, kwargs, fn.decl);
+                // copy buffer back to stack
+                s_data.reset(_base + co->nlocals);
+                for(int j = 0; j < co->nlocals; j++)
+                    _base[j] = __vectorcall_buffer[j];
+                break;
+            case FuncType_SIMPLE:
+                if(args.size() != fn.decl->args.count) {
+                    TypeError(pk_format("{} takes {} positional arguments but {} were given",
+                                        &co->name,
+                                        fn.decl->args.count,
+                                        args.size()));
+                }
+                if(!kwargs.empty()) {
+                    TypeError(pk_format("{} takes no keyword arguments", &co->name));
+                }
+                // [callable, <self>, args..., local_vars...]
+                //      ^p0                    ^p1      ^_sp
+                s_data.reset(_base + co->nlocals);
+                // initialize local variables to PY_NULL
+                std::memset(p1, 0, (char*)s_data._sp - (char*)p1);
+                break;
+            case FuncType_EMPTY:
+                if(args.size() != fn.decl->args.count) {
+                    TypeError(pk_format("{} takes {} positional arguments but {} were given",
+                                        &co->name,
+                                        fn.decl->args.count,
+                                        args.size()));
+                }
+                if(!kwargs.empty()) {
+                    TypeError(pk_format("{} takes no keyword arguments", &co->name));
+                }
+                s_data.reset(p0);
+                return None;
+            case FuncType_GENERATOR:
+                __prepare_py_call(__vectorcall_buffer, args, kwargs, fn.decl);
+                s_data.reset(p0);
+                callstack.emplace(nullptr, co, fn._module, callable.get(), nullptr);
+                return __py_generator(
+                    callstack.popx(),
+                    ArgsView(__vectorcall_buffer, __vectorcall_buffer + co->nlocals));
+            default: PK_UNREACHABLE()
+        };
+
+        // simple or normal
+        callstack.emplace(p0, co, fn._module, callable.get(), args.begin());
+        if(op_call) return pkpy_OP_CALL;
+        return __run_top_frame();
+        /*****************_py_call*****************/
+    }
+#endif
+
+    if(p0->type == tp_nativefunc) {
+        // const auto& f = PK_OBJ_GET(NativeFunc, callable);
+        // PyVar ret;
+        // if(f.decl != nullptr) {
+        //     int co_nlocals = f.decl->code->nlocals;
+        //     __prepare_py_call(__vectorcall_buffer, args, kwargs, f.decl);
+        //     // copy buffer back to stack
+        //     s_data.reset(_base + co_nlocals);
+        //     for(int j = 0; j < co_nlocals; j++)
+        //         _base[j] = __vectorcall_buffer[j];
+        //     ret = f.call(vm, ArgsView(s_data._sp - co_nlocals, s_data._sp));
+        // } else {
+        //     if(f.argc != -1) {
+        //         if(KWARGC != 0)
+        //             TypeError(
+        //                 "old-style native_func does not accept keyword arguments. If you want to skip this check, specify `argc` to -1");
+        //         if(args.size() != f.argc) {
+        //             vm->TypeError(_S("expected ", f.argc, " arguments, got ", args.size()));
+        //         }
+        //     }
+        //     ret = f.call(this, args);
+        // }
+
+        if(!p0->_cfunc(ARGC, argv)) return RES_ERROR;
+        self->stack.sp = p0;
+        return RES_RETURN;
+    }
+
+#if 0
+    if(p0->type == tp_type) {
+        // [type, NULL, args..., kwargs...]
+        PyVar new_f = *find_name_in_mro(PK_OBJ_GET(Type, callable), __new__);
+        PyVar obj;
+        assert(new_f && (!p0[1]));
+        if(PyVar__IS_OP(&new_f, &__cached_object_new)) {
+            // fast path for object.__new__
+            obj = vm->new_object<DummyInstance>(PK_OBJ_GET(Type, callable));
+        } else {
+            PUSH(new_f);
+            PUSH(PY_NULL);
+            PUSH(callable);  // cls
+            for(PyVar o: args)
+                PUSH(o);
+            for(PyVar o: kwargs)
+                PUSH(o);
+            // if obj is not an instance of `cls`, the behavior is undefined
+            obj = vectorcall(ARGC + 1, KWARGC);
+        }
+
+        // __init__
+        PyVar self;
+        callable = get_unbound_method(obj, __init__, &self, false);
+        if(callable) {
+            callable_t = _tp(callable);
+            // replace `NULL` with `self`
+            p1[-(ARGC + 2)] = callable;
+            p1[-(ARGC + 1)] = self;
+            // [init_f, self, args..., kwargs...]
+            vectorcall(ARGC, KWARGC);
+            // We just discard the return value of `__init__`
+            // in cpython it raises a TypeError if the return value is not None
+        } else {
+            // manually reset the stack
+            s_data.reset(p0);
+        }
+        return obj;
+    }
+
+    // handle `__call__` overload
+    PyVar self;
+    PyVar call_f = get_unbound_method(callable, __call__, &self, false);
+    if(self) {
+        p1[-(ARGC + 2)] = call_f;
+        p1[-(ARGC + 1)] = self;
+        // [call_f, self, args..., kwargs...]
+        return vectorcall(ARGC, KWARGC, op_call);
+    }
+    TypeError(_type_name(vm, callable_t).escape() + " object is not callable");
+#endif
+    
+    PK_UNREACHABLE();
+}
+
 /****************************************/
 void PyObject__delete(PyObject* self) {
     pk_TypeInfo* ti = c11__at(pk_TypeInfo, &pk_current_vm->types, self->type);
@@ -221,4 +394,4 @@ void pk_ManagedHeap__mark(pk_ManagedHeap* self) {
     // vm->obj_gc_mark(vm->__c.error);
     // vm->__stack_gc_mark(vm->s_data.begin(), vm->s_data.end());
     // if(self->_gc_marker_ex) self->_gc_marker_ex((pkpy_VM*)vm);
-}
+}

+ 7 - 2
src/public/vm.c

@@ -6,6 +6,7 @@
 #include "pocketpy/objects/object.h"
 #include "pocketpy/interpreter/vm.h"
 #include "pocketpy/compiler/compiler.h"
+#include <stdint.h>
 
 pk_VM* pk_current_vm;
 static pk_VM pk_default_vm;
@@ -73,7 +74,8 @@ static void disassemble(CodeObject* co) {
         c11_sbuf__write_cstr(&ss, OP_NAMES[byte.op]);
         c11_sbuf__write_char(&ss, ex.is_virtual ? '*' : ' ');
         int padding = 24 - strlen(OP_NAMES[byte.op]);
-        for(int j = 0; j < padding; j++) c11_sbuf__write_char(&ss, ' ');
+        for(int j = 0; j < padding; j++)
+            c11_sbuf__write_char(&ss, ' ');
 
         // _opcode_argstr(this, i, byte, co);
         do {
@@ -182,7 +184,10 @@ bool py_call(py_Ref f, int argc, py_Ref argv) { return -1; }
 
 bool py_callmethod(py_Ref self, py_Name name, int argc, py_Ref argv) { return -1; }
 
-bool pk_vectorcall(int argc, int kwargc, bool op_call) { return -1; }
+bool py_vectorcall(uint16_t argc, uint16_t kwargc) {
+    pk_VM* vm = pk_current_vm;
+    return pk_VM__vectorcall(vm, argc, kwargc, false) == RES_ERROR;
+}
 
 py_Ref py_retval() { return &pk_current_vm->last_retval; }