Jelajahi Sumber

add `py_tphookattributes`

blueloveTH 8 bulan lalu
induk
melakukan
b6cefdeedc

+ 4 - 0
include/pocketpy/interpreter/typeinfo.h

@@ -16,6 +16,10 @@ typedef struct py_TypeInfo {
     bool is_python;  // is it a python class? (not derived from c object)
     bool is_sealed;  // can it be subclassed?
 
+    bool (*getattribute)(py_Ref self, py_Name name);
+    bool (*setattribute)(py_Ref self, py_Name name, py_Ref val);
+    bool (*delattribute)(py_Ref self, py_Name name);
+
     py_TValue annotations;
     py_Dtor dtor;  // destructor for this type, NULL if no dtor
     void (*on_end_subclass)(struct py_TypeInfo*);  // backdoor for enum module

+ 13 - 4
include/pocketpy/pocketpy.h

@@ -30,7 +30,10 @@ typedef void (*py_Dtor)(void*);
 
 #ifdef PK_IS_PUBLIC_INCLUDE
 typedef struct py_TValue {
-    int64_t _[2];
+    py_Type type;
+    bool is_ptr;
+    int extra;
+    int64_t _i64;
 } py_TValue;
 #endif
 
@@ -355,6 +358,12 @@ PK_API const char* py_tpname(py_Type type);
 /// Call a type to create a new instance.
 PK_API bool py_tpcall(py_Type type, int argc, py_Ref argv) PY_RAISE PY_RETURN;
 
+/// Set attribute hooks for the given type.
+PK_API void py_tphookattributes(py_Type type,
+                                bool (*getattribute)(py_Ref self, py_Name name),
+                                bool (*setattribute)(py_Ref self, py_Name name, py_Ref val),
+                                bool (*delattribute)(py_Ref self, py_Name name));
+
 /// Check if the object is an instance of the given type exactly.
 /// Raise `TypeError` if the check fails.
 PK_API bool py_checktype(py_Ref self, py_Type type) PY_RAISE;
@@ -760,11 +769,11 @@ enum py_PredefinedType {
     tp_bool,
     tp_str,
     tp_str_iterator,
-    tp_list,   // c11_vector
-    tp_tuple,  // N slots
+    tp_list,            // c11_vector
+    tp_tuple,           // N slots
     tp_list_iterator,   // 1 slot
     tp_tuple_iterator,  // 1 slot
-    tp_slice,  // 3 slots (start, stop, step)
+    tp_slice,           // 3 slots (start, stop, step)
     tp_range,
     tp_range_iterator,
     tp_module,

+ 16 - 1
src/interpreter/typeinfo.c

@@ -81,6 +81,10 @@ static void py_TypeInfo__common_init(py_Name name,
     self->is_python = is_python;
     self->is_sealed = is_sealed;
 
+    self->getattribute = NULL;
+    self->setattribute = NULL;
+    self->delattribute = NULL;
+
     self->annotations = *py_NIL();
     self->dtor = dtor;
     self->on_end_subclass = NULL;
@@ -150,4 +154,15 @@ py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, vo
     py_Type type = pk_newtype(name, base, module, dtor, false, false);
     if(module) py_setdict(module, py_name(name), py_tpobject(type));
     return type;
-}
+}
+
+void py_tphookattributes(py_Type type,
+                         bool (*getattribute)(py_Ref self, py_Name name),
+                         bool (*setattribute)(py_Ref self, py_Name name, py_Ref val),
+                         bool (*delattribute)(py_Ref self, py_Name name)) {
+    assert(type);
+    py_TypeInfo* ti = pk_typeinfo(type);
+    ti->getattribute = getattribute;
+    ti->setattribute = setattribute;
+    ti->delattribute = delattribute;
+}

+ 9 - 2
src/public/py_ops.c

@@ -122,6 +122,8 @@ int py_next(py_Ref val) {
 bool py_getattr(py_Ref self, py_Name name) {
     // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
     py_TypeInfo* ti = pk_typeinfo(self->type);
+    if(ti->getattribute) return ti->getattribute(self, name);
+
     py_Ref cls_var = pk_tpfindname(ti, name);
     if(cls_var) {
         // handle descriptor
@@ -212,8 +214,10 @@ bool py_getattr(py_Ref self, py_Name name) {
 }
 
 bool py_setattr(py_Ref self, py_Name name, py_Ref val) {
-    py_Type type = self->type;
-    py_Ref cls_var = py_tpfindname(type, name);
+    py_TypeInfo* ti = pk_typeinfo(self->type);
+    if(ti->setattribute) return ti->setattribute(self, name, val);
+
+    py_Ref cls_var = pk_tpfindname(ti, name);
     if(cls_var) {
         // handle descriptor
         if(py_istype(cls_var, tp_property)) {
@@ -239,6 +243,9 @@ bool py_setattr(py_Ref self, py_Name name, py_Ref val) {
 }
 
 bool py_delattr(py_Ref self, py_Name name) {
+    py_TypeInfo* ti = pk_typeinfo(self->type);
+    if(ti->delattribute) return ti->delattribute(self, name);
+
     if(self->is_ptr && self->_obj->slots == -1) {
         if(py_deldict(self, name)) return true;
         return AttributeError(self, name);