2024-07-14 12:49:29 +08:00

875 lines
34 KiB
C

#include "pocketpy/common/config.h"
#include "pocketpy/interpreter/vm.h"
#include "pocketpy/common/memorypool.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/objects/codeobject.h"
#include "pocketpy/pocketpy.h"
#include <stdbool.h>
static bool stack_unpack_sequence(pk_VM* self, uint16_t arg);
#define DISPATCH() \
do { \
frame->ip++; \
goto __NEXT_STEP; \
} while(0)
#define DISPATCH_JUMP(__offset) \
do { \
frame->ip += __offset; \
goto __NEXT_STEP; \
} while(0)
#define DISPATCH_JUMP_ABSOLUTE(__target) \
do { \
frame->ip = c11__at(Bytecode, &frame->co->codes, __target); \
goto __NEXT_STEP; \
} while(0)
/* 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 FOURTH() (self->stack.sp - 4)
#define STACK_SHRINK(n) (self->stack.sp -= n)
#define STACK_GROW(n) (self->stack.sp += n)
#define PUSH(v) \
do { \
*self->stack.sp = *(v); \
self->stack.sp++; \
} while(0)
#define POP() (--self->stack.sp)
#define POPX() (*--self->stack.sp)
#define SP() (self->stack.sp)
// [a, b] -> [?, a, b]
#define INSERT_THIRD() \
do { \
PUSH(TOP()); \
*SECOND() = *THIRD(); \
} while(0)
#define vectorcall_opcall(argc, kwargc) \
do { \
pk_FrameResult res = pk_VM__vectorcall(self, (argc), (kwargc), true); \
switch(res) { \
case RES_RETURN: PUSH(&self->last_retval); break; \
case RES_CALL: frame = self->top_frame; goto __NEXT_FRAME; \
case RES_ERROR: goto __ERROR; \
default: c11__unreachedable(); \
} \
} while(0)
pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
Frame* frame = self->top_frame;
const Frame* base_frame = frame;
while(true) {
Bytecode byte;
__NEXT_FRAME:
// if(__internal_exception.type == InternalExceptionType::Null) {
// // None
// frame->_ip++;
// } else if(__internal_exception.type == InternalExceptionType::Handled) {
// // HandledException + continue
// frame->_ip = c11__at(Bytecode, &frame->co->codes, __internal_exception.arg);
// __internal_exception = {};
// } else {
// // UnhandledException + continue (need_raise = true)
// // ToBeRaisedException + continue (need_raise = true)
// __internal_exception = {};
// __raise_exc(); // no return
// }
frame->ip++;
__NEXT_STEP:
byte = *frame->ip;
pk_print_stack(self, frame, byte);
switch((Opcode)byte.op) {
case OP_NO_OP: DISPATCH();
/*****************************************/
case OP_POP_TOP: POP(); DISPATCH();
case OP_DUP_TOP: PUSH(TOP()); DISPATCH();
case OP_DUP_TOP_TWO:
// [a, b]
PUSH(SECOND()); // [a, b, a]
PUSH(SECOND()); // [a, b, a, b]
DISPATCH();
case OP_ROT_TWO: {
py_TValue tmp = *TOP();
*TOP() = *SECOND();
*SECOND() = tmp;
DISPATCH();
}
case OP_ROT_THREE: {
// [a, b, c] -> [c, a, b]
py_TValue tmp = *TOP();
*TOP() = *SECOND();
*SECOND() = *THIRD();
*THIRD() = tmp;
DISPATCH();
}
case OP_PRINT_EXPR:
if(TOP()->type != tp_NoneType) {
bool ok = py_repr(TOP());
if(!ok) goto __ERROR;
self->_stdout("%s\n", py_tostr(&self->last_retval));
}
POP();
DISPATCH();
/*****************************************/
case OP_LOAD_CONST: PUSH(c11__at(py_TValue, &frame->co->consts, byte.arg)); DISPATCH();
case OP_LOAD_NONE: py_newnone(SP()++); DISPATCH();
case OP_LOAD_TRUE: py_newbool(SP()++, true); DISPATCH();
case OP_LOAD_FALSE: py_newbool(SP()++, false); DISPATCH();
/*****************************************/
case OP_LOAD_SMALL_INT: py_newint(SP()++, (int16_t)byte.arg); DISPATCH();
/*****************************************/
case OP_LOAD_ELLIPSIS: py_newellipsis(SP()++); DISPATCH();
case OP_LOAD_FUNCTION: {
FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg);
Function* ud = py_newobject(SP(), tp_function, 0, sizeof(Function));
Function__ctor(ud, decl, frame->module);
if(decl->nested) {
ud->closure = FastLocals__to_namedict(frame->locals, frame->locals_co);
py_Name name = py_namev(c11_string__sv(decl->code.name));
// capture itself to allow recursion
pk_NameDict__set(ud->closure, name, *SP());
}
SP()++;
DISPATCH();
}
case OP_LOAD_NULL:
py_newnil(SP()++);
DISPATCH();
/*****************************************/
case OP_LOAD_FAST: {
PUSH(&frame->locals[byte.arg]);
if(py_isnil(TOP())) {
py_Name name = c11__getitem(uint16_t, &frame->co->varnames, byte.arg);
UnboundLocalError(name);
goto __ERROR;
}
DISPATCH();
}
case OP_LOAD_NAME: {
py_Name name = byte.arg;
py_Ref tmp = Frame__f_locals_try_get(frame, name);
if(tmp != NULL) {
if(py_isnil(tmp)) {
UnboundLocalError(name);
goto __ERROR;
}
PUSH(tmp);
DISPATCH();
}
tmp = Frame__f_closure_try_get(frame, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
}
tmp = Frame__f_globals_try_get(frame, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
}
tmp = py_getdict(&self->builtins, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
}
NameError(name);
goto __ERROR;
}
case OP_LOAD_NONLOCAL: {
py_Name name = byte.arg;
py_Ref tmp = Frame__f_closure_try_get(frame, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
}
tmp = Frame__f_globals_try_get(frame, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
}
tmp = py_getdict(&self->builtins, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
}
NameError(name);
goto __ERROR;
}
case OP_LOAD_GLOBAL: {
py_Name name = byte.arg;
py_Ref tmp = Frame__f_globals_try_get(frame, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
}
tmp = py_getdict(&self->builtins, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
}
NameError(name);
goto __ERROR;
}
case OP_LOAD_ATTR: {
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: {
// [self]
bool ok = py_getunboundmethod(TOP(), byte.arg, TOP(), SP());
if(ok) {
// [unbound, self]
SP()++;
} else {
// fallback to getattr
int res = py_getattr(TOP(), byte.arg, TOP());
if(res != 1) {
if(res == 0) { AttributeError(TOP(), byte.arg); }
goto __ERROR;
}
}
DISPATCH();
}
case OP_LOAD_SUBSCR: {
// [a, b] -> a[b]
py_Ref magic = py_tpfindmagic(SECOND()->type, __getitem__);
if(magic) {
if(magic->type == tp_nativefunc) {
py_TValue* next_sp = TOP();
bool ok = magic->_cfunc(2, SECOND());
if(!ok) goto __ERROR;
SP() = next_sp;
*TOP() = self->last_retval;
} else {
INSERT_THIRD(); // [?, a, b]
*THIRD() = *magic; // [__getitem__, a, b]
if(!py_vectorcall(1, 0)) goto __ERROR;
}
DISPATCH();
}
TypeError("'%t' object is not subscriptable", SECOND()->type);
goto __ERROR;
}
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<Function>();
// 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_Ref magic = py_tpfindmagic(SECOND()->type, __setitem__);
if(magic) {
PUSH(THIRD()); // [val, a, b, val]
if(magic->type == tp_nativefunc) {
py_TValue* next_sp = FOURTH();
bool ok = magic->_cfunc(3, THIRD());
if(!ok) goto __ERROR;
SP() = next_sp;
} else {
*FOURTH() = *magic; // [__selitem__, a, b, val]
if(!py_vectorcall(2, 0)) goto __ERROR;
POP(); // discard retval
}
DISPATCH();
}
TypeError("'%t' object does not support item assignment", SECOND()->type);
goto __ERROR;
}
case OP_DELETE_FAST: {
py_Ref tmp = &frame->locals[byte.arg];
if(py_isnil(tmp)) {
py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg);
UnboundLocalError(name);
goto __ERROR;
}
py_newnil(tmp);
DISPATCH();
}
case OP_DELETE_NAME: {
py_Name name = byte.arg;
if(frame->function) {
py_TValue* slot = Frame__f_locals_try_get(frame, name);
if(slot) {
py_newnil(slot);
} else {
// Function& func = frame->_callable->as<Function>();
// 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: {
py_Name name = byte.arg;
bool ok = pk_NameDict__del(Frame__f_globals(frame), name);
if(!ok) {
NameError(name);
goto __ERROR;
}
DISPATCH();
}
case OP_DELETE_ATTR: {
if(!py_delattr(TOP(), byte.arg)) goto __ERROR;
DISPATCH();
}
case OP_DELETE_SUBSCR: {
// [a, b] -> del a[b]
py_Ref magic = py_tpfindmagic(SECOND()->type, __delitem__);
if(magic) {
if(magic->type == tp_nativefunc) {
py_TValue* next_sp = SECOND();
bool ok = magic->_cfunc(2, SECOND());
if(!ok) goto __ERROR;
SP() = next_sp;
} else {
INSERT_THIRD(); // [?, a, b]
*THIRD() = *magic; // [__delitem__, a, b]
if(!py_vectorcall(1, 0)) goto __ERROR;
POP(); // discard retval
}
DISPATCH();
}
TypeError("'%t' object does not support item deletion", SECOND()->type);
goto __ERROR;
}
/*****************************************/
case OP_BUILD_LONG: {
// [x]
py_Ref f = py_getdict(&self->builtins, py_name("long"));
assert(f != NULL);
if(!py_call(f, 1, TOP())) goto __ERROR;
*TOP() = self->last_retval;
DISPATCH();
}
case OP_BUILD_IMAG: {
// [x]
py_Ref f = py_getdict(&self->builtins, py_name("complex"));
assert(f != NULL);
py_TValue tmp = *TOP();
*TOP() = *f; // [complex]
py_newnil(SP()++); // [complex, NULL]
py_newint(SP()++, 0); // [complex, NULL, 0]
*SP()++ = tmp; // [complex, NULL, 0, x]
if(!py_vectorcall(2, 0)) goto __ERROR;
DISPATCH();
}
case OP_BUILD_BYTES: {
int size;
const char* data = py_tostrn(TOP(), &size);
unsigned char* p = py_newbytes(TOP(), size);
memcpy(p, data, size);
DISPATCH();
}
case OP_BUILD_TUPLE: {
py_TValue tmp;
py_newtuple(&tmp, byte.arg);
py_TValue* begin = SP() - byte.arg;
for(int i = 0; i < byte.arg; i++) {
py_tuple__setitem(&tmp, i, begin + i);
}
SP() = begin;
PUSH(&tmp);
DISPATCH();
}
case OP_BUILD_LIST: {
py_TValue tmp;
py_newlistn(&tmp, byte.arg);
py_TValue* begin = SP() - byte.arg;
for(int i = 0; i < byte.arg; i++) {
py_list__setitem(&tmp, i, begin + i);
}
SP() = begin;
PUSH(&tmp);
DISPATCH();
}
case OP_BUILD_DICT: {
py_TValue* begin = SP() - byte.arg * 2;
py_Ref tmp = py_pushtmp();
py_newdict(tmp);
for(int i = 0; i < byte.arg * 2; i += 2) {
py_dict__setitem(tmp, begin + i, begin + i + 1);
if(py_checkexc()) goto __ERROR;
}
SP() = begin;
PUSH(tmp);
DISPATCH();
}
case OP_BUILD_SET: {
py_TValue* begin = SP() - byte.arg;
py_Ref typeobject_set = py_getdict(&self->builtins, py_name("set"));
assert(typeobject_set != NULL);
py_push(typeobject_set);
py_pushnil();
if(!py_vectorcall(0, 0)) goto __ERROR;
py_push(py_retval()); // empty set
py_Name id_add = py_name("add");
for(int i = 0; i < byte.arg; i++) {
if(!py_callmethod(TOP(), id_add, 1, begin + i)) goto __ERROR;
}
py_TValue tmp = *TOP();
SP() = begin;
PUSH(&tmp);
DISPATCH();
}
case OP_BUILD_SLICE: {
// [start, stop, step]
py_TValue tmp;
py_newslice(&tmp);
py_setslot(&tmp, 0, THIRD());
py_setslot(&tmp, 1, SECOND());
py_setslot(&tmp, 2, TOP());
STACK_SHRINK(3);
PUSH(&tmp);
DISPATCH();
}
case OP_BUILD_STRING: {
py_TValue* begin = SP() - byte.arg;
c11_sbuf ss;
c11_sbuf__ctor(&ss);
for(int i = 0; i < byte.arg; i++) {
if(!py_str(begin + i)) goto __ERROR;
c11_sbuf__write_sv(&ss, py_tosv(&self->last_retval));
}
SP() = begin;
c11_string* res = c11_sbuf__submit(&ss);
py_newstrn(SP()++, res->data, res->size);
c11_string__delete(res);
DISPATCH();
}
/*****************************/
case OP_BINARY_OP: {
py_Name op = byte.arg & 0xFF;
py_Name rop = byte.arg >> 8;
if(!pk_stack_binaryop(self, op, rop)) goto __ERROR;
POP();
*TOP() = self->last_retval;
DISPATCH();
}
case OP_IS_OP: {
bool res = py_isidentical(SECOND(), TOP());
POP();
if(byte.arg) res = !res;
py_newbool(TOP(), res);
DISPATCH();
}
case OP_CONTAINS_OP: {
// [b, a] -> b __contains__ a (a in b) -> [retval]
py_Ref magic = py_tpfindmagic(SECOND()->type, __contains__);
if(magic) {
if(magic->type == tp_nativefunc) {
py_TValue* next_sp = TOP();
bool ok = magic->_cfunc(2, SECOND());
if(!ok) goto __ERROR;
SP() = next_sp;
*TOP() = self->last_retval;
} else {
INSERT_THIRD(); // [?, b, a]
*THIRD() = *magic; // [__contains__, a, b]
if(!py_vectorcall(1, 0)) goto __ERROR;
}
bool res = py_tobool(TOP());
if(byte.arg) py_newbool(TOP(), !res);
DISPATCH();
}
// TODO: fallback to __iter__?
TypeError("argument of type '%t' is not iterable", SECOND()->type);
goto __ERROR;
}
/*****************************************/
case OP_JUMP_FORWARD: DISPATCH_JUMP((int16_t)byte.arg);
case OP_POP_JUMP_IF_FALSE: {
int res = py_bool(TOP());
if(res < 0) goto __ERROR;
POP();
if(!res) DISPATCH_JUMP((int16_t)byte.arg);
DISPATCH();
}
case OP_POP_JUMP_IF_TRUE: {
int res = py_bool(TOP());
if(res < 0) goto __ERROR;
POP();
if(res) DISPATCH_JUMP((int16_t)byte.arg);
DISPATCH();
}
case OP_JUMP_IF_TRUE_OR_POP: {
int res = py_bool(TOP());
if(res < 0) goto __ERROR;
if(res) {
DISPATCH_JUMP((int16_t)byte.arg);
} else {
POP();
DISPATCH();
}
}
case OP_JUMP_IF_FALSE_OR_POP: {
int res = py_bool(TOP());
if(res < 0) goto __ERROR;
if(!res) {
DISPATCH_JUMP((int16_t)byte.arg);
} else {
POP();
DISPATCH();
}
}
case OP_SHORTCUT_IF_FALSE_OR_POP: {
int res = py_bool(TOP());
if(res < 0) goto __ERROR;
if(!res) { // [b, False]
STACK_SHRINK(2); // []
py_newbool(SP()++, false); // [False]
DISPATCH_JUMP((int16_t)byte.arg);
} else {
POP(); // [b]
DISPATCH();
}
}
case OP_LOOP_CONTINUE:
// just an alias of OP_JUMP_FORWARD
DISPATCH_JUMP((int16_t)byte.arg);
case OP_LOOP_BREAK: {
int target = Frame__ip(frame) + byte.arg;
Frame__prepare_jump_break(frame, &self->stack, target);
DISPATCH_JUMP((int16_t)byte.arg);
}
case OP_JUMP_ABSOLUTE_TOP: {
int target = py_toint(TOP());
POP();
DISPATCH_JUMP_ABSOLUTE(target);
}
// case OP_GOTO: {
// py_Name _name(byte.arg);
// int target = c11_smallmap_n2i__get(&frame->co->labels, byte.arg, -1);
// if(target < 0) RuntimeError(_S("label ", _name.escape(), " not found"));
// frame->prepare_jump_break(&s_data, target);
// DISPATCH_JUMP_ABSOLUTE(target)
// }
/*****************************************/
case OP_FSTRING_EVAL: {
assert(false);
}
case OP_REPR: {
assert(false);
}
case OP_CALL: {
pk_ManagedHeap__collect_if_needed(&self->heap);
vectorcall_opcall(byte.arg & 0xFF, byte.arg >> 8);
DISPATCH();
}
case OP_CALL_VARGS: {
uint16_t argc = byte.arg & 0xFF;
uint16_t kwargc = byte.arg >> 8;
int size = 0;
py_TValue* buf = self->__vectorcall_buffer;
// if(size == PK_MAX_CO_VARNAMES) {
// ValueError("**kwargs is too large to unpack");
// goto __ERROR;
// }
uint16_t new_argc = argc;
uint16_t new_kwargc = kwargc;
// pop kwargc
for(int i = 0; i < kwargc; i++) {
// [k1, v1, k2, v2, ...] -> reversed
py_TValue value = POPX();
py_TValue key = POPX();
if(value.type == tp_star_wrapper) {
value = *py_getslot(&value, 0);
// unpack dict
if(value.type != tp_dict) {
TypeError("**kwargs should be a dict, got '%t'", value.type);
goto __ERROR;
}
new_kwargc += (0) - 1;
} else {
buf[size++] = value;
buf[size++] = key;
}
}
// pop argc
for(int i = 0; i < argc; i++) {
py_TValue value = POPX();
if(value.type == tp_star_wrapper) {
value = *py_getslot(&value, 0);
int length;
py_TValue* p = pk_arrayview(&value, &length);
if(!p) {
TypeError("*args should be a list or tuple, got '%t'", value.type);
goto __ERROR;
}
for(int j = 0; j < length; j++) {
buf[size++] = p[j];
}
new_argc += length - 1;
} else {
buf[size++] = value;
}
}
// push everything back in reversed order
for(int i = size - 1; i >= 0; i--) {
PUSH(buf + i);
}
vectorcall_opcall(new_argc, new_kwargc);
DISPATCH();
}
case OP_RETURN_VALUE: {
if(byte.arg == BC_NOARG) {
self->last_retval = POPX();
} else {
py_newnone(&self->last_retval);
}
pk_VM__pop_frame(self);
if(frame == base_frame) { // [ frameBase<- ]
return RES_RETURN;
} else {
frame = self->top_frame;
PUSH(&self->last_retval);
goto __NEXT_FRAME;
}
DISPATCH();
}
case OP_YIELD_VALUE: {
assert(false);
}
/////////
case OP_LIST_APPEND: {
// [list, iter, value]
py_list__append(THIRD(), TOP());
POP();
DISPATCH();
}
case OP_DICT_ADD: {
// [dict, iter, key, value]
py_dict__setitem(FOURTH(), SECOND(), TOP());
if(py_checkexc()) goto __ERROR;
STACK_SHRINK(2);
DISPATCH();
}
case OP_SET_ADD: {
DISPATCH();
}
/////////
case OP_UNARY_NEGATIVE: {
if(!py_callmagic(__neg__, 1, TOP())) goto __ERROR;
*TOP() = self->last_retval;
DISPATCH();
}
case OP_UNARY_NOT: {
int res = py_bool(TOP());
if(res < 0) goto __ERROR;
py_newbool(TOP(), !res);
DISPATCH();
}
case OP_UNARY_STAR: {
py_TValue value = POPX();
int* level = py_newobject(SP()++, tp_star_wrapper, 1, sizeof(int));
*level = byte.arg;
py_setslot(TOP(), 0, &value);
DISPATCH();
}
case OP_UNARY_INVERT: {
if(!py_callmagic(__invert__, 1, TOP())) goto __ERROR;
*TOP() = self->last_retval;
DISPATCH();
}
////////////////
case OP_GET_ITER: {
if(!py_iter(TOP())) goto __ERROR;
*TOP() = *py_retval();
DISPATCH();
}
case OP_FOR_ITER: {
int res = py_next(TOP());
if(res == -1) goto __ERROR;
if(res) {
PUSH(py_retval());
DISPATCH();
} else {
int target = Frame__prepare_loop_break(frame, &self->stack);
DISPATCH_JUMP_ABSOLUTE(target);
}
}
////////
case OP_UNPACK_SEQUENCE: {
if(!stack_unpack_sequence(self, byte.arg)) goto __ERROR;
DISPATCH();
}
case OP_UNPACK_EX: {
int length;
py_TValue* p = pk_arrayview(TOP(), &length);
if(!p) {
TypeError("expected list or tuple to unpack, got '%t'", TOP()->type);
goto __ERROR;
}
int exceed = length - byte.arg;
if(exceed < 0) {
ValueError("not enough values to unpack");
goto __ERROR;
}
POP();
for(int i = 0; i < byte.arg; i++) {
PUSH(p + i);
}
py_newlistn(SP()++, exceed);
for(int i = 0; i < exceed; i++) {
py_list__setitem(TOP(), i, p + byte.arg + i);
}
DISPATCH();
}
///////////
case OP_RAISE_ASSERT: {
if(byte.arg) {
if(!py_str(TOP())) goto __ERROR;
POP();
py_exception("AssertionError", "%s", py_tostr(py_retval()));
} else {
py_exception("AssertionError", "");
}
goto __ERROR;
}
default: c11__unreachedable();
}
assert(false); // should never reach here
__ERROR:
// 1. Exception can be handled inside the current frame
// 2. Exception need to be propagated to the upper frame
printf("error.op: %s, line: %d\n", pk_opname(byte.op), Frame__lineno(frame));
return RES_ERROR;
}
return RES_RETURN;
}
bool pk_stack_binaryop(pk_VM* self, py_Name op, py_Name rop) {
// [a, b]
py_Ref magic = py_tpfindmagic(SECOND()->type, op);
if(magic) {
bool ok = py_call(magic, 2, SECOND());
if(!ok) return false;
if(self->last_retval.type != tp_NotImplementedType) return true;
}
// try reverse operation
if(rop) {
// [a, b] -> [b, a]
py_TValue tmp = *TOP();
*TOP() = *SECOND();
*SECOND() = tmp;
magic = py_tpfindmagic(SECOND()->type, rop);
if(magic) {
bool ok = py_call(magic, 2, SECOND());
if(!ok) return false;
if(self->last_retval.type != tp_NotImplementedType) return true;
}
}
// eq/ne op never fails
if(op == __eq__ || op == __ne__) {
bool res = py_isidentical(SECOND(), TOP());
py_newbool(py_retval(), res);
return true;
}
return py_exception("TypeError", "unsupported operand type(s) for '%n'", op);
}
bool py_binaryop(const py_Ref lhs, const py_Ref rhs, py_Name op, py_Name rop) {
pk_VM* self = pk_current_vm;
PUSH(lhs);
PUSH(rhs);
bool ok = pk_stack_binaryop(self, op, rop);
STACK_SHRINK(2);
return ok;
}
static bool stack_unpack_sequence(pk_VM* self, uint16_t arg) {
int length;
py_TValue* p = pk_arrayview(TOP(), &length);
if(!p) return TypeError("expected list or tuple to unpack, got '%t'", TOP()->type);
if(length != arg) return ValueError("expected %d values to unpack, got %d", arg, length);
POP();
for(int i = 0; i < length; i++) {
PUSH(p + i);
}
return true;
}