فهرست منبع

reimpl `property`

blueloveTH 2 سال پیش
والد
کامیت
e1eed59030
4فایلهای تغییر یافته به همراه36 افزوده شده و 38 حذف شده
  1. 2 22
      python/builtins.py
  2. 6 0
      src/obj.h
  3. 11 1
      src/pocketpy.h
  4. 17 15
      src/vm.h

+ 2 - 22
python/builtins.py

@@ -129,28 +129,8 @@ def list@sort(self, reverse=False):
     if reverse:
         self.reverse()
 
-class property:
-    def __init__(self, fget, fset=None):
-        self.fget = fget
-        self.fset = fset
-
-    def __get__(self, obj):
-        return self.fget(obj)
-    
-    def __set__(self, obj, value):
-        if self.fset is None:
-            raise AttributeError("readonly property")
-        self.fset(obj, value)
-
-class staticmethod:
-    def __init__(self, f):
-        self.f = f
-
-    def __get__(self, obj):
-        return self.f
-
-    def __call__(self, *args):
-        return self.f(*args)
+def staticmethod(f):
+    return f    # no effect
     
 def type@__repr__(self):
     return "<class '" + self.__name__ + "'>"

+ 6 - 0
src/obj.h

@@ -89,6 +89,12 @@ struct BoundMethod {
     }
 };
 
+struct Property{
+    PyObject* getter;
+    PyObject* setter;
+    Property(PyObject* getter, PyObject* setter) : getter(getter), setter(setter) {}
+};
+
 struct Range {
     i64 start = 0;
     i64 stop = -1;

+ 11 - 1
src/pocketpy.h

@@ -999,6 +999,16 @@ inline void init_builtins(VM* _vm) {
         }
         return true;
     });
+    /************ property ************/
+    _vm->bind_constructor<-1>("property", [](VM* vm, ArgsView args) {
+        if(args.size() == 1+1){
+            return VAR(Property(args[1], vm->None));
+        }else if(args.size() == 1+2){
+            return VAR(Property(args[1], args[2]));
+        }
+        vm->TypeError("property() takes at most 2 arguments");
+        return vm->None;
+    });
 
     RangeIter::register_class(_vm, _vm->builtins);
     ArrayIter::register_class(_vm, _vm->builtins);
@@ -1324,7 +1334,7 @@ inline void VM::post_init(){
     }));
 
     _t(tp_object)->attr().set("__dict__", property([](VM* vm, ArgsView args){
-        if(is_tagged(args[0]) || !args[0]->is_attr_valid()) vm->AttributeError("__dict__");
+        if(is_tagged(args[0]) || !args[0]->is_attr_valid()) vm->AttributeError("'__dict__'");
         return VAR(MappingProxy(args[0]));
     }));
 

+ 17 - 15
src/vm.h

@@ -133,7 +133,7 @@ public:
     Type tp_function, tp_native_func, tp_bound_method;
     Type tp_slice, tp_range, tp_module;
     Type tp_super, tp_exception, tp_bytes, tp_mappingproxy;
-    Type tp_dict;
+    Type tp_dict, tp_property;
 
     const bool enable_os;
 
@@ -271,11 +271,10 @@ public:
     }
 
     PyObject* property(NativeFuncC fget, NativeFuncC fset=nullptr){
-        PyObject* p = builtins->attr("property");
         PyObject* _0 = heap.gcnew(tp_native_func, NativeFunc(fget, 1, false));
         PyObject* _1 = vm->None;
         if(fset != nullptr) _1 = heap.gcnew(tp_native_func, NativeFunc(fset, 2, false));
-        return call(p, _0, _1);
+        return call(_t(tp_property), _0, _1);
     }
 
     PyObject* new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled=true){
@@ -640,6 +639,7 @@ DEF_NATIVE_2(Exception, tp_exception)
 DEF_NATIVE_2(Bytes, tp_bytes)
 DEF_NATIVE_2(MappingProxy, tp_mappingproxy)
 DEF_NATIVE_2(Dict, tp_dict)
+DEF_NATIVE_2(Property, tp_property)
 
 #undef DEF_NATIVE_2
 
@@ -1069,6 +1069,7 @@ inline void VM::init_builtin_types(){
     tp_bytes = _new_type_object("bytes");
     tp_mappingproxy = _new_type_object("mappingproxy");
     tp_dict = _new_type_object("dict");
+    tp_property = _new_type_object("property");
 
     this->None = heap._new<Dummy>(_new_type_object("NoneType"), {});
     this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {});
@@ -1090,6 +1091,7 @@ inline void VM::init_builtin_types(){
     builtins->attr().set("range", _t(tp_range));
     builtins->attr().set("bytes", _t(tp_bytes));
     builtins->attr().set("dict", _t(tp_dict));
+    builtins->attr().set("property", _t(tp_property));
     builtins->attr().set("StopIteration", StopIteration);
     builtins->attr().set("slice", _t(tp_slice));
 
@@ -1274,9 +1276,6 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
     return nullptr;
 }
 
-DEF_SNAME(__get__);
-DEF_SNAME(__set__);
-
 // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
 inline PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err){
     PyObject* objtype = _t(obj);
@@ -1289,8 +1288,10 @@ inline PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err){
     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_method(cls_var, descr_get, obj);
+        if(is_non_tagged_type(cls_var, tp_property)){
+            const Property& prop = _CAST(Property&, cls_var);
+            return call(prop.getter, obj);
+        }
     }
     // handle instance __dict__
     if(!is_tagged(obj) && obj->is_attr_valid()){
@@ -1324,8 +1325,10 @@ inline PyObject* VM::get_unbound_method(PyObject* obj, StrName name, PyObject**
     if(fallback){
         if(cls_var != nullptr){
             // handle descriptor
-            PyObject* descr_get = _t(cls_var)->attr().try_get(__get__);
-            if(descr_get != nullptr) return call_method(cls_var, descr_get, obj);
+            if(is_non_tagged_type(cls_var, tp_property)){
+                const Property& prop = _CAST(Property&, cls_var);
+                return call(prop.getter, obj);
+            }
         }
         // handle instance __dict__
         if(!is_tagged(obj) && obj->is_attr_valid()){
@@ -1355,11 +1358,10 @@ inline void VM::setattr(PyObject* obj, StrName name, PyObject* value){
     PyObject* cls_var = find_name_in_mro(objtype, name);
     if(cls_var != nullptr){
         // handle descriptor
-        PyObject* cls_var_t = _t(cls_var);
-        if(cls_var_t->attr().contains(__get__)){
-            PyObject* descr_set = cls_var_t->attr().try_get(__set__);
-            if(descr_set != nullptr){
-                call_method(cls_var, descr_set, obj, value);
+        if(is_non_tagged_type(cls_var, tp_property)){
+            const Property& prop = _CAST(Property&, cls_var);
+            if(prop.setter != vm->None){
+                call(prop.setter, obj, value);
             }else{
                 TypeError(fmt("readonly attribute: ", name.escape()));
             }