update gc

This commit is contained in:
blueloveTH 2023-03-28 17:08:42 +08:00
parent c2ef720d90
commit f57fa16ee2
18 changed files with 323 additions and 415 deletions

View File

@ -7,7 +7,7 @@ namespace pkpy{
Str _read_file_cwd(const Str& name, bool* ok);
PyVar VM::run_frame(Frame* frame){
PyObject* VM::run_frame(Frame* frame){
while(frame->has_next_bytecode()){
const Bytecode& byte = frame->next_bytecode();
switch (byte.op)
@ -16,7 +16,7 @@ PyVar VM::run_frame(Frame* frame){
case OP_SETUP_DECORATOR: continue;
case OP_LOAD_CONST: frame->push(frame->co->consts[byte.arg]); continue;
case OP_LOAD_FUNCTION: {
const PyVar obj = frame->co->consts[byte.arg];
PyObject* obj = frame->co->consts[byte.arg];
Function f = CAST(Function, obj); // copy
f._module = frame->_module;
frame->push(VAR(f));
@ -37,13 +37,13 @@ PyVar VM::run_frame(Frame* frame){
} continue;
case OP_BUILD_ATTR_REF: case OP_BUILD_ATTR: {
auto& attr = frame->co->names[byte.arg];
PyVar obj = frame->pop_value(this);
PyObject* obj = frame->pop_value(this);
AttrRef ref = AttrRef(obj, NameRef(attr));
if(byte.op == OP_BUILD_ATTR) frame->push(ref.get(this, frame));
else frame->push(PyRef(ref));
} continue;
case OP_BUILD_INDEX: {
PyVar index = frame->pop_value(this);
PyObject* index = frame->pop_value(this);
auto ref = IndexRef(frame->pop_value(this), index);
if(byte.arg > 0) frame->push(ref.get(this, frame));
else frame->push(PyRef(ref));
@ -57,9 +57,6 @@ PyVar VM::run_frame(Frame* frame){
} continue;
case OP_ROT_TWO: ::std::swap(frame->top(), frame->top_1()); continue;
case OP_STORE_REF: {
// PyVar obj = frame->pop_value(this);
// PyVarRef r = frame->pop();
// PyRef_AS_C(r)->set(this, frame, std::move(obj));
PyRef_AS_C(frame->top_1())->set(this, frame, frame->top_value(this));
frame->_pop(); frame->_pop();
} continue;
@ -84,25 +81,25 @@ PyVar VM::run_frame(Frame* frame){
case OP_LOAD_EVAL_FN: frame->push(builtins->attr(m_eval)); continue;
case OP_BEGIN_CLASS: {
auto& name = frame->co->names[byte.arg];
PyVar clsBase = frame->pop_value(this);
PyObject* clsBase = frame->pop_value(this);
if(clsBase == None) clsBase = _t(tp_object);
check_type(clsBase, tp_type);
PyVar cls = new_type_object(frame->_module, name.first, OBJ_GET(Type, clsBase));
PyObject* cls = new_type_object(frame->_module, name.first, OBJ_GET(Type, clsBase));
frame->push(cls);
} continue;
case OP_END_CLASS: {
PyVar cls = frame->pop();
PyObject* cls = frame->pop();
cls->attr()._try_perfect_rehash();
}; continue;
case OP_STORE_CLASS_ATTR: {
auto& name = frame->co->names[byte.arg];
PyVar obj = frame->pop_value(this);
PyVar& cls = frame->top();
PyObject* obj = frame->pop_value(this);
PyObject* cls = frame->top();
cls->attr().set(name.first, std::move(obj));
} continue;
case OP_RETURN_VALUE: return frame->pop_value(this);
case OP_PRINT_EXPR: {
const PyVar expr = frame->top_value(this);
PyObject* expr = frame->top_value(this);
if(expr != None) *_stdout << CAST(Str, asRepr(expr)) << '\n';
} continue;
case OP_POP_TOP: frame->_pop(); continue;
@ -122,7 +119,7 @@ PyVar VM::run_frame(Frame* frame){
Args args(2);
args[1] = frame->pop();
args[0] = frame->top_value(this);
PyVar ret = fast_call(BINARY_SPECIAL_METHODS[byte.arg], std::move(args));
PyObject* ret = fast_call(BINARY_SPECIAL_METHODS[byte.arg], std::move(args));
PyRef_AS_C(frame->top())->set(this, frame, std::move(ret));
frame->_pop();
} continue;
@ -130,7 +127,7 @@ PyVar VM::run_frame(Frame* frame){
Args args(2);
args[1] = frame->pop_value(this);
args[0] = frame->top_value(this);
PyVar ret = fast_call(BITWISE_SPECIAL_METHODS[byte.arg], std::move(args));
PyObject* ret = fast_call(BITWISE_SPECIAL_METHODS[byte.arg], std::move(args));
PyRef_AS_C(frame->top())->set(this, frame, std::move(ret));
frame->_pop();
} continue;
@ -141,14 +138,14 @@ PyVar VM::run_frame(Frame* frame){
frame->top() = fast_call(CMP_SPECIAL_METHODS[byte.arg], std::move(args));
} continue;
case OP_IS_OP: {
PyVar rhs = frame->pop_value(this);
PyObject* rhs = frame->pop_value(this);
bool ret_c = rhs == frame->top_value(this);
if(byte.arg == 1) ret_c = !ret_c;
frame->top() = VAR(ret_c);
} continue;
case OP_CONTAINS_OP: {
PyVar rhs = frame->pop_value(this);
bool ret_c = CAST(bool, call(rhs, __contains__, one_arg(frame->pop_value(this))));
PyObject* rhs = frame->pop_value(this);
bool ret_c = CAST(bool, call(rhs, __contains__, Args{frame->pop_value(this)}));
if(byte.arg == 1) ret_c = !ret_c;
frame->push(VAR(ret_c));
} continue;
@ -156,8 +153,8 @@ PyVar VM::run_frame(Frame* frame){
frame->top() = num_negated(frame->top_value(this));
continue;
case OP_UNARY_NOT: {
PyVar obj = frame->pop_value(this);
const PyVar& obj_bool = asBool(obj);
PyObject* obj = frame->pop_value(this);
PyObject* obj_bool = asBool(obj);
frame->push(VAR(!_CAST(bool, obj_bool)));
} continue;
case OP_POP_JUMP_IF_FALSE:
@ -168,9 +165,9 @@ PyVar VM::run_frame(Frame* frame){
case OP_LOAD_FALSE: frame->push(False); continue;
case OP_LOAD_ELLIPSIS: frame->push(Ellipsis); continue;
case OP_ASSERT: {
PyVar _msg = frame->pop_value(this);
PyObject* _msg = frame->pop_value(this);
Str msg = CAST(Str, asStr(_msg));
PyVar expr = frame->pop_value(this);
PyObject* expr = frame->pop_value(this);
if(asBool(expr) != True) _error("AssertionError", msg);
} continue;
case OP_EXCEPTION_MATCH: {
@ -179,7 +176,7 @@ PyVar VM::run_frame(Frame* frame){
frame->push(VAR(e.match_type(name)));
} continue;
case OP_RAISE: {
PyVar obj = frame->pop_value(this);
PyObject* obj = frame->pop_value(this);
Str msg = obj == None ? "" : CAST(Str, asStr(obj));
StrName type = frame->co->names[byte.arg].first;
_error(type, msg);
@ -190,32 +187,32 @@ PyVar VM::run_frame(Frame* frame){
continue;
case OP_BUILD_MAP: {
Args items = frame->pop_n_values_reversed(this, byte.arg*2);
PyVar obj = call(builtins->attr("dict"));
PyObject* obj = call(builtins->attr("dict"));
for(int i=0; i<items.size(); i+=2){
call(obj, __setitem__, two_args(items[i], items[i+1]));
call(obj, __setitem__, Args{items[i], items[i+1]});
}
frame->push(obj);
} continue;
case OP_BUILD_SET: {
PyVar list = VAR(
PyObject* list = VAR(
frame->pop_n_values_reversed(this, byte.arg).move_to_list()
);
PyVar obj = call(builtins->attr("set"), one_arg(list));
PyObject* obj = call(builtins->attr("set"), Args{list});
frame->push(obj);
} continue;
case OP_LIST_APPEND: {
PyVar obj = frame->pop_value(this);
PyObject* obj = frame->pop_value(this);
List& list = CAST(List&, frame->top_1());
list.push_back(std::move(obj));
} continue;
case OP_MAP_ADD: {
PyVar value = frame->pop_value(this);
PyVar key = frame->pop_value(this);
call(frame->top_1(), __setitem__, two_args(key, value));
PyObject* value = frame->pop_value(this);
PyObject* key = frame->pop_value(this);
call(frame->top_1(), __setitem__, Args{key, value});
} continue;
case OP_SET_ADD: {
PyVar obj = frame->pop_value(this);
call(frame->top_1(), "add", one_arg(obj));
PyObject* obj = frame->pop_value(this);
call(frame->top_1(), "add", Args{obj});
} continue;
case OP_DUP_TOP_VALUE: frame->push(frame->top_value(this)); continue;
case OP_UNARY_STAR: {
@ -232,16 +229,16 @@ PyVar VM::run_frame(Frame* frame){
Args kwargs = frame->pop_n_values_reversed(this, KWARGC*2);
Args args = frame->pop_n_values_reversed(this, ARGC);
if(byte.op == OP_CALL_KWARGS_UNPACK) unpack_args(args);
PyVar callable = frame->pop_value(this);
PyVar ret = call(callable, std::move(args), kwargs, true);
PyObject* callable = frame->pop_value(this);
PyObject* ret = call(callable, std::move(args), kwargs, true);
if(ret == _py_op_call) return ret;
frame->push(std::move(ret));
} continue;
case OP_CALL_UNPACK: case OP_CALL: {
Args args = frame->pop_n_values_reversed(this, byte.arg);
if(byte.op == OP_CALL_UNPACK) unpack_args(args);
PyVar callable = frame->pop_value(this);
PyVar ret = call(callable, std::move(args), no_arg(), true);
PyObject* callable = frame->pop_value(this);
PyObject* ret = call(callable, std::move(args), no_arg(), true);
if(ret == _py_op_call) return ret;
frame->push(std::move(ret));
} continue;
@ -254,15 +251,15 @@ PyVar VM::run_frame(Frame* frame){
frame->jump_abs_safe(it->second);
} continue;
case OP_GET_ITER: {
PyVar obj = frame->pop_value(this);
PyVar iter = asIter(obj);
PyObject* obj = frame->pop_value(this);
PyObject* iter = asIter(obj);
check_type(frame->top(), tp_ref);
PyIter_AS_C(iter)->loop_var = frame->pop();
frame->push(std::move(iter));
} continue;
case OP_FOR_ITER: {
BaseIter* it = PyIter_AS_C(frame->top());
PyVar obj = it->next();
PyObject* obj = it->next();
if(obj != nullptr){
PyRef_AS_C(it->loop_var)->set(this, frame, std::move(obj));
}else{
@ -279,18 +276,18 @@ PyVar VM::run_frame(Frame* frame){
frame->jump_abs_safe(blockEnd);
} continue;
case OP_JUMP_IF_FALSE_OR_POP: {
const PyVar expr = frame->top_value(this);
PyObject* expr = frame->top_value(this);
if(asBool(expr)==False) frame->jump_abs(byte.arg);
else frame->pop_value(this);
} continue;
case OP_JUMP_IF_TRUE_OR_POP: {
const PyVar expr = frame->top_value(this);
PyObject* expr = frame->top_value(this);
if(asBool(expr)==True) frame->jump_abs(byte.arg);
else frame->pop_value(this);
} continue;
case OP_BUILD_SLICE: {
PyVar stop = frame->pop_value(this);
PyVar start = frame->pop_value(this);
PyObject* stop = frame->pop_value(this);
PyObject* start = frame->pop_value(this);
Slice s;
if(start != None) { s.start = CAST(int, start);}
if(stop != None) { s.stop = CAST(int, stop);}
@ -298,7 +295,7 @@ PyVar VM::run_frame(Frame* frame){
} continue;
case OP_IMPORT_NAME: {
StrName name = frame->co->names[byte.arg].first;
PyVar* ext_mod = _modules.try_get(name);
PyObject** ext_mod = _modules.try_get(name);
if(ext_mod == nullptr){
Str source;
auto it2 = _lazy_modules.find(name);
@ -311,7 +308,7 @@ PyVar VM::run_frame(Frame* frame){
_lazy_modules.erase(it2);
}
CodeObject_ code = compile(source, name.str(), EXEC_MODE);
PyVar new_mod = new_module(name);
PyObject* new_mod = new_module(name);
_exec(code, new_mod);
frame->push(new_mod);
new_mod->attr()._try_perfect_rehash();
@ -320,7 +317,7 @@ PyVar VM::run_frame(Frame* frame){
}
} continue;
case OP_STORE_ALL_NAMES: {
PyVar obj = frame->pop_value(this);
PyObject* obj = frame->pop_value(this);
for(auto& [name, value]: obj->attr().items()){
Str s = name.str();
if(s.empty() || s[0] == '_') continue;

View File

@ -14,7 +14,7 @@ struct NativeProxyFunc {
_Fp func;
NativeProxyFunc(_Fp func) : func(func) {}
PyVar operator()(VM* vm, Args& args) {
PyObject* operator()(VM* vm, Args& args) {
if (args.size() != N) {
vm->TypeError("expected " + std::to_string(N) + " arguments, but got " + std::to_string(args.size()));
}
@ -22,13 +22,13 @@ struct NativeProxyFunc {
}
template<typename __Ret, size_t... Is>
std::enable_if_t<std::is_void_v<__Ret>, PyVar> call(VM* vm, Args& args, std::index_sequence<Is...>) {
std::enable_if_t<std::is_void_v<__Ret>, PyObject*> call(VM* vm, Args& args, std::index_sequence<Is...>) {
func(py_cast<Params>(vm, args[Is])...);
return vm->None;
}
template<typename __Ret, size_t... Is>
std::enable_if_t<!std::is_void_v<__Ret>, PyVar> call(VM* vm, Args& args, std::index_sequence<Is...>) {
std::enable_if_t<!std::is_void_v<__Ret>, PyObject*> call(VM* vm, Args& args, std::index_sequence<Is...>) {
__Ret ret = func(py_cast<Params>(vm, args[Is])...);
return VAR(std::move(ret));
}
@ -41,7 +41,7 @@ struct NativeProxyMethod {
_Fp func;
NativeProxyMethod(_Fp func) : func(func) {}
PyVar operator()(VM* vm, Args& args) {
PyObject* operator()(VM* vm, Args& args) {
int actual_size = args.size() - 1;
if (actual_size != N) {
vm->TypeError("expected " + std::to_string(N) + " arguments, but got " + std::to_string(actual_size));
@ -50,14 +50,14 @@ struct NativeProxyMethod {
}
template<typename __Ret, size_t... Is>
std::enable_if_t<std::is_void_v<__Ret>, PyVar> call(VM* vm, Args& args, std::index_sequence<Is...>) {
std::enable_if_t<std::is_void_v<__Ret>, PyObject*> call(VM* vm, Args& args, std::index_sequence<Is...>) {
T& self = py_cast<T&>(vm, args[0]);
(self.*func)(py_cast<Params>(vm, args[Is+1])...);
return vm->None;
}
template<typename __Ret, size_t... Is>
std::enable_if_t<!std::is_void_v<__Ret>, PyVar> call(VM* vm, Args& args, std::index_sequence<Is...>) {
std::enable_if_t<!std::is_void_v<__Ret>, PyObject*> call(VM* vm, Args& args, std::index_sequence<Is...>) {
T& self = py_cast<T&>(vm, args[0]);
__Ret ret = (self.*func)(py_cast<Params>(vm, args[Is+1])...);
return VAR(std::move(ret));
@ -200,7 +200,7 @@ struct Pointer{
return Pointer(ctype, level, ptr-offset*unit_size());
}
static void _register(VM* vm, PyVar mod, PyVar type){
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_static_method<-1>(type, "__new__", CPP_NOT_IMPLEMENTED());
vm->bind_method<0>(type, "__repr__", [](VM* vm, Args& args) {
@ -268,7 +268,7 @@ struct Pointer{
template<typename T>
inline T& ref() noexcept { return *reinterpret_cast<T*>(ptr); }
PyVar get(VM* vm){
PyObject* get(VM* vm){
if(level > 1) return VAR_T(Pointer, ctype, level-1, ref<char*>());
switch(ctype->index){
#define CASE(T) case type_index<T>(): return VAR(ref<T>())
@ -291,7 +291,7 @@ struct Pointer{
return VAR_T(Pointer, *this);
}
void set(VM* vm, const PyVar& val){
void set(VM* vm, PyObject* val){
if(level > 1) {
Pointer& p = CAST(Pointer&, val);
ref<char*>() = p.ptr; // We don't check the type, just copy the underlying address
@ -359,7 +359,7 @@ struct Value {
Value& operator=(const Value& other) = delete;
Value(const Value& other) = delete;
static void _register(VM* vm, PyVar mod, PyVar type){
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_static_method<-1>(type, "__new__", CPP_NOT_IMPLEMENTED());
vm->bind_method<0>(type, "ptr", [](VM* vm, Args& args) {
@ -388,7 +388,7 @@ struct CType{
CType() : type(_type_db.get<void>()) {}
CType(const TypeInfo* type) : type(type) {}
static void _register(VM* vm, PyVar mod, PyVar type){
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_static_method<1>(type, "__new__", [](VM* vm, Args& args) {
const Str& name = CAST(Str&, args[0]);
const TypeInfo* type = _type_db.get(name);
@ -404,8 +404,8 @@ struct CType{
};
void add_module_c(VM* vm){
PyVar mod = vm->new_module("c");
PyVar ptr_t = Pointer::register_class(vm, mod);
PyObject* mod = vm->new_module("c");
PyObject* ptr_t = Pointer::register_class(vm, mod);
Value::register_class(vm, mod);
CType::register_class(vm, mod);
@ -462,11 +462,11 @@ void add_module_c(VM* vm){
});
}
PyVar py_var(VM* vm, void* p){
PyObject* py_var(VM* vm, void* p){
return VAR_T(Pointer, _type_db.get<void>(), (char*)p);
}
PyVar py_var(VM* vm, char* p){
PyObject* py_var(VM* vm, char* p){
return VAR_T(Pointer, _type_db.get<char>(), (char*)p);
}
@ -491,7 +491,7 @@ struct pointer {
};
template<typename T>
T py_pointer_cast(VM* vm, const PyVar& var){
T py_pointer_cast(VM* vm, PyObject* var){
static_assert(std::is_pointer_v<T>);
Pointer& p = CAST(Pointer&, var);
const TypeInfo* type = _type_db.get<typename pointer<T>::baseT>();
@ -503,14 +503,14 @@ T py_pointer_cast(VM* vm, const PyVar& var){
}
template<typename T>
T py_value_cast(VM* vm, const PyVar& var){
T py_value_cast(VM* vm, PyObject* var){
static_assert(std::is_pod_v<T>);
Value& v = CAST(Value&, var);
return *reinterpret_cast<T*>(v.data);
}
template<typename T>
std::enable_if_t<std::is_pointer_v<std::decay_t<T>>, PyVar>
std::enable_if_t<std::is_pointer_v<std::decay_t<T>>, PyObject*>
py_var(VM* vm, T p){
const TypeInfo* type = _type_db.get<typename pointer<T>::baseT>();
if(type == nullptr) type = _type_db.get<void>();
@ -518,9 +518,9 @@ py_var(VM* vm, T p){
}
template<typename T>
std::enable_if_t<!std::is_pointer_v<std::decay_t<T>>, PyVar>
std::enable_if_t<!std::is_pointer_v<std::decay_t<T>>, PyObject*>
py_var(VM* vm, T p){
if constexpr(std::is_same_v<T, PyVar>) return p;
if constexpr(std::is_same_v<T, PyObject*>) return p;
const TypeInfo* type = _type_db.get<T>();
return VAR_T(Value, type, &p);
}

View File

@ -94,7 +94,7 @@ struct CodeObject {
return names.size() - 1;
}
int add_const(PyVar v){
int add_const(PyObject* v){
consts.push_back(v);
return consts.size() - 1;
}

View File

@ -57,7 +57,6 @@ namespace std = ::std;
struct Dummy { };
struct DummyInstance { };
struct DummyModule { };
#define DUMMY_VAL Dummy()
struct Type {
int index;
@ -85,4 +84,24 @@ struct Type {
const float kLocalsLoadFactor = 0.67f;
const float kInstAttrLoadFactor = 0.67f;
const float kTypeAttrLoadFactor = 0.5f;
static_assert(sizeof(i64) == sizeof(int*));
static_assert(sizeof(f64) == sizeof(int*));
static_assert(std::numeric_limits<float>::is_iec559);
static_assert(std::numeric_limits<double>::is_iec559);
struct PyObject;
#define BITS(p) (reinterpret_cast<i64>(p))
inline bool is_tagged(PyObject* p) noexcept { return (BITS(p) & 0b11) != 0b00; }
inline bool is_int(PyObject* p) noexcept { return (BITS(p) & 0b11) == 0b01; }
inline bool is_float(PyObject* p) noexcept { return (BITS(p) & 0b11) == 0b10; }
inline bool is_both_int_or_float(PyObject* a, PyObject* b) noexcept {
return is_tagged(a) && is_tagged(b);
}
inline bool is_both_int(PyObject* a, PyObject* b) noexcept {
return is_int(a) && is_int(b);
}
} // namespace pkpy

View File

@ -353,14 +353,14 @@ private:
}
void exprLiteral() {
PyVar value = parser->prev.value;
PyObject* value = parser->prev.value;
int index = co()->add_const(value);
emit(OP_LOAD_CONST, index);
}
void exprFString() {
static const std::regex pattern(R"(\{(.*?)\})");
PyVar value = parser->prev.value;
PyObject* value = parser->prev.value;
Str s = CAST(Str, value);
std::sregex_iterator begin(s.begin(), s.end(), pattern);
std::sregex_iterator end;
@ -1059,7 +1059,7 @@ private:
case 1: func.starred_arg = name; state+=1; break;
case 2: {
consume(TK("="));
PyVarOrNull value = read_literal();
PyObject* value = read_literal();
if(value == nullptr){
SyntaxError(Str("expect a literal, not ") + TK_STR(parser->curr.type));
}
@ -1115,10 +1115,10 @@ private:
}
}
PyVarOrNull read_literal(){
PyObject* read_literal(){
if(match(TK("-"))){
consume(TK("@num"));
PyVar val = parser->prev.value;
PyObject* val = parser->prev.value;
return vm->num_negated(val);
}
if(match(TK("@num"))) return parser->prev.value;
@ -1166,7 +1166,7 @@ public:
code->optimize(vm);
return code;
}else if(mode()==JSON_MODE){
PyVarOrNull value = read_literal();
PyObject* value = read_literal();
if(value != nullptr) emit(OP_LOAD_CONST, code->add_const(value));
else if(match(TK("{"))) exprMap();
else if(match(TK("["))) exprList();

View File

@ -7,27 +7,27 @@ namespace pkpy{
static THREAD_LOCAL uint64_t kFrameGlobalId = 0;
struct Frame {
std::vector<PyVar> _data;
std::vector<PyObject*> _data;
int _ip = -1;
int _next_ip = 0;
const CodeObject* co;
PyVar _module;
PyObject* _module;
NameDict_ _locals;
NameDict_ _closure;
const uint64_t id;
std::vector<std::pair<int, std::vector<PyVar>>> s_try_block;
std::vector<std::pair<int, std::vector<PyObject*>>> s_try_block;
inline NameDict& f_locals() noexcept { return _locals != nullptr ? *_locals : _module->attr(); }
inline NameDict& f_globals() noexcept { return _module->attr(); }
inline PyVar* f_closure_try_get(StrName name) noexcept {
inline PyObject** f_closure_try_get(StrName name) noexcept {
if(_closure == nullptr) return nullptr;
return _closure->try_get(name);
}
Frame(const CodeObject_& co,
const PyVar& _module,
PyObject* _module,
const NameDict_& _locals=nullptr,
const NameDict_& _closure=nullptr)
: co(co.get()), _module(_module), _locals(_locals), _closure(_closure), id(kFrameGlobalId++) { }
@ -57,11 +57,11 @@ struct Frame {
return _next_ip < co->codes.size();
}
inline PyVar pop(){
inline PyObject* pop(){
#if PK_EXTRA_CHECK
if(_data.empty()) throw std::runtime_error("_data.empty() is true");
#endif
PyVar v = std::move(_data.back());
PyObject* v = _data.back();
_data.pop_back();
return v;
}
@ -73,28 +73,28 @@ struct Frame {
_data.pop_back();
}
inline void try_deref(VM*, PyVar&);
inline void try_deref(VM*, PyObject*&);
inline PyVar pop_value(VM* vm){
PyVar value = pop();
inline PyObject* pop_value(VM* vm){
PyObject* value = pop();
try_deref(vm, value);
return value;
}
inline PyVar top_value(VM* vm){
PyVar value = top();
inline PyObject* top_value(VM* vm){
PyObject* value = top();
try_deref(vm, value);
return value;
}
inline PyVar& top(){
inline PyObject*& top(){
#if PK_EXTRA_CHECK
if(_data.empty()) throw std::runtime_error("_data.empty() is true");
#endif
return _data.back();
}
inline PyVar& top_1(){
inline PyObject*& top_1(){
#if PK_EXTRA_CHECK
if(_data.size() < 2) throw std::runtime_error("_data.size() < 2");
#endif
@ -117,7 +117,7 @@ struct Frame {
bool jump_to_exception_handler(){
if(s_try_block.empty()) return false;
PyVar obj = pop();
PyObject* obj = pop();
auto& p = s_try_block.back();
_data = std::move(p.second);
_data.push_back(obj);

View File

@ -3,49 +3,38 @@
#include "obj.h"
namespace pkpy {
using PyVar0 = PyObject*;
struct ManagedHeap{
std::vector<PyObject*> heap;
// a generational mark and sweep garbage collector
struct GC{
using Generation = std::vector<PyVar0>;
static const int kTotalGen = 3;
Generation gen[kTotalGen];
void add(PyVar0 obj){
if(!obj->need_gc) return;
gen[0].push_back(obj);
template<typename T>
PyObject* gcnew(Type type, T&& val){
PyObject* obj = new Py_<std::decay_t<T>>(type, std::forward<T>(val));
obj->gc.enabled = true;
heap.push_back(obj);
return obj;
}
void sweep(int index){
Generation& g = gen[index];
if(index < kTotalGen-1){
for(int i=0; i<g.size(); i++){
if(g[i]->marked){
g[i]->marked = false;
gen[index+1].push_back(g[i]);
void sweep(){
std::vector<PyObject*> alive;
for(PyObject* obj: heap){
if(obj->gc.marked){
obj->gc.marked = false;
alive.push_back(obj);
}else{
delete g[i];
delete obj;
}
}
g.clear();
}else{
Generation alive;
// the oldest generation
for(int i=0; i<g.size(); i++){
if(g[i]->marked){
g[i]->marked = false;
alive.push_back(g[i]);
}else{
delete g[i];
}
}
g = std::move(alive);
}
heap.clear();
heap.swap(alive);
}
void collect(int index){
sweep(index);
void collect(VM* vm){
std::vector<PyObject*> roots = get_roots(vm);
for(PyObject* obj: roots) obj->mark();
sweep();
}
std::vector<PyObject*> get_roots(VM* vm);
};
} // namespace pkpy

View File

@ -42,7 +42,7 @@ struct FileIO {
if(!_fs.is_open()) vm->IOError(strerror(errno));
}
static void _register(VM* vm, PyVar mod, PyVar type){
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_static_method<2>(type, "__new__", [](VM* vm, Args& args){
return VAR_T(FileIO,
vm, CAST(Str, args[0]), CAST(Str, args[1])
@ -79,15 +79,15 @@ struct FileIO {
};
void add_module_io(VM* vm){
PyVar mod = vm->new_module("io");
PyVar type = FileIO::register_class(vm, mod);
PyObject* mod = vm->new_module("io");
PyObject* type = FileIO::register_class(vm, mod);
vm->bind_builtin_func<2>("open", [type](VM* vm, const Args& args){
return vm->call(type, args);
});
}
void add_module_os(VM* vm){
PyVar mod = vm->new_module("os");
PyObject* mod = vm->new_module("os");
// Working directory is shared by all VMs!!
vm->bind_func<0>(mod, "getcwd", [](VM* vm, const Args& args){
return VAR(std::filesystem::current_path().string());

View File

@ -8,7 +8,7 @@ class RangeIter : public BaseIter {
i64 current;
Range r;
public:
RangeIter(VM* vm, PyVar _ref) : BaseIter(vm, _ref) {
RangeIter(VM* vm, PyObject* _ref) : BaseIter(vm, _ref) {
this->r = OBJ_GET(Range, _ref);
this->current = r.start;
}
@ -17,7 +17,7 @@ public:
return r.step > 0 ? current < r.stop : current > r.stop;
}
PyVar next(){
PyObject* next(){
if(!_has_next()) return nullptr;
current += r.step;
return VAR(current-r.step);
@ -29,8 +29,8 @@ class ArrayIter : public BaseIter {
size_t index = 0;
const T* p;
public:
ArrayIter(VM* vm, PyVar _ref) : BaseIter(vm, _ref) { p = &OBJ_GET(T, _ref);}
PyVar next(){
ArrayIter(VM* vm, PyObject* _ref) : BaseIter(vm, _ref) { p = &OBJ_GET(T, _ref);}
PyObject* next(){
if(index == p->size()) return nullptr;
return p->operator[](index++);
}
@ -40,20 +40,20 @@ class StringIter : public BaseIter {
int index = 0;
Str* str;
public:
StringIter(VM* vm, PyVar _ref) : BaseIter(vm, _ref) {
StringIter(VM* vm, PyObject* _ref) : BaseIter(vm, _ref) {
str = &OBJ_GET(Str, _ref);
}
PyVar next() {
PyObject* next() {
if(index == str->u8_length()) return nullptr;
return VAR(str->u8_getitem(index++));
}
};
PyVar Generator::next(){
PyObject* Generator::next(){
if(state == 2) return nullptr;
vm->callstack.push(std::move(frame));
PyVar ret = vm->_exec();
PyObject* ret = vm->_exec();
if(ret == vm->_py_op_yield){
frame = std::move(vm->callstack.top());
vm->callstack.pop();

View File

@ -75,7 +75,7 @@ int main(int argc, char** argv){
// set parent path as cwd
std::filesystem::current_path(filepath.parent_path());
pkpy::PyVarOrNull ret = nullptr;
pkpy::PyObject* ret = nullptr;
ret = vm->exec(src.c_str(), argv_1, pkpy::EXEC_MODE);
pkpy_delete(vm);
return ret != nullptr ? 0 : 1;

View File

@ -4,31 +4,12 @@
namespace pkpy{
struct PyObject;
template<typename T>
struct SpAllocator {
template<typename U>
inline static int* alloc(){
return (int*)malloc(sizeof(int) + sizeof(U));
}
inline static void dealloc(int* counter){
((T*)(counter + 1))->~T();
free(counter);
}
};
template <typename T>
struct shared_ptr {
union {
int* counter;
i64 bits;
};
#define _t() (T*)(counter + 1)
#define _inc_counter() if(!is_tagged() && counter) ++(*counter)
#define _dec_counter() if(!is_tagged() && counter && --(*counter) == 0) SpAllocator<T>::dealloc(counter)
#define _inc_counter() if(counter) ++(*counter)
#define _dec_counter() if(counter && --(*counter) == 0) {((T*)(counter + 1))->~T(); free(counter);}
public:
shared_ptr() : counter(nullptr) {}
@ -69,7 +50,6 @@ public:
T* get() const { return _t(); }
int use_count() const {
if(is_tagged()) return 0;
return counter ? *counter : 0;
}
@ -77,44 +57,20 @@ public:
_dec_counter();
counter = nullptr;
}
inline constexpr bool is_tagged() const {
if constexpr(!std::is_same_v<T, PyObject>) return false;
return (bits & 0b11) != 0b00;
}
inline bool is_tag_00() const { return (bits & 0b11) == 0b00; }
inline bool is_tag_01() const { return (bits & 0b11) == 0b01; }
inline bool is_tag_10() const { return (bits & 0b11) == 0b10; }
inline bool is_tag_11() const { return (bits & 0b11) == 0b11; }
};
#undef _t
#undef _inc_counter
#undef _dec_counter
template <typename T, typename U, typename... Args>
shared_ptr<T> make_sp(Args&&... args) {
static_assert(std::is_base_of_v<T, U>, "U must be derived from T");
static_assert(std::has_virtual_destructor_v<T>, "T must have virtual destructor");
static_assert(!std::is_same_v<T, PyObject> || (!std::is_same_v<U, i64> && !std::is_same_v<U, f64>));
int* p = SpAllocator<T>::template alloc<U>(); *p = 1;
new(p+1) U(std::forward<Args>(args)...);
return shared_ptr<T>(p);
}
template <typename T, typename... Args>
shared_ptr<T> make_sp(Args&&... args) {
int* p = SpAllocator<T>::template alloc<T>(); *p = 1;
int* p = (int*)malloc(sizeof(int) + sizeof(T));
*p = 1;
new(p+1) T(std::forward<Args>(args)...);
return shared_ptr<T>(p);
}
static_assert(sizeof(i64) == sizeof(int*));
static_assert(sizeof(f64) == sizeof(int*));
static_assert(sizeof(shared_ptr<PyObject>) == sizeof(int*));
static_assert(std::numeric_limits<float>::is_iec559);
static_assert(std::numeric_limits<double>::is_iec559);
template<typename T, int __Bucket, int __BucketSize=32>
struct SmallArrayPool {
std::vector<T*> buckets[__Bucket+1];
@ -145,10 +101,4 @@ struct SmallArrayPool {
}
}
};
typedef shared_ptr<PyObject> PyVar;
typedef PyVar PyVarOrNull;
typedef PyVar PyVarRef;
}; // namespace pkpy

View File

@ -6,7 +6,7 @@
namespace pkpy{
const int kNameDictNodeSize = sizeof(StrName) + sizeof(PyVar);
const int kNameDictNodeSize = sizeof(StrName) + sizeof(PyObject*);
template<int __Bucket, int __BucketSize=32>
struct DictArrayPool {
@ -26,9 +26,7 @@ struct DictArrayPool {
}
void dealloc(StrName* head, uint16_t n){
PyVar* _values = (PyVar*)(head + n);
if(n > __Bucket || buckets[n].size() >= __BucketSize){
for(int i=0; i<n; i++) _values[i].~PyVar();
free(head);
}else{
buckets[n].push_back(head);
@ -75,12 +73,12 @@ struct NameDict {
uint16_t _mask;
StrName* _keys;
inline PyVar& value(uint16_t i){
return reinterpret_cast<PyVar*>(_keys + _capacity)[i];
inline PyObject*& value(uint16_t i){
return reinterpret_cast<PyObject**>(_keys + _capacity)[i];
}
inline const PyVar& value(uint16_t i) const {
return reinterpret_cast<const PyVar*>(_keys + _capacity)[i];
inline PyObject* value(uint16_t i) const {
return reinterpret_cast<PyObject**>(_keys + _capacity)[i];
}
NameDict(uint16_t capacity=2, float load_factor=0.67, uint16_t hash_seed=kHashSeeds[0]):
@ -123,19 +121,19 @@ while(!_keys[i].empty()) { \
i = (i + 1) & _mask; \
}
const PyVar& operator[](StrName key) const {
PyObject* operator[](StrName key) const {
bool ok; uint16_t i;
HASH_PROBE(key, ok, i);
if(!ok) throw std::out_of_range("NameDict key not found: " + key.str());
return value(i);
}
PyVar& get(StrName key){
bool ok; uint16_t i;
HASH_PROBE(key, ok, i);
if(!ok) throw std::out_of_range("NameDict key not found: " + key.str());
return value(i);
}
// PyObject*& get(StrName key){
// bool ok; uint16_t i;
// HASH_PROBE(key, ok, i);
// if(!ok) throw std::out_of_range("NameDict key not found: " + key.str());
// return value(i);
// }
template<typename T>
void set(StrName key, T&& val){
@ -154,7 +152,7 @@ while(!_keys[i].empty()) { \
void _rehash(bool resize){
StrName* old_keys = _keys;
PyVar* old_values = &value(0);
PyObject** old_values = &value(0);
uint16_t old_capacity = _capacity;
if(resize){
_capacity = find_next_capacity(_capacity * 2);
@ -177,18 +175,18 @@ while(!_keys[i].empty()) { \
_rehash(false); // do not resize
}
inline PyVar* try_get(StrName key){
inline PyObject** try_get(StrName key){
bool ok; uint16_t i;
HASH_PROBE(key, ok, i);
if(!ok) return nullptr;
return &value(i);
}
inline bool try_set(StrName key, PyVar&& val){
inline bool try_set(StrName key, PyObject* val){
bool ok; uint16_t i;
HASH_PROBE(key, ok, i);
if(!ok) return false;
value(i) = std::move(val);
value(i) = val;
return true;
}
@ -213,8 +211,8 @@ while(!_keys[i].empty()) { \
_size--;
}
std::vector<std::pair<StrName, PyVar>> items() const {
std::vector<std::pair<StrName, PyVar>> v;
std::vector<std::pair<StrName, PyObject*>> items() const {
std::vector<std::pair<StrName, PyObject*>> v;
for(uint16_t i=0; i<_capacity; i++){
if(_keys[i].empty()) continue;
v.push_back(std::make_pair(_keys[i], value(i)));
@ -231,7 +229,7 @@ while(!_keys[i].empty()) { \
return v;
}
void apply_v(void(*f)(PyVar)) {
void apply_v(void(*f)(PyObject*)) {
for(uint16_t i=0; i<_capacity; i++){
if(_keys[i].empty()) continue;
f(value(i));

View File

@ -12,7 +12,7 @@ struct Frame;
struct BaseRef;
class VM;
typedef std::function<PyVar(VM*, Args&)> NativeFuncRaw;
typedef std::function<PyObject*(VM*, Args&)> NativeFuncRaw;
typedef shared_ptr<CodeObject> CodeObject_;
typedef shared_ptr<NameDict> NameDict_;
@ -22,7 +22,7 @@ struct NativeFunc {
bool method;
NativeFunc(NativeFuncRaw f, int argc, bool method) : f(f), argc(argc), method(method) {}
inline PyVar operator()(VM* vm, Args& args) const;
inline PyObject* operator()(VM* vm, Args& args) const;
};
struct Function {
@ -34,7 +34,7 @@ struct Function {
std::vector<StrName> kwargs_order;
// runtime settings
PyVar _module = nullptr;
PyObject* _module = nullptr;
NameDict_ _closure = nullptr;
bool has_name(StrName val) const {
@ -46,9 +46,9 @@ struct Function {
};
struct BoundMethod {
PyVar obj;
PyVar method;
BoundMethod(const PyVar& obj, const PyVar& method) : obj(obj), method(method) {}
PyObject* obj;
PyObject* method;
BoundMethod(PyObject* obj, PyObject* method) : obj(obj), method(method) {}
};
struct Range {
@ -58,9 +58,9 @@ struct Range {
};
struct StarWrapper {
PyVar obj;
PyObject* obj;
bool rvalue;
StarWrapper(const PyVar& obj, bool rvalue): obj(obj), rvalue(rvalue) {}
StarWrapper(PyObject* obj, bool rvalue): obj(obj), rvalue(rvalue) {}
};
struct Slice {
@ -79,30 +79,34 @@ struct Slice {
class BaseIter {
protected:
VM* vm;
PyVar _ref; // keep a reference to the object so it will not be deleted while iterating
PyObject* _ref; // keep a reference to the object so it will not be deleted while iterating
public:
virtual PyVar next() = 0;
PyVarRef loop_var;
BaseIter(VM* vm, PyVar _ref) : vm(vm), _ref(_ref) {}
virtual PyObject* next() = 0;
PyObject* loop_var;
BaseIter(VM* vm, PyObject* _ref) : vm(vm), _ref(_ref) {}
virtual ~BaseIter() = default;
};
struct GCHeader {
bool enabled; // whether this object is managed by GC
bool marked; // whether this object is marked
GCHeader() : enabled(false), marked(false) {}
};
struct PyObject {
bool need_gc;
bool marked;
/**********/
GCHeader gc;
Type type;
NameDict* _attr;
inline bool is_attr_valid() const noexcept { return _attr != nullptr; }
inline NameDict& attr() noexcept { return *_attr; }
inline const PyVar& attr(StrName name) const noexcept { return _attr->get(name); }
inline PyObject* attr(StrName name) const noexcept { return (*_attr)[name]; }
virtual void* value() = 0;
virtual void mark() {
if(!need_gc || marked) return;
marked = true;
if(is_attr_valid()) attr().apply_v([](PyVar v){ v->mark(); });
if(!gc.enabled || gc.marked) return;
gc.marked = true;
if(is_attr_valid()) attr().apply_v([](PyObject* v){ v->mark(); });
}
PyObject(Type type) : type(type) {}
@ -141,67 +145,51 @@ struct Py_ : PyObject {
const int kTpIntIndex = 2;
const int kTpFloatIndex = 3;
inline bool is_type(const PyVar& obj, Type type) noexcept {
inline bool is_type(PyObject* obj, Type type) noexcept {
switch(type.index){
case kTpIntIndex: return obj.is_tag_01();
case kTpFloatIndex: return obj.is_tag_10();
default: return !obj.is_tagged() && obj->type == type;
case kTpIntIndex: return is_tag_01(obj);
case kTpFloatIndex: return is_tag_10(obj);
default: return !is_tagged(obj) && obj->type == type;
}
}
inline bool is_both_int_or_float(const PyVar& a, const PyVar& b) noexcept {
return a.is_tagged() && b.is_tagged();
}
inline bool is_both_int(const PyVar& a, const PyVar& b) noexcept {
return (a.bits & b.bits & 0b11) == 0b01;
}
inline bool is_int(const PyVar& obj) noexcept {
return obj.is_tag_01();
}
inline bool is_float(const PyVar& obj) noexcept {
return obj.is_tag_10();
}
#define PY_CLASS(T, mod, name) \
static Type _type(VM* vm) { \
static const StrName __x0(#mod); \
static const StrName __x1(#name); \
return OBJ_GET(Type, vm->_modules[__x0]->attr(__x1)); \
} \
static PyVar register_class(VM* vm, PyVar mod) { \
PyVar type = vm->new_type_object(mod, #name, vm->tp_object); \
static PyObject* register_class(VM* vm, PyObject* mod) { \
PyObject* type = vm->new_type_object(mod, #name, vm->tp_object); \
if(OBJ_NAME(mod) != #mod) UNREACHABLE(); \
T::_register(vm, mod, type); \
type->attr()._try_perfect_rehash(); \
return type; \
}
union __8B {
union BitsCvt {
i64 _int;
f64 _float;
__8B(i64 val) : _int(val) {}
__8B(f64 val) : _float(val) {}
BitsCvt(i64 val) : _int(val) {}
BitsCvt(f64 val) : _float(val) {}
};
template <typename, typename = void> struct is_py_class : std::false_type {};
template <typename T> struct is_py_class<T, std::void_t<decltype(T::_type)>> : std::true_type {};
template<typename T>
void _check_py_class(VM* vm, const PyVar& var);
void _check_py_class(VM* vm, PyObject* var);
template<typename T>
T py_pointer_cast(VM* vm, const PyVar& var);
T py_pointer_cast(VM* vm, PyObject* var);
template<typename T>
T py_value_cast(VM* vm, const PyVar& var);
T py_value_cast(VM* vm, PyObject* var);
struct Discarded {};
template<typename __T>
__T py_cast(VM* vm, const PyVar& obj) {
__T py_cast(VM* vm, PyObject* obj) {
using T = std::decay_t<__T>;
if constexpr(std::is_pointer_v<T>){
return py_pointer_cast<T>(vm, obj);
@ -216,7 +204,7 @@ __T py_cast(VM* vm, const PyVar& obj) {
}
template<typename __T>
__T _py_cast(VM* vm, const PyVar& obj) {
__T _py_cast(VM* vm, PyObject* obj) {
using T = std::decay_t<__T>;
if constexpr(std::is_pointer_v<__T>){
return py_pointer_cast<__T>(vm, obj);
@ -228,7 +216,7 @@ __T _py_cast(VM* vm, const PyVar& obj) {
}
#define VAR(x) py_var(vm, x)
#define VAR_T(T, ...) vm->new_object(T::_type(vm), T(__VA_ARGS__))
#define VAR_T(T, ...) vm->heap.gcnew<T>(T::_type(vm), T(__VA_ARGS__))
#define CAST(T, x) py_cast<T>(vm, x)
#define _CAST(T, x) _py_cast<T>(vm, x)

View File

@ -54,7 +54,7 @@ struct Token{
const char* start;
int length;
int line;
PyVar value;
PyObject* value;
Str str() const { return Str(start, length);}
@ -271,7 +271,7 @@ struct Parser {
return true;
}
void set_next_token(TokenIndex type, PyVar value=nullptr) {
void set_next_token(TokenIndex type, PyObject* value=nullptr) {
switch(type){
case TK("{"): case TK("["): case TK("("): brackets_level++; break;
case TK(")"): case TK("]"): case TK("}"): brackets_level--; break;

View File

@ -69,7 +69,7 @@ void init_builtins(VM* _vm) {
vm->TypeError("super(type, obj): obj must be an instance or subtype of type");
}
Type base = vm->_all_types[type.index].base;
return vm->new_object(vm->tp_super, Super(args[1], base));
return vm->heap.gcnew(vm->tp_super, Super(args[1], base));
});
_vm->bind_builtin_func<2>("isinstance", [](VM* vm, Args& args) {
@ -79,16 +79,16 @@ void init_builtins(VM* _vm) {
});
_vm->bind_builtin_func<1>("id", [](VM* vm, Args& args) {
const PyVar& obj = args[0];
if(obj.is_tagged()) return VAR((i64)0);
return VAR(obj.bits);
PyObject* obj = args[0];
if(is_tagged(obj)) return VAR((i64)0);
return VAR(BITS(obj));
});
_vm->bind_builtin_func<2>("divmod", [](VM* vm, Args& args) {
i64 lhs = CAST(i64, args[0]);
i64 rhs = CAST(i64, args[1]);
if(rhs == 0) vm->ZeroDivisionError();
return VAR(two_args(VAR(lhs/rhs), VAR(lhs%rhs)));
return VAR(Tuple{VAR(lhs/rhs), VAR(lhs%rhs)});
});
_vm->bind_builtin_func<1>("eval", [](VM* vm, Args& args) {
@ -169,8 +169,8 @@ void init_builtins(VM* _vm) {
});
_vm->bind_method<0>("object", "__repr__", [](VM* vm, Args& args) {
PyVar self = args[0];
std::uintptr_t addr = self.is_tagged() ? 0 : (uintptr_t)self.get();
PyObject* self = args[0];
std::uintptr_t addr = is_tagged(self) ? 0 : (uintptr_t)self;
StrStream ss;
ss << std::hex << addr;
Str s = "<" + OBJ_NAME(vm->_t(self)) + " object at 0x" + ss.str() + ">";
@ -405,7 +405,7 @@ void init_builtins(VM* _vm) {
_vm->bind_method<1>("str", "join", [](VM* vm, Args& args) {
const Str& self = CAST(Str&, args[0]);
StrStream ss;
PyVar obj = vm->asList(args[1]);
PyObject* obj = vm->asList(args[1]);
const List& list = CAST(List&, obj);
for (int i = 0; i < list.size(); ++i) {
if (i > 0) ss << self;
@ -423,7 +423,7 @@ void init_builtins(VM* _vm) {
_vm->bind_method<1>("list", "extend", [](VM* vm, Args& args) {
List& self = CAST(List&, args[0]);
PyVar obj = vm->asList(args[1]);
PyObject* obj = vm->asList(args[1]);
const List& list = CAST(List&, obj);
self.insert(self.end(), list.begin(), list.end());
return vm->None;
@ -575,7 +575,7 @@ void init_builtins(VM* _vm) {
#endif
void add_module_time(VM* vm){
PyVar mod = vm->new_module("time");
PyObject* mod = vm->new_module("time");
vm->bind_func<0>(mod, "time", [](VM* vm, Args& args) {
auto now = std::chrono::high_resolution_clock::now();
return VAR(std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count() / 1000000.0);
@ -583,7 +583,7 @@ void add_module_time(VM* vm){
}
void add_module_sys(VM* vm){
PyVar mod = vm->new_module("sys");
PyObject* mod = vm->new_module("sys");
vm->setattr(mod, "version", VAR(PK_VERSION));
vm->bind_func<1>(mod, "getrefcount", CPP_LAMBDA(VAR(args[0].use_count())));
@ -596,7 +596,7 @@ void add_module_sys(VM* vm){
}
void add_module_json(VM* vm){
PyVar mod = vm->new_module("json");
PyObject* mod = vm->new_module("json");
vm->bind_func<1>(mod, "loads", [](VM* vm, Args& args) {
const Str& expr = CAST(Str&, args[0]);
CodeObject_ code = vm->compile(expr, "<json>", JSON_MODE);
@ -607,7 +607,7 @@ void add_module_json(VM* vm){
}
void add_module_math(VM* vm){
PyVar mod = vm->new_module("math");
PyObject* mod = vm->new_module("math");
vm->setattr(mod, "pi", VAR(3.1415926535897932384));
vm->setattr(mod, "e" , VAR(2.7182818284590452354));
@ -626,9 +626,9 @@ void add_module_math(VM* vm){
}
void add_module_dis(VM* vm){
PyVar mod = vm->new_module("dis");
PyObject* mod = vm->new_module("dis");
vm->bind_func<1>(mod, "dis", [](VM* vm, Args& args) {
PyVar f = args[0];
PyObject* f = args[0];
if(is_type(f, vm->tp_bound_method)) f = CAST(BoundMethod, args[0]).method;
CodeObject_ code = CAST(Function, f).code;
(*vm->_stdout) << vm->disassemble(code);
@ -644,14 +644,14 @@ struct ReMatch {
std::smatch m;
ReMatch(i64 start, i64 end, std::smatch m) : start(start), end(end), m(m) {}
static void _register(VM* vm, PyVar mod, PyVar type){
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_method<-1>(type, "__init__", CPP_NOT_IMPLEMENTED());
vm->bind_method<0>(type, "start", CPP_LAMBDA(VAR(CAST(ReMatch&, args[0]).start)));
vm->bind_method<0>(type, "end", CPP_LAMBDA(VAR(CAST(ReMatch&, args[0]).end)));
vm->bind_method<0>(type, "span", [](VM* vm, Args& args) {
auto& self = CAST(ReMatch&, args[0]);
return VAR(two_args(VAR(self.start), VAR(self.end)));
return VAR(Tuple{VAR(self.start), VAR(self.end)});
});
vm->bind_method<1>(type, "group", [](VM* vm, Args& args) {
@ -663,7 +663,7 @@ struct ReMatch {
}
};
PyVar _regex_search(const Str& pattern, const Str& string, bool fromStart, VM* vm){
PyObject* _regex_search(const Str& pattern, const Str& string, bool fromStart, VM* vm){
std::regex re(pattern);
std::smatch m;
if(std::regex_search(string, m, re)){
@ -676,7 +676,7 @@ PyVar _regex_search(const Str& pattern, const Str& string, bool fromStart, VM* v
};
void add_module_re(VM* vm){
PyVar mod = vm->new_module("re");
PyObject* mod = vm->new_module("re");
ReMatch::register_class(vm, mod);
vm->bind_func<2>(mod, "match", [](VM* vm, Args& args) {
@ -740,7 +740,7 @@ struct Random{
gen.seed(seed);
}
static void _register(VM* vm, PyVar mod, PyVar type){
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_static_method<0>(type, "__new__", CPP_LAMBDA(VAR_T(Random)));
vm->bind_method<1>(type, "seed", native_proxy_callable(&Random::seed));
vm->bind_method<2>(type, "randint", native_proxy_callable(&Random::randint));
@ -750,7 +750,7 @@ struct Random{
};
void add_module_random(VM* vm){
PyVar mod = vm->new_module("random");
PyObject* mod = vm->new_module("random");
Random::register_class(vm, mod);
CodeObject_ code = vm->compile(kPythonLibs["random"], "random.py", EXEC_MODE);
vm->_exec(code, mod);
@ -851,7 +851,7 @@ extern "C" {
/// Return `__repr__` of the result.
/// If the variable is not found, return `nullptr`.
char* pkpy_vm_get_global(pkpy::VM* vm, const char* name){
pkpy::PyVar* val = vm->_main->attr().try_get(name);
pkpy::PyObject** val = vm->_main->attr().try_get(name);
if(val == nullptr) return nullptr;
try{
pkpy::Str repr = pkpy::CAST(pkpy::Str, vm->asRepr(*val));
@ -867,7 +867,7 @@ extern "C" {
/// Return `__repr__` of the result.
/// If there is any error, return `nullptr`.
char* pkpy_vm_eval(pkpy::VM* vm, const char* source){
pkpy::PyVarOrNull ret = vm->exec(source, "<eval>", pkpy::EVAL_MODE);
pkpy::PyObject* ret = vm->exec(source, "<eval>", pkpy::EVAL_MODE);
if(ret == nullptr) return nullptr;
try{
pkpy::Str repr = pkpy::CAST(pkpy::Str, vm->asRepr(ret));
@ -950,13 +950,13 @@ extern "C" {
for(int i=0; mod[i]; i++) if(mod[i] == ' ') return nullptr;
for(int i=0; name[i]; i++) if(name[i] == ' ') return nullptr;
std::string f_header = std::string(mod) + '.' + name + '#' + std::to_string(kGlobalBindId++);
pkpy::PyVar obj = vm->_modules.contains(mod) ? vm->_modules[mod] : vm->new_module(mod);
pkpy::PyObject* obj = vm->_modules.contains(mod) ? vm->_modules[mod] : vm->new_module(mod);
vm->bind_func<-1>(obj, name, [ret_code, f_header](pkpy::VM* vm, const pkpy::Args& args){
pkpy::StrStream ss;
ss << f_header;
for(int i=0; i<args.size(); i++){
ss << ' ';
pkpy::PyVar x = vm->call(args[i], pkpy::__json__);
pkpy::PyObject* x = vm->call(args[i], pkpy::__json__);
ss << pkpy::CAST(pkpy::Str&, x);
}
char* packet = strdup(ss.str().c_str());

View File

@ -6,8 +6,8 @@
namespace pkpy {
struct BaseRef {
virtual PyVar get(VM*, Frame*) const = 0;
virtual void set(VM*, Frame*, PyVar) const = 0;
virtual PyObject* get(VM*, Frame*) const = 0;
virtual void set(VM*, Frame*, PyObject*) const = 0;
virtual void del(VM*, Frame*) const = 0;
virtual ~BaseRef() = default;
};
@ -18,8 +18,8 @@ struct NameRef : BaseRef {
inline NameScope scope() const { return pair.second; }
NameRef(const std::pair<StrName, NameScope>& pair) : pair(pair) {}
PyVar get(VM* vm, Frame* frame) const{
PyVar* val;
PyObject* get(VM* vm, Frame* frame) const{
PyObject** val;
val = frame->f_locals().try_get(name());
if(val != nullptr) return *val;
val = frame->f_closure_try_get(name());
@ -32,12 +32,12 @@ struct NameRef : BaseRef {
return nullptr;
}
void set(VM* vm, Frame* frame, PyVar val) const{
void set(VM* vm, Frame* frame, PyObject* val) const{
switch(scope()) {
case NAME_LOCAL: frame->f_locals().set(name(), std::move(val)); break;
case NAME_LOCAL: frame->f_locals().set(name(), val); break;
case NAME_GLOBAL:
if(frame->f_locals().try_set(name(), std::move(val))) return;
frame->f_globals().set(name(), std::move(val));
if(frame->f_locals().try_set(name(), val)) return;
frame->f_globals().set(name(), val);
break;
default: UNREACHABLE();
}
@ -70,15 +70,15 @@ struct NameRef : BaseRef {
};
struct AttrRef : BaseRef {
mutable PyVar obj;
mutable PyObject* obj;
NameRef attr;
AttrRef(PyVar obj, NameRef attr) : obj(obj), attr(attr) {}
AttrRef(PyObject* obj, NameRef attr) : obj(obj), attr(attr) {}
PyVar get(VM* vm, Frame* frame) const{
PyObject* get(VM* vm, Frame* frame) const{
return vm->getattr(obj, attr.name());
}
void set(VM* vm, Frame* frame, PyVar val) const{
void set(VM* vm, Frame* frame, PyObject* val) const{
vm->setattr(obj, attr.name(), std::move(val));
}
@ -90,22 +90,22 @@ struct AttrRef : BaseRef {
};
struct IndexRef : BaseRef {
mutable PyVar obj;
PyVar index;
IndexRef(PyVar obj, PyVar index) : obj(obj), index(index) {}
mutable PyObject* obj;
PyObject* index;
IndexRef(PyObject* obj, PyObject* index) : obj(obj), index(index) {}
PyVar get(VM* vm, Frame* frame) const{
return vm->fast_call(__getitem__, two_args(obj, index));
PyObject* get(VM* vm, Frame* frame) const{
return vm->fast_call(__getitem__, Args{obj, index});
}
void set(VM* vm, Frame* frame, PyVar val) const{
void set(VM* vm, Frame* frame, PyObject* val) const{
Args args(3);
args[0] = obj; args[1] = index; args[2] = std::move(val);
vm->fast_call(__setitem__, std::move(args));
}
void del(VM* vm, Frame* frame) const{
vm->fast_call(__delitem__, two_args(obj, index));
vm->fast_call(__delitem__, Args{obj, index});
}
};
@ -113,7 +113,7 @@ struct TupleRef : BaseRef {
Tuple objs;
TupleRef(Tuple&& objs) : objs(std::move(objs)) {}
PyVar get(VM* vm, Frame* frame) const{
PyObject* get(VM* vm, Frame* frame) const{
Tuple args(objs.size());
for (int i = 0; i < objs.size(); i++) {
args[i] = vm->PyRef_AS_C(objs[i])->get(vm, frame);
@ -121,11 +121,11 @@ struct TupleRef : BaseRef {
return VAR(std::move(args));
}
void set(VM* vm, Frame* frame, PyVar val) const{
void set(VM* vm, Frame* frame, PyObject* val) const{
val = vm->asIter(val);
BaseIter* iter = vm->PyIter_AS_C(val);
for(int i=0; i<objs.size(); i++){
PyVarOrNull x;
PyObject* x;
if(is_type(objs[i], vm->tp_star_wrapper)){
auto& star = _CAST(StarWrapper&, objs[i]);
if(star.rvalue) vm->ValueError("can't use starred expression here");
@ -141,7 +141,7 @@ struct TupleRef : BaseRef {
vm->PyRef_AS_C(objs[i])->set(vm, frame, x);
}
}
PyVarOrNull x = iter->next();
PyObject* x = iter->next();
if(x != nullptr) vm->ValueError("too many values to unpack");
}
@ -152,19 +152,19 @@ struct TupleRef : BaseRef {
template<typename P>
PyVarRef VM::PyRef(P&& value) {
PyObject* VM::PyRef(P&& value) {
static_assert(std::is_base_of_v<BaseRef, std::decay_t<P>>);
return new_object(tp_ref, std::forward<P>(value));
return heap.gcnew<P>(tp_ref, std::forward<P>(value));
}
const BaseRef* VM::PyRef_AS_C(const PyVar& obj)
const BaseRef* VM::PyRef_AS_C(PyObject* obj)
{
if(!is_type(obj, tp_ref)) TypeError("expected an l-value");
return static_cast<const BaseRef*>(obj->value());
}
/***** Frame's Impl *****/
inline void Frame::try_deref(VM* vm, PyVar& v){
inline void Frame::try_deref(VM* vm, PyObject*& v){
if(is_type(v, vm->tp_ref)) v = vm->PyRef_AS_C(v)->get(vm, this);
}

View File

@ -3,6 +3,7 @@
#include "common.h"
#include "memory.h"
#include "str.h"
#include <initializer_list>
namespace pkpy {
using List = std::vector<PyObject*>;
@ -33,6 +34,11 @@ namespace pkpy {
other._size = 0;
}
Args(std::initializer_list<PyObject*> list) : Args(list.size()){
int i=0;
for(auto& p : list) _args[i++] = p;
}
static pkpy::Args from_list(List&& other) noexcept {
Args ret(other.size());
memcpy((void*)ret._args, (void*)other.data(), sizeof(PyObject*)*ret.size());
@ -82,30 +88,6 @@ namespace pkpy {
return _zero;
}
template<typename T>
Args one_arg(T&& a) {
Args ret(1);
ret[0] = std::forward<T>(a);
return ret;
}
template<typename T1, typename T2>
Args two_args(T1&& a, T2&& b) {
Args ret(2);
ret[0] = std::forward<T1>(a);
ret[1] = std::forward<T2>(b);
return ret;
}
template<typename T1, typename T2, typename T3>
Args three_args(T1&& a, T2&& b, T3&& c) {
Args ret(3);
ret[0] = std::forward<T1>(a);
ret[1] = std::forward<T2>(b);
ret[2] = std::forward<T3>(c);
return ret;
}
typedef Args Tuple;
THREAD_LOCAL SmallArrayPool<PyObject*, 10> Args::_pool;
} // namespace pkpy

131
src/vm.h
View File

@ -1,7 +1,9 @@
#pragma once
#include "common.h"
#include "frame.h"
#include "error.h"
#include "gc.h"
namespace pkpy{
@ -20,8 +22,8 @@ namespace pkpy{
template<> ctype& _py_cast<ctype&>(VM* vm, PyObject* obj) { \
return OBJ_GET(ctype, obj); \
} \
PyObject* py_var(VM* vm, const ctype& value) { return vm->new_object(vm->ptype, value);} \
PyObject* py_var(VM* vm, ctype&& value) { return vm->new_object(vm->ptype, std::move(value));}
PyObject* py_var(VM* vm, const ctype& value) { return vm->heap.gcnew(vm->ptype, value);} \
PyObject* py_var(VM* vm, ctype&& value) { return vm->heap.gcnew(vm->ptype, std::move(value));}
class Generator: public BaseIter {
std::unique_ptr<Frame> frame;
@ -41,6 +43,7 @@ struct PyTypeInfo{
class VM {
VM* vm; // self reference for simplify code
ManagedHeap heap;
public:
std::stack< std::unique_ptr<Frame> > callstack;
std::vector<PyTypeInfo> _all_types;
@ -78,11 +81,10 @@ public:
}
init_builtin_types();
// for(int i=0; i<128; i++) _ascii_str_pool[i] = new_object(tp_str, std::string(1, (char)i));
}
PyObject* asStr(PyObject* obj){
PyVarOrNull f = getattr(obj, __str__, false, true);
PyObject* f = getattr(obj, __str__, false, true);
if(f != nullptr) return call(f);
return asRepr(obj);
}
@ -95,8 +97,8 @@ public:
}
PyObject* asIter(PyObject* obj){
if(is_type(obj, tp_native_iterator)) return obj;
PyVarOrNull iter_f = getattr(obj, __iter__, false, true);
if(is_type(obj, tp_iterator)) return obj;
PyObject* iter_f = getattr(obj, __iter__, false, true);
if(iter_f != nullptr) return call(iter_f);
TypeError(OBJ_NAME(_t(obj)).escape(true) + " object is not iterable");
return nullptr;
@ -104,7 +106,7 @@ public:
PyObject* asList(PyObject* iterable){
if(is_type(iterable, tp_list)) return iterable;
return call(_t(tp_list), one_arg(iterable));
return call(_t(tp_list), Args{iterable});
}
PyObject** find_name_in_mro(PyObject* cls, StrName name){
@ -191,13 +193,13 @@ public:
PyObject* property(NativeFuncRaw fget){
PyObject* p = builtins->attr("property");
PyObject* method = new_object(tp_native_function, NativeFunc(fget, 1, false));
return call(p, one_arg(method));
PyObject* method = heap.gcnew(tp_native_function, NativeFunc(fget, 1, false));
return call(p, Args{method});
}
PyObject* new_type_object(PyObject* mod, StrName name, Type base){
// use gcnew
PyObject* obj = make_sp<PyObject, Py_<Type>>(tp_type, _all_types.size());
PyObject* obj = new Py_<Type>(tp_type, _all_types.size());
PyTypeInfo info{
.obj = obj,
.base = base,
@ -213,30 +215,6 @@ public:
return OBJ_GET(Type, obj);
}
template<typename T>
inline PyObject* new_object(PyObject* type, const T& _value) {
#if PK_EXTRA_CHECK
if(!is_type(type, tp_type)) UNREACHABLE();
#endif
return make_sp<PyObject, Py_<std::decay_t<T>>>(OBJ_GET(Type, type), _value);
}
template<typename T>
inline PyObject* new_object(PyObject* type, T&& _value) {
#if PK_EXTRA_CHECK
if(!is_type(type, tp_type)) UNREACHABLE();
#endif
return make_sp<PyObject, Py_<std::decay_t<T>>>(OBJ_GET(Type, type), std::move(_value));
}
template<typename T>
inline PyObject* new_object(Type type, const T& _value) {
return make_sp<PyObject, Py_<std::decay_t<T>>>(type, _value);
}
template<typename T>
inline PyObject* new_object(Type type, T&& _value) {
return make_sp<PyObject, Py_<std::decay_t<T>>>(type, std::move(_value));
}
PyObject* _find_type(const Str& type){
PyObject** obj = builtins->attr().try_get(type);
if(!obj){
@ -282,19 +260,19 @@ public:
// for quick access
Type tp_object, tp_type, tp_int, tp_float, tp_bool, tp_str;
Type tp_list, tp_tuple;
Type tp_function, tp_native_function, tp_native_iterator, tp_bound_method;
Type tp_function, tp_native_function, tp_iterator, tp_bound_method;
Type tp_slice, tp_range, tp_module, tp_ref;
Type tp_super, tp_exception, tp_star_wrapper;
template<typename P>
inline PyObject* PyIter(P&& value) {
static_assert(std::is_base_of_v<BaseIter, std::decay_t<P>>);
return new_object(tp_native_iterator, std::forward<P>(value));
return heap.gcnew<P>(tp_iterator, std::forward<P>(value));
}
inline BaseIter* PyIter_AS_C(PyObject* obj)
{
check_type(obj, tp_native_iterator);
check_type(obj, tp_iterator);
return static_cast<BaseIter*>(obj->value());
}
@ -369,7 +347,7 @@ public:
PyObject* _exec();
template<typename P>
PyVarRef PyRef(P&& value);
PyObject* PyRef(P&& value);
const BaseRef* PyRef_AS_C(PyObject* obj);
};
@ -458,23 +436,23 @@ template<> float py_cast<float>(VM* vm, PyObject* obj){
vm->check_type(obj, vm->tp_float);
i64 bits = obj.bits;
bits = (bits >> 2) << 2;
return __8B(bits)._float;
return BitsCvt(bits)._float;
}
template<> float _py_cast<float>(VM* vm, PyObject* obj){
i64 bits = obj.bits;
bits = (bits >> 2) << 2;
return __8B(bits)._float;
return BitsCvt(bits)._float;
}
template<> double py_cast<double>(VM* vm, PyObject* obj){
vm->check_type(obj, vm->tp_float);
i64 bits = obj.bits;
bits = (bits >> 2) << 2;
return __8B(bits)._float;
return BitsCvt(bits)._float;
}
template<> double _py_cast<double>(VM* vm, PyObject* obj){
i64 bits = obj.bits;
bits = (bits >> 2) << 2;
return __8B(bits)._float;
return BitsCvt(bits)._float;
}
@ -502,7 +480,7 @@ PY_VAR_INT(unsigned long long)
#define PY_VAR_FLOAT(T) \
PyObject* py_var(VM* vm, T _val){ \
f64 val = static_cast<f64>(_val); \
i64 bits = __8B(val)._int; \
i64 bits = BitsCvt(val)._int; \
bits = (bits >> 2) << 2; \
bits |= 0b10; \
return reinterpret_cast<PyObject*>(bits); \
@ -561,7 +539,7 @@ PyObject* VM::asBool(PyObject* obj){
if(obj == None) return False;
if(is_type(obj, tp_int)) return VAR(CAST(i64, obj) != 0);
if(is_type(obj, tp_float)) return VAR(CAST(f64, obj) != 0.0);
PyVarOrNull len_fn = getattr(obj, __len__, false, true);
PyObject* len_fn = getattr(obj, __len__, false, true);
if(len_fn != nullptr){
PyObject* ret = call(len_fn);
return VAR(CAST(i64, ret) > 0);
@ -596,8 +574,11 @@ PyObject* VM::asRepr(PyObject* obj){
}
PyObject* VM::new_module(StrName name) {
PyObject* obj = new_object(tp_module, DummyModule());
PyObject* obj = new Py_<DummyModule>(tp_module, DummyModule());
obj->attr().set(__name__, VAR(name.str()));
// we do not allow override in order to avoid memory leak
// it is because Module objects are not garbage collected
if(_modules.contains(name)) UNREACHABLE();
_modules.set(name, obj);
return obj;
}
@ -672,9 +653,11 @@ Str VM::disassemble(CodeObject_ co){
}
void VM::init_builtin_types(){
// Py_(Type type, T&& val)
PyVar _tp_object = make_sp<PyObject, Py_<Type>>(Type(1), Type(0));
PyVar _tp_type = make_sp<PyObject, Py_<Type>>(Type(1), Type(1));
PyObject* _tp_object = new Py_<Type>(Type(1), Type(0));
PyObject* _tp_type = new Py_<Type>(Type(1), Type(1));
// PyTypeObject is managed by _all_types
// PyModuleObject is managed by _modules
// They are not managed by GC, so we use a simple "new"
_all_types.push_back({.obj = _tp_object, .base = -1, .name = "object"});
_all_types.push_back({.obj = _tp_type, .base = 0, .name = "type"});
tp_object = 0; tp_type = 1;
@ -695,17 +678,17 @@ void VM::init_builtin_types(){
tp_function = _new_type_object("function");
tp_native_function = _new_type_object("native_function");
tp_native_iterator = _new_type_object("native_iterator");
tp_iterator = _new_type_object("iterator");
tp_bound_method = _new_type_object("bound_method");
tp_super = _new_type_object("super");
tp_exception = _new_type_object("Exception");
this->None = new_object(_new_type_object("NoneType"), DUMMY_VAL);
this->Ellipsis = new_object(_new_type_object("ellipsis"), DUMMY_VAL);
this->True = new_object(tp_bool, true);
this->False = new_object(tp_bool, false);
this->_py_op_call = new_object(_new_type_object("_py_op_call"), DUMMY_VAL);
this->_py_op_yield = new_object(_new_type_object("_py_op_yield"), DUMMY_VAL);
this->None = new Py_<Dummy>(_new_type_object("NoneType"), {});
this->Ellipsis = new Py_<Dummy>(_new_type_object("ellipsis"), {});
this->True = new Py_<Dummy>(tp_bool, {});
this->False = new Py_<Dummy>(tp_bool, {});
this->_py_op_call = new Py_<Dummy>(_new_type_object("_py_op_call"), {});
this->_py_op_yield = new Py_<Dummy>(_new_type_object("_py_op_yield"), {});
this->builtins = new_module("builtins");
this->_main = new_module("__main__");
@ -735,8 +718,8 @@ PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, bool opCal
if(new_f != nullptr){
obj = call(*new_f, std::move(args), kwargs, false);
}else{
obj = new_object(_callable, DummyInstance());
PyVarOrNull init_f = getattr(obj, __init__, false, true);
obj = heap.gcnew<DummyInstance>(_callable, {});
PyObject* init_f = getattr(obj, __init__, false, true);
if (init_f != nullptr) call(init_f, std::move(args), kwargs, false);
}
return obj;
@ -801,7 +784,7 @@ PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, bool opCal
return _exec();
}
PyVarOrNull call_f = getattr(_callable, __call__, false, true);
PyObject* call_f = getattr(_callable, __call__, false, true);
if(call_f != nullptr){
return call(call_f, std::move(args), kwargs, false);
}
@ -829,42 +812,44 @@ using Super = std::pair<PyObject*, Type>;
// https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err, bool class_only){
PyObject* objtype = _t(*obj).get();
if(is_type(*obj, tp_super)){
const Super& super = OBJ_GET(Super, *obj);
obj = &super.first;
objtype = _t(super.second).get();
PyObject* objtype = _t(obj);
// handle super() proxy
if(is_type(obj, tp_super)){
const Super& super = OBJ_GET(Super, obj);
obj = super.first;
objtype = _t(super.second);
}
PyObject** cls_var = find_name_in_mro(objtype, name);
if(cls_var != nullptr){
// handle descriptor
PyObject** descr_get = _t(*cls_var)->attr().try_get(__get__);
if(descr_get != nullptr) return call(*descr_get, two_args(*cls_var, *obj));
if(descr_get != nullptr) return call(*descr_get, Args{*cls_var, obj});
}
// handle instance __dict__
if(!class_only && !(*obj).is_tagged() && (*obj)->is_attr_valid()){
PyObject** val = (*obj)->attr().try_get(name);
if(!class_only && !is_tagged(obj) && obj->is_attr_valid()){
PyObject** val = obj->attr().try_get(name);
if(val != nullptr) return *val;
}
if(cls_var != nullptr){
// bound method is non-data descriptor
if(is_type(*cls_var, tp_function) || is_type(*cls_var, tp_native_function)){
return VAR(BoundMethod(*obj, *cls_var));
return VAR(BoundMethod(obj, *cls_var));
}
return *cls_var;
}
if(throw_err) AttributeError(*obj, name);
if(throw_err) AttributeError(obj, name);
return nullptr;
}
template<typename T>
void VM::setattr(PyObject* obj, StrName name, T&& value){
static_assert(std::is_same_v<std::decay_t<T>, PyVar>);
PyObject* objtype = _t(obj).get();
static_assert(std::is_same_v<std::decay_t<T>, PyObject*>);
PyObject* objtype = _t(obj);
// handle super() proxy
if(is_type(obj, tp_super)){
Super& super = OBJ_GET(Super, *obj);
obj = super.first;
objtype = _t(super.second).get();
objtype = _t(super.second);
}
PyObject** cls_var = find_name_in_mro(objtype, name);
if(cls_var != nullptr){
@ -873,7 +858,7 @@ void VM::setattr(PyObject* obj, StrName name, T&& value){
if(cls_var_t->attr().contains(__get__)){
PyObject** descr_set = cls_var_t->attr().try_get(__set__);
if(descr_set != nullptr){
call(*descr_set, three_args(*cls_var, obj, std::forward<T>(value)));
call(*descr_set, Args{*cls_var, obj, std::forward<T>(value)});
}else{
TypeError("readonly attribute: " + name.str().escape(true));
}
@ -881,7 +866,7 @@ void VM::setattr(PyObject* obj, StrName name, T&& value){
}
}
// handle instance __dict__
if(obj.is_tagged() || !(*obj)->is_attr_valid()) TypeError("cannot set attribute");
if(is_tagged(obj) || !obj->is_attr_valid()) TypeError("cannot set attribute");
obj->attr().set(name, std::forward<T>(value));
}