#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()){ dict->set(kv.first, a[kv.second]); } return dict; } }; struct ValueStack { static const size_t MAX_SIZE = 32768; // We allocate 512 more bytes to keep `_sp` valid when `is_overflow() == true`. PyObject* _begin[MAX_SIZE + 512]; PyObject** _sp; ValueStack(): _sp(_begin) {} 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 >= _begin + MAX_SIZE; } ValueStack(const ValueStack&) = delete; ValueStack(ValueStack&&) = delete; ValueStack& operator=(const ValueStack&) = delete; ValueStack& operator=(ValueStack&&) = delete; }; 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; 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++; 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); if(_callable != nullptr) OBJ_MARK(_callable); co->_gc_mark(); } }; }; // namespace pkpy