diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index c553713a..e6bc6aa5 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -63,8 +63,8 @@ typedef struct pk_VM { py_TValue last_retval; py_TValue reg[8]; // users' registers - PyObject* __curr_class; - PyObject* __cached_object_new; + py_TValue __curr_class; + py_TValue __cached_object_new; FuncDecl_ __dynamic_func_decl; py_TValue __vectorcall_buffer[PK_MAX_CO_VARNAMES]; diff --git a/include/pocketpy/objects/base.h b/include/pocketpy/objects/base.h index 1cd86034..dc859640 100644 --- a/include/pocketpy/objects/base.h +++ b/include/pocketpy/objects/base.h @@ -29,7 +29,8 @@ typedef struct py_TValue{ }; } py_TValue; -static_assert(sizeof(py_TValue) <= 16, "!sizeof(py_TValue) <= 16"); +// 16 bytes to make py_arg() macro work +static_assert(sizeof(py_TValue) == 16, "sizeof(py_TValue) != 16"); /* predefined vars */ static const py_Type tp_object = {1}, tp_type = {2}; diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index b7e2f739..e34027dd 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -40,12 +40,12 @@ void py_newbool(py_Ref, bool); void py_newstr(py_Ref, const char*); void py_newstrn(py_Ref, const char*, int); // void py_newfstr(py_Ref, const char*, ...); -// void py_newbytes(py_Ref, const uint8_t*, int); +void py_newbytes(py_Ref, const unsigned char*, int); void py_newnone(py_Ref); void py_newnull(py_Ref); -void py_newtuple(py_Ref, int); -// void py_newlist(py_Ref); +void py_newtuple(py_Ref, int count); +void py_newlist(py_Ref); // new style decl-based function void py_newfunction(py_Ref out, py_CFunction, const char* sig); @@ -101,6 +101,8 @@ bool py_istype(const py_Ref, py_Type); // bool py_issubclass(py_Type derived, py_Type base); /************* References *************/ +#define py_arg(i) (py_Ref)((char*)argv+((i)<<4)) + py_Ref py_getreg(int i); void py_setreg(int i, const py_Ref val); @@ -114,9 +116,13 @@ py_Ref py_getupvalue(py_Ref self); void py_setupvalue(py_Ref self, const py_Ref val); /// Gets the attribute of the object. -int py_getattr(const py_Ref self, py_Name name, py_Ref out); +bool py_getattr(const py_Ref self, py_Name name, py_Ref out); +/// Gets the unbound method of the object. +bool py_getunboundmethod(const py_Ref self, py_Name name, bool fallback, py_Ref out, py_Ref out_self); /// Sets the attribute of the object. int py_setattr(py_Ref self, py_Name name, const py_Ref val); +/// Deletes the attribute of the object. +int py_delattr(py_Ref self, py_Name name); /// Equivalent to `*dst = *src`. void py_assign(py_Ref dst, const py_Ref src); @@ -193,6 +199,10 @@ void py_dict__setitem(py_Ref self, const py_Ref key, const py_Ref val); void py_dict__delitem(py_Ref self, const py_Ref key); void py_dict__clear(py_Ref self); +// internal functions +typedef struct pk_TypeInfo pk_TypeInfo; +pk_TypeInfo* pk_tpinfo(const py_Ref self); + #ifdef __cplusplus } #endif diff --git a/include/pocketpy/xmacros/opcodes.h b/include/pocketpy/xmacros/opcodes.h index c6d265dc..18138968 100644 --- a/include/pocketpy/xmacros/opcodes.h +++ b/include/pocketpy/xmacros/opcodes.h @@ -29,14 +29,12 @@ OPCODE(LOAD_ATTR) OPCODE(LOAD_CLASS_GLOBAL) OPCODE(LOAD_METHOD) OPCODE(LOAD_SUBSCR) -OPCODE(LOAD_SUBSCR_FAST) OPCODE(STORE_FAST) OPCODE(STORE_NAME) OPCODE(STORE_GLOBAL) OPCODE(STORE_ATTR) OPCODE(STORE_SUBSCR) -OPCODE(STORE_SUBSCR_FAST) OPCODE(DELETE_FAST) OPCODE(DELETE_NAME) diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 9458850d..b1c2efab 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -1001,26 +1001,14 @@ void SubscrExpr__emit_(Expr* self_, Ctx* ctx) { SubscrExpr* self = (SubscrExpr*)self_; vtemit_(self->lhs, ctx); vtemit_(self->rhs, ctx); - Bytecode last_bc = c11_vector__back(Bytecode, &ctx->co->codes); - if(self->rhs->vt->is_name && last_bc.op == OP_LOAD_FAST) { - Ctx__revert_last_emit_(ctx); - Ctx__emit_(ctx, OP_LOAD_SUBSCR_FAST, last_bc.arg, self->line); - } else { - Ctx__emit_(ctx, OP_LOAD_SUBSCR, BC_NOARG, self->line); - } + Ctx__emit_(ctx, OP_LOAD_SUBSCR, BC_NOARG, self->line); } bool SubscrExpr__emit_store(Expr* self_, Ctx* ctx) { SubscrExpr* self = (SubscrExpr*)self_; vtemit_(self->lhs, ctx); vtemit_(self->rhs, ctx); - Bytecode last_bc = c11_vector__back(Bytecode, &ctx->co->codes); - if(self->rhs->vt->is_name && last_bc.op == OP_LOAD_FAST) { - Ctx__revert_last_emit_(ctx); - Ctx__emit_(ctx, OP_STORE_SUBSCR_FAST, last_bc.arg, self->line); - } else { - Ctx__emit_(ctx, OP_STORE_SUBSCR, BC_NOARG, self->line); - } + Ctx__emit_(ctx, OP_STORE_SUBSCR, BC_NOARG, self->line); return true; } diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 87945b35..e547fac1 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -8,6 +8,8 @@ int UnboundLocalError(py_Name name) { return -1; } int NameError(py_Name name) { return -1; } +#define AttributeError(obj, name) + #define DISPATCH() \ do { \ frame->ip++; \ @@ -26,15 +28,39 @@ int NameError(py_Name name) { return -1; } /* Stack manipulation macros */ // https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123 -#define TOP() self->stack.sp[-1] -#define SECOND() self->stack.sp[-2] -#define THIRD() self->stack.sp[-3] +#define TOP() (self->stack.sp - 1) +#define SECOND() (self->stack.sp - 2) +#define THIRD() (self->stack.sp - 3) +#define FOURTH() (self->stack.sp - 4) #define STACK_SHRINK(n) (self->stack.sp -= n) -#define PUSH(v) (*self->stack.sp++ = v) +#define PUSH(v) (*self->stack.sp++ = *v) #define POP() (--self->stack.sp) #define POPX() (*--self->stack.sp) #define SP() (self->stack.sp) +static void pack_stack_values(int n) { + assert(n > 1); + pk_VM* self = pk_current_vm; + py_TValue tmp; + py_newtuple(&tmp, n); + for(int i = 0; i < n; i++) + py_tuple__setitem(&tmp, i, SP() - n + i); + STACK_SHRINK(n); + PUSH(&tmp); +} + +// n == 1 is the most likely result +#define HANDLE_RETVAL(n) \ + if(n != 1) { \ + if(n == 0) { \ + PUSH(&self->None); \ + } else if(n > 1) { \ + pack_stack_values(n); \ + } else { \ + goto __ERROR; \ + } \ + } + pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { Frame* frame = self->top_frame; const Frame* base_frame = frame; @@ -72,39 +98,37 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { PUSH(SECOND()); // [a, b, a, b] DISPATCH(); case OP_ROT_TWO: { - py_TValue tmp = TOP(); - TOP() = SECOND(); - SECOND() = tmp; + py_TValue tmp = *TOP(); + *TOP() = *SECOND(); + *SECOND() = tmp; DISPATCH(); } case OP_ROT_THREE: { // [a, b, c] -> [c, a, b] - py_TValue _0 = TOP(); - TOP() = SECOND(); - SECOND() = THIRD(); - THIRD() = _0; + py_TValue tmp = *TOP(); + *TOP() = *SECOND(); + *SECOND() = *THIRD(); + *THIRD() = tmp; DISPATCH(); } case OP_PRINT_EXPR: - if(TOP().type != tp_none_type) { - int err = py_repr(&TOP()); + if(TOP()->type != tp_none_type) { + int err = py_repr(TOP()); if(err) goto __ERROR; - self->_stdout("%s\n", py_tostr(&TOP())); + self->_stdout("%s\n", py_tostr(TOP())); POP(); } POP(); DISPATCH(); /*****************************************/ - case OP_LOAD_CONST: - PUSH(c11__getitem(py_TValue, &frame->co->consts, byte.arg)); - DISPATCH(); - case OP_LOAD_NONE: PUSH(self->None); DISPATCH(); - case OP_LOAD_TRUE: PUSH(self->True); DISPATCH(); - case OP_LOAD_FALSE: PUSH(self->False); DISPATCH(); + case OP_LOAD_CONST: PUSH(c11__at(py_TValue, &frame->co->consts, byte.arg)); DISPATCH(); + case OP_LOAD_NONE: PUSH(&self->None); DISPATCH(); + case OP_LOAD_TRUE: PUSH(&self->True); DISPATCH(); + case OP_LOAD_FALSE: PUSH(&self->False); DISPATCH(); /*****************************************/ case OP_LOAD_SMALL_INT: py_newint(SP()++, (int64_t)(int16_t)byte.arg); DISPATCH(); /*****************************************/ - case OP_LOAD_ELLIPSIS: PUSH(self->Ellipsis); DISPATCH(); + case OP_LOAD_ELLIPSIS: PUSH(&self->Ellipsis); DISPATCH(); case OP_LOAD_FUNCTION: { // FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg); // py_TValue obj; @@ -126,8 +150,8 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { DISPATCH(); /*****************************************/ case OP_LOAD_FAST: { - PUSH(frame->locals[byte.arg]); - if(py_isnull(&TOP())) { + PUSH(&frame->locals[byte.arg]); + if(py_isnull(TOP())) { py_Name name = c11__getitem(uint16_t, &frame->co->varnames, byte.arg); UnboundLocalError(name); goto __ERROR; @@ -142,22 +166,22 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { UnboundLocalError(name); goto __ERROR; } - PUSH(*tmp); + PUSH(tmp); DISPATCH(); } tmp = Frame__f_closure_try_get(frame, name); if(tmp != NULL) { - PUSH(*tmp); + PUSH(tmp); DISPATCH(); } tmp = Frame__f_globals_try_get(frame, name); if(tmp != NULL) { - PUSH(*tmp); + PUSH(tmp); DISPATCH(); } tmp = py_getdict(&self->builtins, name); if(tmp != NULL) { - PUSH(*tmp); + PUSH(tmp); DISPATCH(); } NameError(name); @@ -167,17 +191,17 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { py_Name name = byte.arg; py_Ref tmp = Frame__f_closure_try_get(frame, name); if(tmp != NULL) { - PUSH(*tmp); + PUSH(tmp); DISPATCH(); } tmp = Frame__f_globals_try_get(frame, name); if(tmp != NULL) { - PUSH(*tmp); + PUSH(tmp); DISPATCH(); } tmp = py_getdict(&self->builtins, name); if(tmp != NULL) { - PUSH(*tmp); + PUSH(tmp); DISPATCH(); } NameError(name); @@ -187,27 +211,203 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { py_Name name = byte.arg; py_Ref tmp = Frame__f_globals_try_get(frame, name); if(tmp != NULL) { - PUSH(*tmp); + PUSH(tmp); DISPATCH(); } tmp = py_getdict(&self->builtins, name); if(tmp != NULL) { - PUSH(*tmp); + PUSH(tmp); DISPATCH(); } NameError(name); goto __ERROR; } case OP_LOAD_ATTR: { - int err = py_getattr(&TOP(), byte.arg, &TOP()); - if(err) goto __ERROR; + if(!py_getattr(TOP(), byte.arg, TOP())) { + AttributeError(TOP(), byte.arg); + goto __ERROR; + } DISPATCH(); } - /*******************/ + case OP_LOAD_CLASS_GLOBAL: { + assert(self->__curr_class.type); + py_Name name = byte.arg; + if(py_getattr(&self->__curr_class, name, SP())) { + SP()++; + DISPATCH(); + } + // load global if attribute not found + py_Ref tmp = Frame__f_globals_try_get(frame, name); + if(tmp) { + PUSH(tmp); + DISPATCH(); + } + tmp = py_getdict(&self->builtins, name); + if(tmp) { + PUSH(tmp); + DISPATCH(); + } + NameError(name); + goto __ERROR; + } + case OP_LOAD_METHOD: { + // `py_getunboundmethod` never fails on `fallback=true` + py_getunboundmethod(TOP(), byte.arg, true, TOP(), SP()); + SP()++; + DISPATCH(); + } + case OP_LOAD_SUBSCR: { + // [a, b] -> a[b] + int n = py_callmethod(SECOND(), __getitem__, TOP()); + HANDLE_RETVAL(n); + // [a, b, retval] + *THIRD() = *TOP(); // [retval, b, retval] + STACK_SHRINK(2); // [retval] + DISPATCH(); + } + case OP_STORE_FAST: frame->locals[byte.arg] = POPX(); DISPATCH(); + case OP_STORE_NAME: { + py_Name _name = byte.arg; + py_TValue _0 = POPX(); + if(frame->function) { + py_Ref slot = Frame__f_locals_try_get(frame, _name); + if(slot != NULL) { + *slot = _0; // store in locals if possible + } else { + // Function& func = frame->_callable->as(); + // if(func.decl == __dynamic_func_decl) { + // assert(func._closure != nullptr); + // func._closure->set(_name, _0); + // } else { + // NameError(_name); + // goto __ERROR; + // } + } + } else { + pk_NameDict__set(Frame__f_globals(frame), _name, _0); + } + DISPATCH(); + } + case OP_STORE_GLOBAL: + pk_NameDict__set(Frame__f_globals(frame), byte.arg, POPX()); + DISPATCH(); - // ... + case OP_STORE_ATTR: { + int err = py_setattr(TOP(), byte.arg, SECOND()); + if(err) goto __ERROR; + STACK_SHRINK(2); + DISPATCH(); + } + case OP_STORE_SUBSCR: { + // [val, a, b] -> a[b] = val + py_TValue* backup = SP(); + PUSH(THIRD()); // [val, a, b, val] + bool ok = py_getunboundmethod(THIRD(), __setitem__, false, FOURTH(), THIRD()); + if(!ok) { + // __setitem__ not found + goto __ERROR; + } + // [__setitem__, self, b, val] + int n = py_vectorcall(3, 0); + if(n < 0) goto __ERROR; + SP() = backup; // discard retval if any + DISPATCH(); + } + case OP_DELETE_FAST: { + py_Ref tmp = &frame->locals[byte.arg]; + if(py_isnull(tmp)) { + UnboundLocalError(c11__getitem(uint16_t, &frame->co->varnames, byte.arg)); + goto __ERROR; + } + py_newnull(tmp); + DISPATCH(); + } + case OP_DELETE_NAME: { + StrName name = byte.arg; + if(frame->function) { + py_TValue* slot = Frame__f_locals_try_get(frame, name); + if(slot) { + py_newnull(slot); + } else { + // Function& func = frame->_callable->as(); + // if(func.decl == __dynamic_func_decl) { + // assert(func._closure != nullptr); + // bool ok = func._closure->del(_name); + // if(!ok) vm->NameError(_name); + // } else { + // vm->NameError(_name); + // } + } + } else { + // if(!frame->f_globals().del(_name)) vm->NameError(_name); + bool ok = pk_NameDict__del(Frame__f_globals(frame), name); + if(!ok) { + NameError(name); + goto __ERROR; + } + } + DISPATCH(); + } + case OP_DELETE_GLOBAL: { + StrName name = byte.arg; + bool ok = pk_NameDict__del(Frame__f_globals(frame), name); + if(!ok) { + NameError(name); + goto __ERROR; + } + DISPATCH(); + } - /*******************/ + case OP_DELETE_ATTR: { + int err = py_delattr(TOP(), byte.arg); + if(err) goto __ERROR; + POP(); + DISPATCH(); + } + + case OP_DELETE_SUBSCR: { + // [a, b] -> del a[b] + py_Ref backup = SP(); + int n = py_callmethod(SECOND(), __delitem__, TOP()); + if(n < 0) { goto __ERROR; } + SP() = backup; // discard retval if any + DISPATCH(); + } + + /*****************************************/ + + case OP_BUILD_LONG: { + py_Ref _0 = py_getdict(&self->builtins, pk_id_long); + assert(_0 != NULL); + int n = py_call(_0, TOP()); + if(n < 0) goto __ERROR; + assert(n == 1); + // [x, long(x)] + *SECOND() = *TOP(); // [long(x), long(x)] + POP(); // [long(x)] + DISPATCH(); + } + + case OP_BUILD_IMAG: { + py_Ref _0 = py_getdict(&self->builtins, pk_id_complex); + assert(_0 != NULL); + py_TValue zero; + py_newint(&zero, 0); + int n = py_call(_0, &zero, TOP()); + if(n < 0) goto __ERROR; + assert(n == 1); + // [x, complex(0, x)] + *SECOND() = *TOP(); // [complex(0, x), complex(0, x)] + POP(); // [complex(0, x)] + DISPATCH(); + } + case OP_BUILD_BYTES: { + py_Str* s = py_touserdata(TOP()); + unsigned char* p = (unsigned char*)malloc(s->size); + memcpy(p, py_Str__data(s), s->size); + py_newbytes(SP()++, p, s->size); + DISPATCH(); + } case OP_BUILD_TUPLE: { py_TValue tmp; py_newtuple(&tmp, byte.arg); @@ -216,19 +416,59 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) { py_tuple__setitem(&tmp, i, begin + i); } SP() = begin; - PUSH(tmp); + PUSH(&tmp); DISPATCH(); } + // case OP_BUILD_LIST: { + // PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list()); + // STACK_SHRINK(byte.arg); + // PUSH(_0); + // DISPATCH(); + // } + // case OP_BUILD_DICT: { + // if(byte.arg == 0) { + // PUSH(VAR(Dict())); + // DISPATCH() + // } + // PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list()); + // _0 = call(_t(tp_dict), _0); + // STACK_SHRINK(byte.arg); + // PUSH(_0); + // DISPATCH(); + // } + // case OP_BUILD_SET: { + // PyVar _0 = VAR(STACK_VIEW(byte.arg).to_list()); + // _0 = call(builtins->attr()[pk_id_set], _0); + // STACK_SHRINK(byte.arg); + // PUSH(_0); + // DISPATCH(); + // } + // case OP_BUILD_SLICE: { + // PyVar _2 = POPX(); // step + // PyVar _1 = POPX(); // stop + // PyVar _0 = POPX(); // start + // PUSH(VAR(Slice(_0, _1, _2))); + // DISPATCH(); + // } + // case OP_BUILD_STRING: { + // SStream ss; + // ArgsView view = STACK_VIEW(byte.arg); + // for(PyVar obj: view) + // ss << py_str(obj); + // STACK_SHRINK(byte.arg); + // PUSH(VAR(ss.str())); + // DISPATCH(); + // } /**************************** */ case OP_RETURN_VALUE: { - py_TValue tmp = byte.arg == BC_NOARG ? POPX() : self->None; + py_TValue tmp = byte.arg == BC_NOARG ? POPX() : self->None; pk_VM__pop_frame(self); if(frame == base_frame) { // [ frameBase<- ] self->last_retval = tmp; return RES_RETURN; } else { frame = self->top_frame; - PUSH(tmp); + PUSH(&tmp); goto __NEXT_FRAME; } DISPATCH(); diff --git a/src/interpreter/py_number.c b/src/interpreter/py_number.c index 9d9ab283..01e542c3 100644 --- a/src/interpreter/py_number.c +++ b/src/interpreter/py_number.c @@ -175,7 +175,7 @@ static int _py_int__invert__(int argc, py_Ref argv) { } static int _py_int__bit_length(int argc, py_Ref argv) { - int64_t x = py_toint(&argv[0]); + int64_t x = py_toint(py_arg(0)); if(x < 0) x = -x; int bits = 0; while(x) { diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 439ece22..8ef0852e 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -58,8 +58,8 @@ void pk_VM__ctor(pk_VM* self){ self->last_error = NULL; self->last_retval = PY_NULL; - self->__curr_class = NULL; - self->__cached_object_new = NULL; + self->__curr_class = PY_NULL; + self->__cached_object_new = PY_NULL; self->__dynamic_func_decl = NULL; pk_ManagedHeap__ctor(&self->heap, self); diff --git a/src/public/py_ops.c b/src/public/py_ops.c index bb7c33dc..81686e79 100644 --- a/src/public/py_ops.c +++ b/src/public/py_ops.c @@ -15,14 +15,14 @@ int py_repr(const py_Ref val) { return py_callmethod(val, __repr__); } -int py_getattr(const py_Ref self, py_Name name, py_Ref out){ - return -1; +bool py_getattr(const py_Ref self, py_Name name, py_Ref out){ + return true; } int py_setattr(py_Ref self, py_Name name, const py_Ref val){ return -1; } -int py_callmethod(py_Ref self, py_Name name, ...){ +int py_delattr(py_Ref self, py_Name name){ return -1; -} \ No newline at end of file +} diff --git a/src/public/values.c b/src/public/values.c index a946b4f8..7f35ee64 100644 --- a/src/public/values.c +++ b/src/public/values.c @@ -39,6 +39,18 @@ void py_newstrn(py_Ref out, const char* data, int size) { out->_obj = obj; } +void py_newbytes(py_Ref out, const unsigned char* data, int size) { + pk_ManagedHeap* heap = &pk_current_vm->heap; + // 4 bytes size + data + PyObject* obj = pk_ManagedHeap__gcnew(heap, tp_bytes, 0, sizeof(int) + size); + int* psize = (int*)PyObject__value(obj); + *psize = size; + memcpy(psize + 1, data, size); + out->type = tp_bytes; + out->is_ptr = true; + out->_obj = obj; +} + void py_newnone(py_Ref out) { pk_VM* vm = pk_current_vm; *out = vm->None; diff --git a/src/public/vm.c b/src/public/vm.c index 2fb9e14b..d073daeb 100644 --- a/src/public/vm.c +++ b/src/public/vm.c @@ -45,4 +45,20 @@ int py_eval(const char* source, py_Ref out) { return 0; } PK_UNREACHABLE(); +} + +int py_call(py_Ref callable, ...){ + return -1; +} + +int py_callmethod(py_Ref self, py_Name name, ...){ + return -1; +} + +int py_vectorcall(int argc, int kwargc){ + return -1; +} + +bool py_getunboundmethod(const py_Ref self, py_Name name, bool fallback, py_Ref out, py_Ref out_self){ + return -1; } \ No newline at end of file