mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 19:40:18 +00:00
197 lines
6.3 KiB
C++
197 lines
6.3 KiB
C++
#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<NameDict>();
|
|
// 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<size_t MAX_SIZE>
|
|
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
|