blueloveTH 2024-01-14 14:19:31 +08:00
parent 094fac8f72
commit 7a386fa218
15 changed files with 275 additions and 167 deletions

View File

@ -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: ...

View File

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

View File

@ -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"

View File

@ -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;

View File

@ -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

View File

@ -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: ...

View File

@ -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)

View File

@ -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)

View File

@ -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+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(list[i]);
return vm->heap.gcnew<PyMat3x3>(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<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]);

View File

@ -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){

View File

@ -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"));
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<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;
}

View File

@ -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'

View File

@ -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

View File

@ -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
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()

View File

@ -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]);