diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 8ebffa47..99d03a7c 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/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(); diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 502c1115..ec6f1444 100644 --- a/include/pocketpy/pocketpy.h +++ b/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 diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 02bdf4c8..65a047cd 100644 --- a/src/interpreter/ceval.c +++ b/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); diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 214e99e5..e8469f52 100644 --- a/src/interpreter/vm.c +++ b/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 #include +#include 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, , 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, , 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(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); -} +} \ No newline at end of file diff --git a/src/public/vm.c b/src/public/vm.c index 059b9856..8e5fcc80 100644 --- a/src/public/vm.c +++ b/src/public/vm.c @@ -6,6 +6,7 @@ #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" #include "pocketpy/compiler/compiler.h" +#include 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; }