diff --git a/python/builtins.py b/python/builtins.py index 44b652b9..118845f7 100644 --- a/python/builtins.py +++ b/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 "" diff --git a/src/obj.h b/src/obj.h index 2dfcc1b6..ea723662 100644 --- a/src/obj.h +++ b/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; diff --git a/src/pocketpy.h b/src/pocketpy.h index 96267990..5cd31ffa 100644 --- a/src/pocketpy.h +++ b/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])); })); diff --git a/src/vm.h b/src/vm.h index 5e24b2f0..550c35a7 100644 --- a/src/vm.h +++ b/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(_new_type_object("NoneType"), {}); this->Ellipsis = heap._new(_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())); }