diff --git a/src/pocketpy.h b/src/pocketpy.h index 43f597ad..a1d3f3ac 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -204,8 +204,10 @@ inline void init_builtins(VM* _vm) { _vm->bind_method<1>("object", "__eq__", CPP_LAMBDA(VAR(args[0] == args[1]))); _vm->bind_method<1>("object", "__ne__", CPP_LAMBDA(VAR(args[0] != args[1]))); - _vm->bind_static_method<1>("type", "__new__", CPP_LAMBDA(vm->_t(args[0]))); - _vm->bind_static_method<-1>("range", "__new__", [](VM* vm, ArgsView args) { + _vm->bind_constructor<2>("type", CPP_LAMBDA(vm->_t(args[1]))); + + _vm->bind_constructor<-1>("range", [](VM* vm, ArgsView args) { + args._begin += 1; // skip cls Range r; switch (args.size()) { case 1: r.stop = CAST(i64, args[0]); break; @@ -258,12 +260,12 @@ inline void init_builtins(VM* _vm) { _vm->bind_method<1>("float", "__pow__", py_number_pow); /************ PyInt ************/ - _vm->bind_static_method<1>("int", "__new__", [](VM* vm, ArgsView args) { - if (is_type(args[0], vm->tp_float)) return VAR((i64)CAST(f64, args[0])); - if (is_type(args[0], vm->tp_int)) return args[0]; - if (is_type(args[0], vm->tp_bool)) return VAR(_CAST(bool, args[0]) ? 1 : 0); - if (is_type(args[0], vm->tp_str)) { - const Str& s = CAST(Str&, args[0]); + _vm->bind_constructor<2>("int", [](VM* vm, ArgsView args) { + if (is_type(args[1], vm->tp_float)) return VAR((i64)CAST(f64, args[1])); + if (is_type(args[1], vm->tp_int)) return args[1]; + if (is_type(args[1], vm->tp_bool)) return VAR(_CAST(bool, args[1]) ? 1 : 0); + if (is_type(args[1], vm->tp_str)) { + const Str& s = CAST(Str&, args[1]); try{ size_t parsed = 0; i64 val = Number::stoi(s.str(), &parsed, 10); @@ -304,12 +306,12 @@ inline void init_builtins(VM* _vm) { #undef INT_BITWISE_OP /************ PyFloat ************/ - _vm->bind_static_method<1>("float", "__new__", [](VM* vm, ArgsView args) { - if (is_type(args[0], vm->tp_int)) return VAR((f64)CAST(i64, args[0])); - if (is_type(args[0], vm->tp_float)) return args[0]; - if (is_type(args[0], vm->tp_bool)) return VAR(_CAST(bool, args[0]) ? 1.0 : 0.0); - if (is_type(args[0], vm->tp_str)) { - const Str& s = CAST(Str&, args[0]); + _vm->bind_constructor<2>("float", [](VM* vm, ArgsView args) { + if (is_type(args[1], vm->tp_int)) return VAR((f64)CAST(i64, args[1])); + if (is_type(args[1], vm->tp_float)) return args[1]; + if (is_type(args[1], vm->tp_bool)) return VAR(_CAST(bool, args[1]) ? 1.0 : 0.0); + if (is_type(args[1], vm->tp_str)) { + const Str& s = CAST(Str&, args[1]); if(s == "inf") return VAR(INFINITY); if(s == "-inf") return VAR(-INFINITY); try{ @@ -340,7 +342,7 @@ inline void init_builtins(VM* _vm) { }); /************ PyString ************/ - _vm->bind_static_method<1>("str", "__new__", CPP_LAMBDA(vm->asStr(args[0]))); + _vm->bind_constructor<2>("str", CPP_LAMBDA(vm->asStr(args[1]))); _vm->bind_method<1>("str", "__add__", [](VM* vm, ArgsView args) { const Str& lhs = _CAST(Str&, args[0]); @@ -453,16 +455,21 @@ inline void init_builtins(VM* _vm) { _vm->bind_method<1>("str", "join", [](VM* vm, ArgsView args) { const Str& self = _CAST(Str&, args[0]); FastStrStream ss; - PyObject* obj = vm->asList(args[1]); - const List& list = CAST(List&, obj); - for (int i = 0; i < list.size(); ++i) { - if (i > 0) ss << self; - ss << CAST(Str&, list[i]); + PyObject* it = vm->asIter(args[1]); + PyObject* obj = vm->PyIterNext(it); + while(obj != vm->StopIteration){ + if(!ss.empty()) ss << self; + ss << CAST(Str&, obj); + obj = vm->PyIterNext(it); } return VAR(ss.str()); }); /************ PyList ************/ + _vm->bind_constructor<2>("list", [](VM* vm, ArgsView args) { + return vm->asList(args[1]); + }); + _vm->bind_method<1>("list", "append", [](VM* vm, ArgsView args) { List& self = _CAST(List&, args[0]); self.push_back(args[1]); @@ -471,9 +478,12 @@ inline void init_builtins(VM* _vm) { _vm->bind_method<1>("list", "extend", [](VM* vm, ArgsView args) { List& self = _CAST(List&, args[0]); - PyObject* obj = vm->asList(args[1]); - const List& list = CAST(List&, obj); - self.extend(list); + PyObject* it = vm->asIter(args[1]); + PyObject* obj = vm->PyIterNext(it); + while(obj != vm->StopIteration){ + self.push_back(obj); + obj = vm->PyIterNext(it); + } return vm->None; }); @@ -560,8 +570,8 @@ inline void init_builtins(VM* _vm) { }); /************ PyTuple ************/ - _vm->bind_static_method<1>("tuple", "__new__", [](VM* vm, ArgsView args) { - List list = CAST(List, vm->asList(args[0])); + _vm->bind_constructor<2>("tuple", [](VM* vm, ArgsView args) { + List list = CAST(List, vm->asList(args[1])); return VAR(Tuple(std::move(list))); }); @@ -592,7 +602,7 @@ inline void init_builtins(VM* _vm) { }); /************ bool ************/ - _vm->bind_static_method<1>("bool", "__new__", CPP_LAMBDA(VAR(vm->asBool(args[0])))); + _vm->bind_constructor<2>("bool", CPP_LAMBDA(VAR(vm->asBool(args[1])))); _vm->bind_method<0>("bool", "__repr__", [](VM* vm, ArgsView args) { bool val = _CAST(bool, args[0]); @@ -613,8 +623,8 @@ inline void init_builtins(VM* _vm) { _vm->bind_method<0>("ellipsis", "__repr__", CPP_LAMBDA(VAR("Ellipsis"))); /************ bytes ************/ - _vm->bind_static_method<1>("bytes", "__new__", [](VM* vm, ArgsView args){ - List& list = CAST(List&, args[0]); + _vm->bind_constructor<2>("bytes", [](VM* vm, ArgsView args){ + List& list = CAST(List&, args[1]); std::vector buffer(list.size()); for(int i=0; ibind_static_method<3>("slice", "__new__", [](VM* vm, ArgsView args) { - return VAR(Slice(args[0], args[1], args[2])); + _vm->bind_constructor<4>("slice", [](VM* vm, ArgsView args) { + return VAR(Slice(args[1], args[2], args[3])); }); _vm->bind_method<0>("slice", "__repr__", [](VM* vm, ArgsView args) { @@ -919,7 +929,7 @@ struct Random{ } static void _register(VM* vm, PyObject* mod, PyObject* type){ - vm->bind_static_method<0>(type, "__new__", CPP_LAMBDA(VAR_T(Random))); + vm->bind_default_constructor(type); vm->bind_method<1>(type, "seed", [](VM* vm, ArgsView args) { Random& self = _CAST(Random&, args[0]); diff --git a/src/str.h b/src/str.h index 3e2507e1..50996b1f 100644 --- a/src/str.h +++ b/src/str.h @@ -359,6 +359,8 @@ struct FastStrStream{ return *this; } + bool empty() const { return parts.empty(); } + Str str() const{ int len = 0; bool is_ascii = true; diff --git a/src/vm.h b/src/vm.h index ead7b57b..dc87394c 100644 --- a/src/vm.h +++ b/src/vm.h @@ -64,6 +64,7 @@ struct PyTypeInfo{ PyObject* obj; Type base; Str name; + bool subclass_enabled; }; struct FrameId{ @@ -142,18 +143,12 @@ public: return nullptr; } - PyObject* asList(PyObject* it){ - if(is_non_tagged_type(it, tp_list)) return it; - return call(_t(tp_list), it); - } - PyObject* find_name_in_mro(PyObject* cls, StrName name){ PyObject* val; do{ val = cls->attr().try_get(name); if(val != nullptr) return val; - Type cls_t = OBJ_GET(Type, cls); - Type base = _all_types[cls_t].base; + Type base = _all_types[OBJ_GET(Type, cls)].base; if(base.index == -1) break; cls = _all_types[base].obj; }while(true); @@ -242,12 +237,17 @@ public: return call(p, _0, _1); } - PyObject* new_type_object(PyObject* mod, StrName name, Type base){ + PyObject* new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled=true){ PyObject* obj = heap._new(tp_type, _all_types.size()); + const PyTypeInfo& base_info = _all_types[base]; + if(!base_info.subclass_enabled){ + TypeError(fmt("type ", base_info.name.escape(), " is not `subclass_enabled`")); + } PyTypeInfo info{ obj, base, - (mod!=nullptr && mod!=builtins) ? Str(OBJ_NAME(mod)+"."+name.sv()): name.sv() + (mod!=nullptr && mod!=builtins) ? Str(OBJ_NAME(mod)+"."+name.sv()): name.sv(), + subclass_enabled, }; if(mod != nullptr) mod->attr().set(name, obj); _all_types.push_back(info); @@ -255,7 +255,7 @@ public: } Type _new_type_object(StrName name, Type base=0) { - PyObject* obj = new_type_object(nullptr, name, base); + PyObject* obj = new_type_object(nullptr, name, base, false); return OBJ_GET(Type, obj); } @@ -278,9 +278,18 @@ public: bind_method(_find_type(type), name, fn); } - template - void bind_static_method(Args&&... args) { - bind_func(std::forward(args)...); + template + void bind_constructor(__T&& type, NativeFuncC fn) { + static_assert(ARGC==-1 || ARGC>=1); + bind_func(std::forward<__T>(type), "__new__", fn); + } + + template + void bind_default_constructor(__T&& type) { + bind_constructor<1>(std::forward<__T>(type), [](VM* vm, ArgsView args){ + Type t = OBJ_GET(Type, args[0]); + return vm->heap.gcnew(t, T()); + }); } template @@ -382,7 +391,8 @@ public: f64 num_to_float(PyObject* obj); bool asBool(PyObject* obj); i64 hash(PyObject* obj); - PyObject* asRepr(PyObject* obj); + PyObject* asRepr(PyObject*); + PyObject* asList(PyObject*); PyObject* new_module(StrName name); Str disassemble(CodeObject_ co); void init_builtin_types(); @@ -568,6 +578,17 @@ inline bool VM::asBool(PyObject* obj){ return true; } +inline PyObject* VM::asList(PyObject* it){ + it = asIter(it); + List list; + PyObject* obj = PyIterNext(it); + while(obj != StopIteration){ + list.push_back(obj); + obj = PyIterNext(it); + } + return VAR(std::move(list)); +} + inline void VM::parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step){ auto clip = [](int value, int min, int max){ if(value < min) return min; @@ -838,8 +859,8 @@ inline void VM::_log_s_data(const char* title) { } inline void VM::init_builtin_types(){ - _all_types.push_back({heap._new(Type(1), Type(0)), -1, "object"}); - _all_types.push_back({heap._new(Type(1), Type(1)), 0, "type"}); + _all_types.push_back({heap._new(Type(1), Type(0)), -1, "object", true}); + _all_types.push_back({heap._new(Type(1), Type(1)), 0, "type", false}); tp_object = 0; tp_type = 1; tp_int = _new_type_object("int"); @@ -947,19 +968,25 @@ inline PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){ if(method_call) FATAL_ERROR(); // [type, NULL, args..., kwargs...] - // TODO: derived __new__ ? - PyObject* new_f = callable->attr().try_get(__new__); + // __new__ + const static StrName m_new("__new__"); + PyObject* new_f = find_name_in_mro(callable, m_new); PyObject* obj; if(new_f != nullptr){ PUSH(new_f); PUSH(PY_NULL); + PUSH(callable); // cls for(PyObject* obj: args) PUSH(obj); for(PyObject* obj: kwargs) PUSH(obj); - obj = vectorcall(ARGC, KWARGC); - if(!isinstance(obj, OBJ_GET(Type, callable))) return obj; + // if obj is not an instance of callable, the behavior is undefined + obj = vectorcall(ARGC+1, KWARGC); }else{ - obj = heap.gcnew(OBJ_GET(Type, callable), {}); + // fast path for object.__new__ + Type t = OBJ_GET(Type, callable); + obj= vm->heap.gcnew(t, {}); } + + // __init__ PyObject* self; callable = get_unbound_method(obj, __init__, &self, false); if (self != PY_NULL) {