#pragma once #include "codeobject.h" #include "common.h" #include "memory.h" #include "obj.h" #include "vector.h" namespace pkpy{ // weak reference fast locals struct FastLocals{ // this is a weak reference const NameDictInt* varnames_inv; PyObject** a; int size() const{ return varnames_inv->size(); } PyObject*& operator[](int i){ return a[i]; } PyObject* operator[](int i) const { return a[i]; } FastLocals(const CodeObject* co, PyObject** a): varnames_inv(&co->varnames_inv), a(a) {} FastLocals(const FastLocals& other): varnames_inv(other.varnames_inv), a(other.a) {} PyObject* try_get(StrName name){ int index = varnames_inv->try_get(name); if(index == -1) return nullptr; return a[index]; } bool contains(StrName name){ return varnames_inv->contains(name); } void erase(StrName name){ int index = varnames_inv->try_get(name); if(index == -1) FATAL_ERROR(); a[index] = nullptr; } bool try_set(StrName name, PyObject* value){ int index = varnames_inv->try_get(name); if(index == -1) return false; a[index] = value; return true; } NameDict_ to_namedict(){ NameDict_ dict = make_sp(); // TODO: optimize this, NameDict.items() is expensive for(auto& kv: varnames_inv->items()){ PyObject* value = a[kv.second]; if(value == PY_NULL) continue; dict->set(kv.first, value); } return dict; } }; template struct ValueStackImpl { // We allocate extra MAX_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`. PyObject* _begin[MAX_SIZE + MAX_SIZE/128]; PyObject** _sp; PyObject** _max_end; ValueStackImpl(): _sp(_begin), _max_end(_begin + MAX_SIZE) {} PyObject*& top(){ return _sp[-1]; } PyObject* top() const { return _sp[-1]; } PyObject*& second(){ return _sp[-2]; } PyObject* second() const { return _sp[-2]; } PyObject*& third(){ return _sp[-3]; } PyObject* third() const { return _sp[-3]; } PyObject*& peek(int n){ return _sp[-n]; } PyObject* peek(int n) const { return _sp[-n]; } void push(PyObject* v){ *_sp++ = v; } void pop(){ --_sp; } PyObject* popx(){ return *--_sp; } ArgsView view(int n){ return ArgsView(_sp-n, _sp); } void shrink(int n){ _sp -= n; } int size() const { return _sp - _begin; } bool empty() const { return _sp == _begin; } PyObject** begin() { return _begin; } PyObject** end() { return _sp; } void reset(PyObject** sp) { #if DEBUG_EXTRA_CHECK if(sp < _begin || sp > _begin + MAX_SIZE) FATAL_ERROR(); #endif _sp = sp; } void clear() { _sp = _begin; } bool is_overflow() const { return _sp >= _max_end; } ValueStackImpl(const ValueStackImpl&) = delete; ValueStackImpl(ValueStackImpl&&) = delete; ValueStackImpl& operator=(const ValueStackImpl&) = delete; ValueStackImpl& operator=(ValueStackImpl&&) = delete; }; using ValueStack = ValueStackImpl<32768>; struct Frame { int _ip = -1; int _next_ip = 0; ValueStack* _s; // This is for unwinding only, use `actual_sp_base()` for value stack access PyObject** _sp_base; const CodeObject* co; PyObject* _module; PyObject* _callable; // weak ref FastLocals _locals; NameDict& f_globals() noexcept { return _module->attr(); } PyObject* f_closure_try_get(StrName name){ if(_callable == nullptr) return nullptr; Function& fn = OBJ_GET(Function, _callable); if(fn._closure == nullptr) return nullptr; return fn._closure->try_get(name); } Frame(ValueStack* _s, PyObject** p0, const CodeObject* co, PyObject* _module, PyObject* _callable) : _s(_s), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(co, p0) { } Frame(ValueStack* _s, PyObject** p0, const CodeObject* co, PyObject* _module, PyObject* _callable, FastLocals _locals) : _s(_s), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals) { } Frame(ValueStack* _s, PyObject** p0, const CodeObject_& co, PyObject* _module) : _s(_s), _sp_base(p0), co(co.get()), _module(_module), _callable(nullptr), _locals(co.get(), p0) {} Bytecode next_bytecode() { _ip = _next_ip++; #if DEBUG_EXTRA_CHECK if(_ip >= co->codes.size()) FATAL_ERROR(); #endif return co->codes[_ip]; } Str snapshot(){ int line = co->lines[_ip]; return co->src->snapshot(line); } PyObject** actual_sp_base() const { return _locals.a; } int stack_size() const { return _s->_sp - actual_sp_base(); } ArgsView stack_view() const { return ArgsView(actual_sp_base(), _s->_sp); } void jump_abs(int i){ _next_ip = i; } // void jump_rel(int i){ _next_ip += i; } bool jump_to_exception_handler(){ // try to find a parent try block int block = co->codes[_ip].block; while(block >= 0){ if(co->blocks[block].type == TRY_EXCEPT) break; block = co->blocks[block].parent; } if(block < 0) return false; PyObject* obj = _s->popx(); // pop exception object // get the stack size of the try block (depth of for loops) int _stack_size = co->blocks[block].for_loop_depth; if(stack_size() < _stack_size) throw std::runtime_error("invalid stack size"); _s->reset(actual_sp_base() + _stack_size); // rollback the stack _s->push(obj); // push exception object _next_ip = co->blocks[block].end; return true; } int _exit_block(int i){ if(co->blocks[i].type == FOR_LOOP) _s->pop(); return co->blocks[i].parent; } void jump_abs_break(int target){ const Bytecode& prev = co->codes[_ip]; int i = prev.block; _next_ip = target; if(_next_ip >= co->codes.size()){ while(i>=0) i = _exit_block(i); }else{ const Bytecode& next = co->codes[target]; while(i>=0 && i!=next.block) i = _exit_block(i); if(i!=next.block) throw std::runtime_error("invalid jump"); } } void _gc_mark() const { OBJ_MARK(_module); co->_gc_mark(); } }; }; // namespace pkpy