ソースを参照

fix https://github.com/blueloveTH/pocketpy/issues/174

blueloveTH 2 年 前
コミット
7a386fa218

+ 1 - 1
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: ...

+ 1 - 1
include/pocketpy/codeobject.h

@@ -108,7 +108,7 @@ struct FuncDecl {
 };
 
 struct UserData{
-    char data[15];
+    char data[12];
     bool empty;
 
     UserData(): empty(true) {}

+ 1 - 1
include/pocketpy/common.h

@@ -21,7 +21,7 @@
 #include <random>
 #include <deque>
 
-#define PK_VERSION				"1.3.7"
+#define PK_VERSION				"1.3.8"
 
 #include "config.h"
 #include "export.h"

+ 36 - 7
include/pocketpy/obj.h

@@ -15,17 +15,26 @@ using NativeFuncC = std::function<PyObject*(VM*, ArgsView)>;
 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_<StarWrapper> final: PyObject {
     void* _value_ptr() override { return &_value; }
 };
 
+template<>
+struct Py_<StaticMethod> 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_<ClassMethod> 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_<Property> final: PyObject {
     Property _value;

+ 5 - 2
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
 

+ 1 - 1
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: ...

+ 0 - 21
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)

+ 1 - 9
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)

+ 26 - 28
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<PyVec2>(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<PyVec3>(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<PyVec4>(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+9){
+            if(args.size() == 1+0) return vm->heap.gcnew<PyMat3x3>(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(args[1+i]);
-                return VAR_T(PyMat3x3, mat);
+                for(int i=0; i<9; i++) mat.v[i] = CAST_F(list[i]);
+                return vm->heap.gcnew<PyMat3x3>(PK_OBJ_GET(Type, args[0]), mat);
             }
-            if(args.size() == 1+1){
-                List& a = CAST(List&, args[1]);
-                if(a.size() != 3) vm->ValueError("Mat3x3.__new__ takes 3x3 list");
+            if(args.size() == 1+9){
                 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);
+                for(int i=0; i<9; i++) mat.v[i] = CAST_F(args[1+i]);
+                return vm->heap.gcnew<PyMat3x3>(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]);

+ 36 - 17
src/pocketpy.cpp

@@ -76,6 +76,18 @@ void init_builtins(VM* _vm) {
         return vm->heap.gcnew<Super>(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<StaticMethod>(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<ClassMethod>(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){

+ 93 - 37
src/vm.cpp

@@ -705,28 +705,31 @@ void VM::init_builtin_types(){
     _all_types.push_back({heap._new<Type>(Type(1), Type(0)), -1, nullptr, "object", true});
     _all_types.push_back({heap._new<Type>(Type(1), Type(1)), 0, nullptr, "type", false});
 
-    PK_ASSERT(tp_int == _new_type_object("int"));
-    PK_ASSERT(tp_float == _new_type_object("float"));
-
-    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"));
-
-    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"));
-
-    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_int != _new_type_object("int")) exit(-3);
+    if((tp_float != _new_type_object("float"))) exit(-3);
+
+    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);
+
+    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);
+
+    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<Dummy>(_new_type_object("NoneType"));
     this->NotImplemented = heap._new<Dummy>(_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;
 }

+ 32 - 0
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'

+ 9 - 1
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
+assert getattr(a, 'b') == 1
+
+try:
+    getattr(a, 'xxx')
+    exit(1)
+except AttributeError:
+    pass
+
+assert getattr(a, 'xxx', 1) == 1

+ 36 - 28
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()
-    raise Exception('未能拦截错误 ValueError("matrix is not invertible") 在 test_mat_copy 的行列式为0')
-except:
-    pass
-
-try:
-    ~mat3x3([[1, 2, 3], [2, 4, 6], [3, 6, 9]])
+    ~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
+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()

+ 0 - 16
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]);