diff --git a/src/cffi.h b/src/cffi.h index 424414f5..f6bbbeae 100644 --- a/src/cffi.h +++ b/src/cffi.h @@ -2,8 +2,18 @@ #include "vm.h" +// struct Point2{ +// int x; +// int y; +// }; + +// std::map _Point2_members = { +// {"x", offsetof(Point2, x)}, +// {"y", offsetof(Point2, y)}, +// }; + struct CType{ - PY_CLASS(c, _type) + PY_CLASS(c, type_) const char* name; // must be a literal const int size; @@ -48,13 +58,21 @@ constexpr int ctype(const char name[]){ #define ctype_t(x) (kCTypes[ctype(x)]) struct Pointer{ - PY_CLASS(c, _ptr) + PY_CLASS(c, ptr_) void* ptr; CType _ctype; // base type Pointer(void* ptr, CType _ctype) : ptr(ptr), _ctype(_ctype) {} + Pointer operator+(i64 offset) const { + return Pointer((int8_t*)ptr + offset * _ctype.size, _ctype); + } + + Pointer operator-(i64 offset) const { + return Pointer((int8_t*)ptr - offset * _ctype.size, _ctype); + } + static void _register(VM* vm, PyVar mod, PyVar type){ vm->bind_static_method<-1>(type, "__new__", CPP_NOT_IMPLEMENTED()); @@ -67,16 +85,12 @@ struct Pointer{ vm->bind_method<1>(type, "__add__", [](VM* vm, pkpy::Args& args) { Pointer& self = vm->py_cast(args[0]); - i64 offset = vm->PyInt_AS_C(args[1]); - int8_t* new_ptr = (int8_t*)self.ptr + offset * self._ctype.size; - return vm->new_object((void*)new_ptr, self._ctype); + return vm->new_object(self + vm->PyInt_AS_C(args[1])); }); vm->bind_method<1>(type, "__sub__", [](VM* vm, pkpy::Args& args) { Pointer& self = vm->py_cast(args[0]); - i64 offset = vm->PyInt_AS_C(args[1]); - int8_t* new_ptr = (int8_t*)self.ptr - offset * self._ctype.size; - return vm->new_object((void*)new_ptr, self._ctype); + return vm->new_object(self - vm->PyInt_AS_C(args[1])); }); vm->bind_method<1>(type, "__eq__", [](VM* vm, pkpy::Args& args) { @@ -95,48 +109,13 @@ struct Pointer{ vm->bind_method<1>(type, "__getitem__", [](VM* vm, pkpy::Args& args) { Pointer& self = vm->py_cast(args[0]); i64 index = vm->PyInt_AS_C(args[1]); - switch(self._ctype.index){ - case ctype("char_"): return vm->PyInt(((char*)self.ptr)[index]); - case ctype("int_"): return vm->PyInt(((int*)self.ptr)[index]); - case ctype("float_"): return vm->PyFloat(((float*)self.ptr)[index]); - case ctype("double_"): return vm->PyFloat(((double*)self.ptr)[index]); - case ctype("bool_"): return vm->PyBool(((bool*)self.ptr)[index]); - case ctype("void_"): vm->TypeError("cannot index void*"); break; - case ctype("int8_"): return vm->PyInt(((int8_t*)self.ptr)[index]); - case ctype("int16_"): return vm->PyInt(((int16_t*)self.ptr)[index]); - case ctype("int32_"): return vm->PyInt(((int32_t*)self.ptr)[index]); - case ctype("int64_"): return vm->PyInt(((int64_t*)self.ptr)[index]); - case ctype("uint8_"): return vm->PyInt(((uint8_t*)self.ptr)[index]); - case ctype("uint16_"): return vm->PyInt(((uint16_t*)self.ptr)[index]); - case ctype("uint32_"): return vm->PyInt(((uint32_t*)self.ptr)[index]); - case ctype("uint64_"): return vm->PyInt(((uint64_t*)self.ptr)[index]); - // use macro here to do extension - default: UNREACHABLE(); - } - return vm->None; + return (self+index).get(vm); }); vm->bind_method<2>(type, "__setitem__", [](VM* vm, pkpy::Args& args) { Pointer& self = vm->py_cast(args[0]); i64 index = vm->PyInt_AS_C(args[1]); - switch(self._ctype.index){ - case ctype("char_"): ((char*)self.ptr)[index] = vm->PyInt_AS_C(args[2]); break; - case ctype("int_"): ((int*)self.ptr)[index] = vm->PyInt_AS_C(args[2]); break; - case ctype("float_"): ((float*)self.ptr)[index] = vm->PyFloat_AS_C(args[2]); break; - case ctype("double_"): ((double*)self.ptr)[index] = vm->PyFloat_AS_C(args[2]); break; - case ctype("bool_"): ((bool*)self.ptr)[index] = vm->PyBool_AS_C(args[2]); break; - case ctype("void_"): vm->TypeError("cannot index void*"); break; - case ctype("int8_"): ((int8_t*)self.ptr)[index] = vm->PyInt_AS_C(args[2]); break; - case ctype("int16_"): ((int16_t*)self.ptr)[index] = vm->PyInt_AS_C(args[2]); break; - case ctype("int32_"): ((int32_t*)self.ptr)[index] = vm->PyInt_AS_C(args[2]); break; - case ctype("int64_"): ((int64_t*)self.ptr)[index] = vm->PyInt_AS_C(args[2]); break; - case ctype("uint8_"): ((uint8_t*)self.ptr)[index] = vm->PyInt_AS_C(args[2]); break; - case ctype("uint16_"): ((uint16_t*)self.ptr)[index] = vm->PyInt_AS_C(args[2]); break; - case ctype("uint32_"): ((uint32_t*)self.ptr)[index] = vm->PyInt_AS_C(args[2]); break; - case ctype("uint64_"): ((uint64_t*)self.ptr)[index] = vm->PyInt_AS_C(args[2]); break; - // use macro here to do extension - default: UNREACHABLE(); - } + (self+index).set(vm, args[2]); return vm->None; }); @@ -147,14 +126,125 @@ struct Pointer{ }); } - template - inline T cast() noexcept { return reinterpret_cast(ptr); } + template + inline T& ref() noexcept { return *reinterpret_cast(ptr); } + + template + inline TP cast() noexcept { + static_assert(std::is_pointer_v); + return reinterpret_cast(ptr); + } + + PyVar get(VM* vm){ + switch(_ctype.index){ + case ctype("char_"): return vm->PyInt(ref()); + case ctype("int_"): return vm->PyInt(ref()); + case ctype("float_"): return vm->PyFloat(ref()); + case ctype("double_"): return vm->PyFloat(ref()); + case ctype("bool_"): return vm->PyBool(ref()); + case ctype("void_"): vm->ValueError("cannot get void*"); break; + case ctype("int8_"): return vm->PyInt(ref()); + case ctype("int16_"): return vm->PyInt(ref()); + case ctype("int32_"): return vm->PyInt(ref()); + case ctype("int64_"): return vm->PyInt(ref()); + case ctype("uint8_"): return vm->PyInt(ref()); + case ctype("uint16_"): return vm->PyInt(ref()); + case ctype("uint32_"): return vm->PyInt(ref()); + case ctype("uint64_"): return vm->PyInt(ref()); + // use macro here to do extension + default: UNREACHABLE(); + } + return vm->None; + } + + void set(VM* vm, const PyVar& val){ + switch(_ctype.index){ + case ctype("char_"): ref() = vm->PyInt_AS_C(val); break; + case ctype("int_"): ref() = vm->PyInt_AS_C(val); break; + case ctype("float_"): ref() = vm->PyFloat_AS_C(val); break; + case ctype("double_"): ref() = vm->PyFloat_AS_C(val); break; + case ctype("bool_"): ref() = vm->PyBool_AS_C(val); break; + case ctype("void_"): vm->ValueError("cannot set void*"); break; + case ctype("int8_"): ref() = vm->PyInt_AS_C(val); break; + case ctype("int16_"): ref() = vm->PyInt_AS_C(val); break; + case ctype("int32_"): ref() = vm->PyInt_AS_C(val); break; + case ctype("int64_"): ref() = vm->PyInt_AS_C(val); break; + case ctype("uint8_"): ref() = vm->PyInt_AS_C(val); break; + case ctype("uint16_"): ref() = vm->PyInt_AS_C(val); break; + case ctype("uint32_"): ref() = vm->PyInt_AS_C(val); break; + case ctype("uint64_"): ref() = vm->PyInt_AS_C(val); break; + // use macro here to do extension + default: UNREACHABLE(); + } + } +}; + +struct StructMemberInfo { + int offset; + CType type; +}; + +struct StructMetaInfo { + Str name; + std::map members; +}; + +struct Struct { + PY_CLASS(c, struct_) + + const StructMetaInfo* info; + int8_t* _data; // store any `struct` + + Struct(const StructMetaInfo* info, int8_t* data) : info(info), _data(data) {} + ~Struct(){ delete[] _data; } + + int8_t* address(std::string_view name){ + auto it = info->members.find(name); + if(it == info->members.end()) return nullptr; + return _data + it->second.offset; + } + + static void _register(VM* vm, PyVar mod, PyVar type){ + vm->bind_static_method<-1>(type, "__new__", CPP_NOT_IMPLEMENTED()); + + vm->bind_method<0>(type, "__repr__", [](VM* vm, pkpy::Args& args) { + Struct& self = vm->py_cast(args[0]); + StrStream ss; + ss << "name << "'>"; + return vm->PyStr(ss.str()); + }); + +#define MEMBER_LOOKUP() \ + Struct& self = vm->py_cast(args[0]); \ + std::string_view name = vm->PyStr_AS_C(args[1]); \ + auto it = self.info->members.find(name); \ + if(it == self.info->members.end()){ \ + vm->AttributeError(args[0], name.data()); \ + return vm->None; \ + } \ + const StructMemberInfo& info = it->second; \ + Pointer p = Pointer(self._data+info.offset, info.type); \ + + vm->bind_method<1>(type, "__getattr__", [](VM* vm, pkpy::Args& args) { + MEMBER_LOOKUP() + return p.get(vm); + }); + + vm->bind_method<2>(type, "__setattr__", [](VM* vm, pkpy::Args& args) { + MEMBER_LOOKUP() + p.set(vm, args[2]); + return vm->None; + }); + +#undef MEMBER_LOOKUP + } }; void add_module_c(VM* vm){ PyVar mod = vm->new_module("c"); PyVar ptr_t = vm->register_class(mod); vm->register_class(mod); + vm->register_class(mod); for(int i=0; isetattr(mod, kCTypes[i].name, vm->new_object(kCTypes[i])); diff --git a/src/common.h b/src/common.h index bd8b1869..c3703a9a 100644 --- a/src/common.h +++ b/src/common.h @@ -51,6 +51,7 @@ typedef double f64; struct Dummy { }; struct DummyInstance { }; +struct DummyProperty { }; struct DummyModule { }; #define DUMMY_VAL Dummy() diff --git a/src/obj.h b/src/obj.h index 86c6d798..7c4e19cd 100644 --- a/src/obj.h +++ b/src/obj.h @@ -109,7 +109,7 @@ struct Py_ : PyObject { _attr = new pkpy::NameDict(16, kTypeAttrLoadFactor); }else if constexpr(std::is_same_v){ _attr = new pkpy::NameDict(4, kInstAttrLoadFactor); - }else if constexpr(std::is_same_v || std::is_same_v){ + }else if constexpr(std::is_same_v || std::is_same_v || std::is_same_v){ _attr = new pkpy::NameDict(4, kInstAttrLoadFactor); }else{ _attr = nullptr; diff --git a/src/vm.h b/src/vm.h index 2928193b..a9d590bf 100644 --- a/src/vm.h +++ b/src/vm.h @@ -361,6 +361,7 @@ public: while(cls != None.get()) { val = cls->attr().try_get(name); if(val != nullptr){ + if(is_type(*val, tp_property)) return call((*val)->attr("__get__"), pkpy::one_arg(obj)); if(is_type(*val, tp_function) || is_type(*val, tp_native_function)){ return PyBoundMethod({obj, *val}); }else{ @@ -378,10 +379,26 @@ public: if(obj.is_tagged()) TypeError("cannot set attribute"); PyObject* p = obj.get(); while(p->type == tp_super) p = static_cast(p->value())->get(); + + // handle property + PyVar* prop = _t(obj)->attr().try_get(name); + if(prop != nullptr && is_type(*prop, tp_property)){ + call((*prop)->attr("__set__"), pkpy::two_args(obj, std::forward(value))); + return; + } + if(!p->is_attr_valid()) TypeError("cannot set attribute"); p->attr().set(name, std::forward(value)); } + void bind_property(PyVar obj, Str field, NativeFuncRaw getter, NativeFuncRaw setter){ + check_type(obj, tp_type); + PyVar prop = new_object(tp_property, DummyProperty()); + prop->attr().set("__get__", PyNativeFunc(pkpy::NativeFunc(getter, 0, true))); + prop->attr().set("__set__", PyNativeFunc(pkpy::NativeFunc(setter, 1, true))); + setattr(obj, field, prop); + } + template void bind_method(PyVar obj, Str funcName, NativeFuncRaw fn) { check_type(obj, tp_type); @@ -520,7 +537,7 @@ public: Type tp_list, tp_tuple; Type tp_function, tp_native_function, tp_native_iterator, tp_bound_method; Type tp_slice, tp_range, tp_module, tp_ref; - Type tp_super, tp_exception, tp_star_wrapper; + Type tp_super, tp_exception, tp_star_wrapper, tp_property; template inline PyVarRef PyRef(P&& value) { @@ -637,6 +654,7 @@ public: tp_module = _new_type_object("module"); tp_ref = _new_type_object("_ref"); tp_star_wrapper = _new_type_object("_star_wrapper"); + tp_property = _new_type_object("property"); tp_function = _new_type_object("function"); tp_native_function = _new_type_object("native_function");