diff --git a/docs/modules/linalg.md b/docs/modules/linalg.md index 13635769..a952e053 100644 --- a/docs/modules/linalg.md +++ b/docs/modules/linalg.md @@ -92,7 +92,7 @@ class mat3x3(_StructLike['mat3x3']): @overload def __init__(self, _11, _12, _13, _21, _22, _23, _31, _32, _33) -> None: ... @overload - def __init__(self, a: list[list]): ... + def __init__(self, a: list[float]): ... def set_zeros(self) -> None: ... def set_ones(self) -> None: ... diff --git a/include/pocketpy/codeobject.h b/include/pocketpy/codeobject.h index 5affdb64..93d2c99c 100644 --- a/include/pocketpy/codeobject.h +++ b/include/pocketpy/codeobject.h @@ -108,7 +108,7 @@ struct FuncDecl { }; struct UserData{ - char data[15]; + char data[12]; bool empty; UserData(): empty(true) {} diff --git a/include/pocketpy/common.h b/include/pocketpy/common.h index 7bd5665c..a69ffd4a 100644 --- a/include/pocketpy/common.h +++ b/include/pocketpy/common.h @@ -21,7 +21,7 @@ #include #include -#define PK_VERSION "1.3.7" +#define PK_VERSION "1.3.8" #include "config.h" #include "export.h" diff --git a/include/pocketpy/obj.h b/include/pocketpy/obj.h index e81cc262..10ed2d69 100644 --- a/include/pocketpy/obj.h +++ b/include/pocketpy/obj.h @@ -15,17 +15,26 @@ using NativeFuncC = std::function; typedef PyObject* (*NativeFuncC)(VM*, ArgsView); #endif +enum class BindType{ + DEFAULT, + STATICMETHOD, + CLASSMETHOD, +}; + struct BoundMethod { PyObject* self; PyObject* func; BoundMethod(PyObject* self, PyObject* func) : self(self), func(func) {} - - bool operator==(const BoundMethod& rhs) const noexcept { - return self == rhs.self && func == rhs.func; - } - bool operator!=(const BoundMethod& rhs) const noexcept { - return self != rhs.self || func != rhs.func; - } +}; + +struct StaticMethod{ + PyObject* func; + StaticMethod(PyObject* func) : func(func) {} +}; + +struct ClassMethod{ + PyObject* func; + ClassMethod(PyObject* func) : func(func) {} }; struct Property{ @@ -323,6 +332,26 @@ struct Py_ final: PyObject { void* _value_ptr() override { return &_value; } }; +template<> +struct Py_ final: PyObject { + StaticMethod _value; + Py_(Type type, StaticMethod val): PyObject(type), _value(val) {} + void _obj_gc_mark() override { + PK_OBJ_MARK(_value.func); + } + void* _value_ptr() override { return &_value; } +}; + +template<> +struct Py_ final: PyObject { + ClassMethod _value; + Py_(Type type, ClassMethod val): PyObject(type), _value(val) {} + void _obj_gc_mark() override { + PK_OBJ_MARK(_value.func); + } + void* _value_ptr() override { return &_value; } +}; + template<> struct Py_ final: PyObject { Property _value; diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index 8319ed7e..2ffdfe7d 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -158,6 +158,7 @@ public: static constexpr Type tp_super=14, tp_exception=15, tp_bytes=16, tp_mappingproxy=17; static constexpr Type tp_dict=18, tp_property=19, tp_star_wrapper=20; + static constexpr Type tp_staticmethod=21, tp_classmethod=22; PyObject* cached_object__new__; @@ -475,8 +476,8 @@ public: PyObject* _py_generator(Frame&& frame, ArgsView buffer); void _prepare_py_call(PyObject**, ArgsView, ArgsView, const FuncDecl_&); // new style binding api - PyObject* bind(PyObject*, const char*, const char*, NativeFuncC, UserData userdata={}); - PyObject* bind(PyObject*, const char*, NativeFuncC, UserData userdata={}); + PyObject* bind(PyObject*, const char*, const char*, NativeFuncC, UserData userdata={}, BindType bt=BindType::DEFAULT); + PyObject* bind(PyObject*, const char*, NativeFuncC, UserData userdata={}, BindType bt=BindType::DEFAULT); PyObject* bind_property(PyObject*, Str, NativeFuncC fget, NativeFuncC fset=nullptr); }; @@ -494,6 +495,8 @@ DEF_NATIVE_2(MappingProxy, tp_mappingproxy) DEF_NATIVE_2(Dict, tp_dict) DEF_NATIVE_2(Property, tp_property) DEF_NATIVE_2(StarWrapper, tp_star_wrapper) +DEF_NATIVE_2(StaticMethod, tp_staticmethod) +DEF_NATIVE_2(ClassMethod, tp_classmethod) #undef DEF_NATIVE_2 diff --git a/include/typings/linalg.pyi b/include/typings/linalg.pyi index c391b686..9bf342c0 100644 --- a/include/typings/linalg.pyi +++ b/include/typings/linalg.pyi @@ -82,7 +82,7 @@ class mat3x3(_StructLike['mat3x3']): @overload def __init__(self, _11, _12, _13, _21, _22, _23, _31, _32, _33) -> None: ... @overload - def __init__(self, a: list[list]): ... + def __init__(self, a: list[float]): ... def set_zeros(self) -> None: ... def set_ones(self) -> None: ... diff --git a/python/builtins.py b/python/builtins.py index 303a5738..7f5026c4 100644 --- a/python/builtins.py +++ b/python/builtins.py @@ -5,17 +5,6 @@ def print(*args, sep=' ', end='\n'): s = sep.join([str(i) for i in args]) _sys.stdout.write(s + end) -def issubclass(cls, base): - if type(cls) is not type: - raise TypeError('issubclass() arg 1 must be a class') - if type(base) is not type: - raise TypeError('issubclass() arg 2 must be a class') - while cls is not None: - if cls is base: - return True - cls = cls.__base__ - return False - def _minmax_reduce(op, args, key): if key is None: if len(args) == 2: @@ -267,16 +256,6 @@ def help(obj): print(obj.__signature__) print(obj.__doc__) - -class classmethod: - def __init__(self, f): - self.f = f - raise NotImplementedError - -def staticmethod(f): - return f - - def complex(*args, **kwargs): import cmath return cmath.complex(*args, **kwargs) diff --git a/python/pickle.py b/python/pickle.py index bc23785f..df8f7a6b 100644 --- a/python/pickle.py +++ b/python/pickle.py @@ -10,14 +10,6 @@ def _find_class(path: str): modpath, name = path.split(_MOD_T_SEP) return __import__(modpath).__dict__[name] -def _find__new__(cls): - while cls is not None: - d = cls.__dict__ - if "__new__" in d: - return d["__new__"] - cls = cls.__base__ - assert False - class _Pickler: def __init__(self, obj) -> None: self.obj = obj @@ -148,7 +140,7 @@ class _Unpickler: cls, newargs, state = o cls = _find_class(o[0]) # create uninitialized instance - new_f = _find__new__(cls) + new_f = getattr(cls, "__new__") if newargs is not None: newargs = [self.unwrap(i) for i in newargs] inst = new_f(cls, *newargs) diff --git a/src/linalg.cpp b/src/linalg.cpp index 8c937dee..6199d0d0 100644 --- a/src/linalg.cpp +++ b/src/linalg.cpp @@ -102,7 +102,7 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float vm->bind_constructor<3>(type, [](VM* vm, ArgsView args){ float x = CAST_F(args[1]); float y = CAST_F(args[2]); - return VAR(Vec2(x, y)); + return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), Vec2(x, y)); }); vm->bind_method<0>(type, "__getnewargs__", [](VM* vm, ArgsView args){ @@ -120,7 +120,7 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float float delta_time = CAST_F(args[5]); Vec2 ret = SmoothDamp(current, target, current_velocity, smooth_time, max_speed, delta_time); return VAR(ret); - }); + }, {}, BindType::STATICMETHOD); // @staticmethod vm->bind(type, "angle(__from: vec2, __to: vec2) -> float", [](VM* vm, ArgsView args){ @@ -131,7 +131,7 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float if(val > PI) val -= 2*PI; if(val < -PI) val += 2*PI; return VAR(val); - }); + }, {}, BindType::STATICMETHOD); vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ PyVec2& self = _CAST(PyVec2&, obj); @@ -174,7 +174,7 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float float x = CAST_F(args[1]); float y = CAST_F(args[2]); float z = CAST_F(args[3]); - return VAR(Vec3(x, y, z)); + return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), Vec3(x, y, z)); }); vm->bind_method<0>(type, "__getnewargs__", [](VM* vm, ArgsView args){ @@ -213,7 +213,7 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float float y = CAST_F(args[2]); float z = CAST_F(args[3]); float w = CAST_F(args[4]); - return VAR(Vec4(x, y, z, w)); + return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), Vec4(x, y, z, w)); }); vm->bind_method<0>(type, "__getnewargs__", [](VM* vm, ArgsView args){ @@ -255,24 +255,18 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float PY_STRUCT_LIKE(PyMat3x3) vm->bind_constructor<-1>(type, [](VM* vm, ArgsView args){ - if(args.size() == 1+0) return VAR_T(PyMat3x3, Mat3x3::zeros()); + if(args.size() == 1+0) return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), Mat3x3::zeros()); + if(args.size() == 1+1){ + const List& list = CAST(List&, args[1]); + if(list.size() != 9) vm->TypeError("Mat3x3.__new__ takes a list of 9 floats"); + Mat3x3 mat; + for(int i=0; i<9; i++) mat.v[i] = CAST_F(list[i]); + return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), mat); + } if(args.size() == 1+9){ Mat3x3 mat; for(int i=0; i<9; i++) mat.v[i] = CAST_F(args[1+i]); - return VAR_T(PyMat3x3, mat); - } - if(args.size() == 1+1){ - List& a = CAST(List&, args[1]); - if(a.size() != 3) vm->ValueError("Mat3x3.__new__ takes 3x3 list"); - Mat3x3 mat; - for(int i=0; i<3; i++){ - List& b = CAST(List&, a[i]); - if(b.size() != 3) vm->ValueError("Mat3x3.__new__ takes 3x3 list"); - for(int j=0; j<3; j++){ - mat.m[i][j] = CAST_F(b[j]); - } - } - return VAR_T(PyMat3x3, mat); + return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), mat); } vm->TypeError(fmt("Mat3x3.__new__ takes 0 or 1 or 9 arguments, got ", args.size()-1)); return vm->None; @@ -424,28 +418,32 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, PyVec2& currentVelocity, float return VAR_T(PyMat3x3, ret); }); - vm->bind_func<0>(type, "zeros", [](VM* vm, ArgsView args){ + // @staticmethod + vm->bind(type, "zeros()", [](VM* vm, ArgsView args){ PK_UNUSED(args); return VAR_T(PyMat3x3, Mat3x3::zeros()); - }); + }, {}, BindType::STATICMETHOD); - vm->bind_func<0>(type, "ones", [](VM* vm, ArgsView args){ + // @staticmethod + vm->bind(type, "ones()", [](VM* vm, ArgsView args){ PK_UNUSED(args); return VAR_T(PyMat3x3, Mat3x3::ones()); - }); + }, {}, BindType::STATICMETHOD); - vm->bind_func<0>(type, "identity", [](VM* vm, ArgsView args){ + // @staticmethod + vm->bind(type, "identity()", [](VM* vm, ArgsView args){ PK_UNUSED(args); return VAR_T(PyMat3x3, Mat3x3::identity()); - }); + }, {}, BindType::STATICMETHOD); /*************** affine transformations ***************/ - vm->bind_func<3>(type, "trs", [](VM* vm, ArgsView args){ + // @staticmethod + vm->bind(type, "trs(t: vec2, r: float, s: vec2)", [](VM* vm, ArgsView args){ PyVec2& t = CAST(PyVec2&, args[0]); f64 r = CAST_F(args[1]); PyVec2& s = CAST(PyVec2&, args[2]); return VAR_T(PyMat3x3, Mat3x3::trs(t, r, s)); - }); + }, {}, BindType::STATICMETHOD); vm->bind_method<0>(type, "is_affine", [](VM* vm, ArgsView args){ PyMat3x3& self = _CAST(PyMat3x3&, args[0]); diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index 5d585500..fce1ad77 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -76,6 +76,18 @@ void init_builtins(VM* _vm) { return vm->heap.gcnew(vm->tp_super, self_arg, vm->_all_types[type].base); }); + _vm->bind_builtin_func<1>("staticmethod", [](VM* vm, ArgsView args) { + PyObject* func = args[0]; + vm->check_non_tagged_type(func, vm->tp_function); + return vm->heap.gcnew(vm->tp_staticmethod, args[0]); + }); + + _vm->bind_builtin_func<1>("classmethod", [](VM* vm, ArgsView args) { + PyObject* func = args[0]; + vm->check_non_tagged_type(func, vm->tp_function); + return vm->heap.gcnew(vm->tp_classmethod, args[0]); + }); + _vm->bind_builtin_func<2>("isinstance", [](VM* vm, ArgsView args) { if(is_non_tagged_type(args[1], vm->tp_tuple)){ Tuple& types = _CAST(Tuple&, args[1]); @@ -90,6 +102,12 @@ void init_builtins(VM* _vm) { return VAR(vm->isinstance(args[0], type)); }); + _vm->bind_builtin_func<2>("issubclass", [](VM* vm, ArgsView args) { + vm->check_non_tagged_type(args[0], vm->tp_type); + vm->check_non_tagged_type(args[1], vm->tp_type); + return VAR(vm->issubclass(PK_OBJ_GET(Type, args[0]), PK_OBJ_GET(Type, args[1]))); + }); + _vm->bind_builtin_func<0>("globals", [](VM* vm, ArgsView args) { PyObject* mod = vm->top_frame()->_module; return VAR(MappingProxy(mod)); @@ -124,11 +142,12 @@ void init_builtins(VM* _vm) { _vm->bind_builtin_func<1>("callable", [](VM* vm, ArgsView args) { PyObject* cls = vm->_t(args[0]); - Type t = PK_OBJ_GET(Type, cls); - if(t == vm->tp_function) return vm->True; - if(t == vm->tp_native_func) return vm->True; - if(t == vm->tp_bound_method) return vm->True; - if(t == vm->tp_type) return vm->True; + switch(PK_OBJ_GET(Type, cls).index){ + case VM::tp_function.index: return vm->True; + case VM::tp_native_func.index: return vm->True; + case VM::tp_bound_method.index: return vm->True; + case VM::tp_type.index: return vm->True; + } bool ok = vm->find_name_in_mro(cls, __call__) != nullptr; return VAR(ok); }); @@ -217,9 +236,15 @@ void init_builtins(VM* _vm) { return vm->None; }); - _vm->bind_builtin_func<2>("getattr", [](VM* vm, ArgsView args) { - const Str& name = CAST(Str&, args[1]); - return vm->getattr(args[0], name); + _vm->bind_builtin_func<-1>("getattr", [](VM* vm, ArgsView args) { + if(args.size()!=2 && args.size()!=3) vm->TypeError("getattr() takes 2 or 3 arguments"); + StrName name = CAST(Str&, args[1]); + PyObject* val = vm->getattr(args[0], name, false); + if(val == nullptr){ + if(args.size()==2) vm->AttributeError(args[0], name); + return args[2]; + } + return val; }); _vm->bind_builtin_func<2>("delattr", [](VM* vm, ArgsView args) { @@ -1277,20 +1302,12 @@ void init_builtins(VM* _vm) { return VAR(func.decl->signature); }); - // _vm->bind_property(_vm->_t(_vm->tp_function), "__call__", [](VM* vm, ArgsView args) { - // return args[0]; - // }); - _vm->bind_property(_vm->_t(_vm->tp_native_func), "__signature__", [](VM* vm, ArgsView args) { NativeFunc& func = _CAST(NativeFunc&, args[0]); if(func.decl != nullptr) return VAR(func.decl->signature); return VAR(""); }); - // _vm->bind_property(_vm->_t(_vm->tp_native_func), "__call__", [](VM* vm, ArgsView args) { - // return args[0]; - // }); - // Exception _vm->bind_constructor<-1>("Exception", [](VM* vm, ArgsView args){ Type cls = PK_OBJ_GET(Type, args[0]); @@ -1647,7 +1664,9 @@ void VM::post_init(){ bind__eq__(tp_bound_method, [](VM* vm, PyObject* lhs, PyObject* rhs){ if(!is_non_tagged_type(rhs, vm->tp_bound_method)) return vm->NotImplemented; - return VAR(_CAST(BoundMethod&, lhs) == _CAST(BoundMethod&, rhs)); + const BoundMethod& _0 = PK_OBJ_GET(BoundMethod, lhs); + const BoundMethod& _1 = PK_OBJ_GET(BoundMethod, rhs); + return VAR(_0.self == _1.self && _0.func == _1.func); }); bind_property(_t(tp_slice), "start", [](VM* vm, ArgsView args){ diff --git a/src/vm.cpp b/src/vm.cpp index 25d41190..8b151c50 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -705,28 +705,31 @@ void VM::init_builtin_types(){ _all_types.push_back({heap._new(Type(1), Type(0)), -1, nullptr, "object", true}); _all_types.push_back({heap._new(Type(1), Type(1)), 0, nullptr, "type", false}); - PK_ASSERT(tp_int == _new_type_object("int")); - PK_ASSERT(tp_float == _new_type_object("float")); + if(tp_int != _new_type_object("int")) exit(-3); + if((tp_float != _new_type_object("float"))) exit(-3); - PK_ASSERT(tp_bool == _new_type_object("bool")); - PK_ASSERT(tp_str == _new_type_object("str")); - PK_ASSERT(tp_list == _new_type_object("list")); - PK_ASSERT(tp_tuple == _new_type_object("tuple")); + if(tp_bool != _new_type_object("bool")) exit(-3); + if(tp_str != _new_type_object("str")) exit(-3); + if(tp_list != _new_type_object("list")) exit(-3); + if(tp_tuple != _new_type_object("tuple")) exit(-3); - PK_ASSERT(tp_slice == _new_type_object("slice")); - PK_ASSERT(tp_range == _new_type_object("range")); - PK_ASSERT(tp_module == _new_type_object("module")); - PK_ASSERT(tp_function == _new_type_object("function")); - PK_ASSERT(tp_native_func == _new_type_object("native_func")); - PK_ASSERT(tp_bound_method == _new_type_object("bound_method")); + if(tp_slice != _new_type_object("slice")) exit(-3); + if(tp_range != _new_type_object("range")) exit(-3); + if(tp_module != _new_type_object("module")) exit(-3); + if(tp_function != _new_type_object("function")) exit(-3); + if(tp_native_func != _new_type_object("native_func")) exit(-3); + if(tp_bound_method != _new_type_object("bound_method")) exit(-3); - PK_ASSERT(tp_super == _new_type_object("super")); - PK_ASSERT(tp_exception == _new_type_object("Exception", 0, true)); - PK_ASSERT(tp_bytes == _new_type_object("bytes")); - PK_ASSERT(tp_mappingproxy == _new_type_object("mappingproxy")); - PK_ASSERT(tp_dict == _new_type_object("dict")); - PK_ASSERT(tp_property == _new_type_object("property")); - PK_ASSERT(tp_star_wrapper == _new_type_object("_star_wrapper")); + if(tp_super != _new_type_object("super")) exit(-3); + if(tp_exception != _new_type_object("Exception", 0, true)) exit(-3); + if(tp_bytes != _new_type_object("bytes")) exit(-3); + if(tp_mappingproxy != _new_type_object("mappingproxy")) exit(-3); + if(tp_dict != _new_type_object("dict")) exit(-3); + if(tp_property != _new_type_object("property")) exit(-3); + if(tp_star_wrapper != _new_type_object("_star_wrapper")) exit(-3); + + if(tp_staticmethod != _new_type_object("staticmethod")) exit(-3); + if(tp_classmethod != _new_type_object("classmethod")) exit(-3); this->None = heap._new(_new_type_object("NoneType")); this->NotImplemented = heap._new(_new_type_object("NotImplementedType")); @@ -866,7 +869,7 @@ PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ // handle boundmethod, do a patch if(is_non_tagged_type(callable, tp_bound_method)){ if(method_call) PK_FATAL_ERROR(); - auto& bm = CAST(BoundMethod&, callable); + BoundMethod& bm = PK_OBJ_GET(BoundMethod, callable); callable = bm.func; // get unbound method p1[-(ARGC + 2)] = bm.func; p1[-(ARGC + 1)] = bm.self; @@ -997,7 +1000,7 @@ __FAST_CALL: return vectorcall(ARGC, KWARGC, false); } TypeError(OBJ_NAME(_t(callable)).escape() + " object is not callable"); - return nullptr; + PK_UNREACHABLE() } void VM::delattr(PyObject *_0, StrName _name){ @@ -1020,19 +1023,38 @@ PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err){ if(cls_var != nullptr){ // handle descriptor if(is_non_tagged_type(cls_var, tp_property)){ - const Property& prop = _CAST(Property&, cls_var); + const Property& prop = PK_OBJ_GET(Property, cls_var); return call(prop.getter, obj); } } // handle instance __dict__ if(!is_tagged(obj) && obj->is_attr_valid()){ - PyObject* val = obj->attr().try_get_likely_found(name); - if(val != nullptr) return val; + PyObject* val; + if(obj->type == tp_type){ + val = find_name_in_mro(obj, name); + }else{ + val = obj->attr().try_get_likely_found(name); + } + if(val != nullptr){ + if(is_tagged(val)) return val; + if(val->type == tp_staticmethod) return PK_OBJ_GET(StaticMethod, val).func; + if(val->type == tp_classmethod) return VAR(BoundMethod(obj, PK_OBJ_GET(ClassMethod, val).func)); + return val; + } } if(cls_var != nullptr){ // bound method is non-data descriptor - if(is_non_tagged_type(cls_var, tp_function) || is_non_tagged_type(cls_var, tp_native_func)){ - return VAR(BoundMethod(obj, cls_var)); + if(!is_tagged(cls_var)){ + switch(cls_var->type){ + case tp_function.index: + return VAR(BoundMethod(obj, cls_var)); + case tp_native_func.index: + return VAR(BoundMethod(obj, cls_var)); + case tp_staticmethod.index: + return PK_OBJ_GET(StaticMethod, cls_var).func; + case tp_classmethod.index: + return VAR(BoundMethod(objtype, PK_OBJ_GET(ClassMethod, cls_var).func)); + } } return cls_var; } @@ -1070,20 +1092,43 @@ PyObject* VM::get_unbound_method(PyObject* obj, StrName name, PyObject** self, b if(cls_var != nullptr){ // handle descriptor if(is_non_tagged_type(cls_var, tp_property)){ - const Property& prop = _CAST(Property&, cls_var); + const Property& prop = PK_OBJ_GET(Property, cls_var); return call(prop.getter, obj); } } // handle instance __dict__ if(!is_tagged(obj) && obj->is_attr_valid()){ - PyObject* val = obj->attr().try_get(name); - if(val != nullptr) return val; + PyObject* val; + if(obj->type == tp_type){ + val = find_name_in_mro(obj, name); + }else{ + val = obj->attr().try_get_likely_found(name); + } + if(val != nullptr){ + if(is_tagged(val)) return val; + if(val->type == tp_staticmethod) return PK_OBJ_GET(StaticMethod, val).func; + if(val->type == tp_classmethod) return VAR(BoundMethod(obj, PK_OBJ_GET(ClassMethod, val).func)); + return val; + } } } if(cls_var != nullptr){ - if(is_non_tagged_type(cls_var, tp_function) || is_non_tagged_type(cls_var, tp_native_func)){ - *self = obj; + if(!is_tagged(cls_var)){ + switch(cls_var->type){ + case tp_function.index: + *self = obj; + break; + case tp_native_func.index: + *self = obj; + break; + case tp_staticmethod.index: + *self = PY_NULL; + return PK_OBJ_GET(StaticMethod, cls_var).func; + case tp_classmethod.index: + *self = objtype; + return PK_OBJ_GET(ClassMethod, cls_var).func; + } } return cls_var; } @@ -1119,11 +1164,11 @@ void VM::setattr(PyObject* obj, StrName name, PyObject* value){ obj->attr().set(name, value); } -PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn, UserData userdata){ - return bind(obj, sig, nullptr, fn, userdata); +PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn, UserData userdata, BindType bt){ + return bind(obj, sig, nullptr, fn, userdata, bt); } -PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn, UserData userdata){ +PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn, UserData userdata, BindType bt){ CodeObject_ co; try{ // fn(a, b, *c, d=1) -> None @@ -1141,6 +1186,17 @@ PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Native } PyObject* f_obj = VAR(NativeFunc(fn, decl)); PK_OBJ_GET(NativeFunc, f_obj).set_userdata(userdata); + + switch(bt){ + case BindType::STATICMETHOD: + f_obj = VAR(StaticMethod(f_obj)); + break; + case BindType::CLASSMETHOD: + f_obj = VAR(ClassMethod(f_obj)); + break; + case BindType::DEFAULT: + break; + } if(obj != nullptr) obj->attr().set(decl->code->name, f_obj); return f_obj; } diff --git a/tests/40_class_ex.py b/tests/40_class_ex.py index 3877fc04..63c84b51 100644 --- a/tests/40_class_ex.py +++ b/tests/40_class_ex.py @@ -126,3 +126,35 @@ else: pass assert TrueClass + +# staticmethod and classmethod +class A(): + dd = 2 + + def __init__(self): + self.a = 1 + + @staticmethod + def static_method(txt): + return txt + + @classmethod + def class_method(cls, txt): + return cls.__name__ + txt + +assert A.static_method(123) == 123 +assert A.class_method('123') == 'A123' +assert A().static_method(123) == 123 +assert A().class_method('123') == 'A123' +assert A.dd == 2 +assert A().dd == 2 + +class B(A): pass + +assert B.dd == 2 +assert B().dd == 2 + +assert B.static_method(123) == 123 +assert B.class_method('123') == 'B123' +assert B().static_method(123) == 123 +assert B().class_method('123') == 'B123' diff --git a/tests/47_reflection.py b/tests/47_reflection.py index a48c364f..7140a853 100644 --- a/tests/47_reflection.py +++ b/tests/47_reflection.py @@ -13,4 +13,12 @@ assert getattr(1, '__add__')(2) == 3 a = object() setattr(a, 'b', 1) assert a.b == 1 -assert getattr(a, 'b') == 1 \ No newline at end of file +assert getattr(a, 'b') == 1 + +try: + getattr(a, 'xxx') + exit(1) +except AttributeError: + pass + +assert getattr(a, 'xxx', 1) == 1 diff --git a/tests/80_linalg.py b/tests/80_linalg.py index f3b6c4ff..08db4665 100644 --- a/tests/80_linalg.py +++ b/tests/80_linalg.py @@ -185,21 +185,21 @@ def row_operation(matrix, target_row, source_row, scale): # 生成随机测试目标 min_num = -10.0 max_num = 10.0 -test_mat = mat3x3([[random.uniform(min_num, max_num) for _ in range(3)] for _ in range(3)]) -static_test_mat_float= mat3x3([ - [7.264189733952545, -5.432187523625671, 1.8765304152872613], - [-2.4910524352374734, 8.989660807513068, -0.7168824333280513], - [9.558042327611506, -3.336280256662496, 4.951381528057387]] - ) +test_mat = mat3x3([random.uniform(min_num, max_num) for _ in range(9)]) +static_test_mat_float= mat3x3( + 7.264189733952545, -5.432187523625671, 1.8765304152872613, + -2.4910524352374734, 8.989660807513068, -0.7168824333280513, + 9.558042327611506, -3.336280256662496, 4.951381528057387 +) -static_test_mat_float_inv = mat3x3([[ 0.32265243, 0.15808159, -0.09939472], - [ 0.04199553, 0.13813096, 0.00408326], - [-0.59454451, -0.21208362, 0.39658464]]) +static_test_mat_float_inv = mat3x3( 0.32265243, 0.15808159, -0.09939472, + 0.04199553, 0.13813096, 0.00408326, + -0.59454451, -0.21208362, 0.39658464) static_test_mat_int = mat3x3([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9]] + 1, 2, 3, + 4, 5, 6, + 7, 8, 9] ) # test incorrect number of parameters is passed @@ -236,17 +236,19 @@ assert test_mat == test_mat_copy # test setzeros test_mat_copy = test_mat.copy() test_mat_copy.set_zeros() -assert test_mat_copy == mat3x3([[0,0,0],[0,0,0],[0,0,0]]) +assert test_mat_copy == mat3x3.zeros() # test set_ones test_mat_copy = test_mat.copy() test_mat_copy.set_ones() -assert test_mat_copy == mat3x3([[1,1,1],[1,1,1],[1,1,1]]) +assert test_mat_copy == mat3x3.ones() # test set_identity test_mat_copy = test_mat.copy() test_mat_copy.set_identity() -assert test_mat_copy == mat3x3([[1, 0, 0],[0, 1, 0],[0, 0, 1]]) +assert test_mat_copy == mat3x3([1, 0, 0, + 0, 1, 0, + 0, 0, 1]) # test __getitem__ for i, element in enumerate([getattr(test_mat, e) for e in element_name_list]): @@ -268,7 +270,9 @@ except: test_mat_copy = test_mat.copy() for i, element in enumerate([getattr(test_mat_copy, e) for e in element_name_list]): test_mat_copy[int(i/3), i%3] = list(range(9))[i] -assert test_mat_copy == mat3x3([[0,1,2], [3,4,5], [6,7,8]]) +assert test_mat_copy == mat3x3([0,1,2, + 3,4,5, + 6,7,8]) try: test_mat[1,2,3] = 1 @@ -380,25 +384,19 @@ assert test_mat_copy.transpose() == test_mat_copy.transpose().transpose().transp assert ~static_test_mat_float == static_test_mat_float_inv try: - mat3x3([[1, 2, 3], [2, 4, 6], [3, 6, 9]]).inverse() + ~mat3x3([1, 2, 3, 2, 4, 6, 3, 6, 9]) raise Exception('未能拦截错误 ValueError("matrix is not invertible") 在 test_mat_copy 的行列式为0') -except: - pass - -try: - ~mat3x3([[1, 2, 3], [2, 4, 6], [3, 6, 9]]) - raise Exception('未能拦截错误 ValueError("matrix is not invertible") 在 test_mat_copy 的行列式为0') -except: +except ValueError: pass # test zeros -assert mat3x3([[0 for _ in range(3)] for _ in range(3)]) == mat3x3.zeros() +assert mat3x3([0 for _ in range(9)]) == mat3x3.zeros() # test ones -assert mat3x3([[1 for _ in range(3)] for _ in range(3)]) == mat3x3.ones() +assert mat3x3([1 for _ in range(9)]) == mat3x3.ones() # test identity -assert mat3x3([[1,0,0],[0,1,0],[0,0,1]]) == mat3x3.identity() +assert mat3x3([1,0,0,0,1,0,0,0,1]) == mat3x3.identity() # test affine transformations----------------------------------------------- @@ -470,4 +468,14 @@ assert b.sizeof() == 8 assert vec2.from_struct(b) == a val = vec2.angle(vec2(-1, 0), vec2(0, -1)) -assert 1.57 < val < 1.58 \ No newline at end of file +assert 1.57 < val < 1.58 + +# test about staticmethod +class mymat3x3(mat3x3): + def f(self): + _0 = self.zeros() + _1 = super().zeros() + _2 = mat3x3.zeros() + return _0 == _1 == _2 + +assert mymat3x3().f() diff --git a/tests/99_builtin_func.py b/tests/99_builtin_func.py index e9a55cf4..cb6c6d1a 100644 --- a/tests/99_builtin_func.py +++ b/tests/99_builtin_func.py @@ -95,22 +95,6 @@ try: except: pass - -class A(): - def __init__(self): - self.a = 1 - - @staticmethod - def static_method(txt): - return txt - - # @classmethod - # def class_method(cls, txt): - # return cls.__name__ + txt - -assert A.static_method(123) == 123 -# assert A.class_method(123) == 'A123' - # 无法测试 ----------------------------------------------- # 248: 192: _vm->bind_builtin_func<1>("__import__", [](VM* vm, ArgsView args) { # 67: 193: const Str& name = CAST(Str&, args[0]);