From eae0aa6c8ba29cdf51ff63c5fd4583716960b778 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 5 May 2024 11:36:17 +0800 Subject: [PATCH] some fix --- docs/1_5_0.md | 10 ++++ docs/cheatsheet.md | 6 +-- docs/quick-start/misc.md | 10 ++-- include/pocketpy/vm.h | 19 +++---- src/array2d.cpp | 4 +- src/ceval.cpp | 11 ++-- src/cffi.cpp | 10 ++-- src/collections.cpp | 12 ++--- src/dataclasses.cpp | 7 ++- src/linalg.cpp | 16 +++--- src/modules.cpp | 2 +- src/pocketpy.cpp | 107 ++++++++++++++++++++------------------- src/pocketpy_c.cpp | 4 +- src/vm.cpp | 53 +++++++++++++------ tests/07_dict.py | 4 +- 15 files changed, 156 insertions(+), 119 deletions(-) diff --git a/docs/1_5_0.md b/docs/1_5_0.md index 52d02567..b7e5e44b 100644 --- a/docs/1_5_0.md +++ b/docs/1_5_0.md @@ -194,3 +194,13 @@ Enabling the profiler has a performance overhead. Only enable it when you need i + `vm->is_non_tagged_type` was removed. Use `vm->is_type` instead. + `vm->check_non_tagged_type` was removed. Use `vm->check_type` instead. + +## Python Stringify functions + +The following functions now return `Str` object instead of `PyObject*`: + ++ `vm->py_str` ++ `vm->py_repr` ++ `vm->py_json` + +Also, `vm->bind__str__` and `vm->bind__repr__` were changed to return `Str` object. diff --git a/docs/cheatsheet.md b/docs/cheatsheet.md index ddf1820c..cb0f8347 100644 --- a/docs/cheatsheet.md +++ b/docs/cheatsheet.md @@ -237,21 +237,21 @@ Convert a python object to string ```cpp PyObject* obj = py_var(vm, 123); -obj = vm->py_str(obj); // 123 +Str s = vm->py_str(obj); // 123 ``` Get the string representation of a python object ```cpp PyObject* obj = py_var(vm, "123"); -obj = vm->py_repr(obj); // "'123'" +Str s = vm->py_repr(obj); // "'123'" ``` Get the JSON representation of a python object ```cpp PyObject* obj = py_var(vm, 123); -obj = vm->py_json(obj); // "123" +Str s = vm->py_json(obj); // "123" ``` Get the hash value of a python object diff --git a/docs/quick-start/misc.md b/docs/quick-start/misc.md index 2afa491d..62fe444f 100644 --- a/docs/quick-start/misc.md +++ b/docs/quick-start/misc.md @@ -14,17 +14,18 @@ auto _lock = vm->heap.gc_scope_lock(); The scope lock is required if you create a PyObject and then try to run python-level bytecodes. -For example, you create a temporary object on the stack and then call `vm->py_str`. +For example, you create a temporary object on the stack and then call `vm->py_next`. ```cpp void some_func(VM* vm){ PyObject* obj = VAR(List(5)); // unsafe - PyObject obj_string = vm->py_str(obj); + PyObject iter = vm->py_iter(obj); + PyObject next = vm->py_next(iter); } ``` -Because users can have an overload of `__str__`, this call is unsafe. +Because users can have an overload of `__next__`, this call is unsafe. When the vm is running python-level bytecodes, gc may start and delete your temporary object. The scope lock prevents this from happening. @@ -34,6 +35,7 @@ void some_func(VM* vm){ PyObject* obj = VAR(List(5)); // safe auto _lock = vm->heap.gc_scope_lock(); - PyObject obj_string = vm->py_str(obj); + PyObject iter = vm->py_iter(obj); + PyObject next = vm->py_next(iter); } ``` diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index bb291a1b..9c4405f3 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -50,10 +50,9 @@ struct PyTypeInfo{ std::vector annotated_fields = {}; - // cached special methods // unary operators - PyObject* (*m__repr__)(VM* vm, PyObject*) = nullptr; - PyObject* (*m__str__)(VM* vm, PyObject*) = nullptr; + Str (*m__repr__)(VM* vm, PyObject*) = nullptr; + Str (*m__str__)(VM* vm, PyObject*) = nullptr; i64 (*m__hash__)(VM* vm, PyObject*) = nullptr; i64 (*m__len__)(VM* vm, PyObject*) = nullptr; PyObject* (*m__iter__)(VM* vm, PyObject*) = nullptr; @@ -183,9 +182,10 @@ public: VM(bool enable_os=true); #if PK_REGION("Python Equivalents") - PyObject* py_str(PyObject* obj); // x -> str(x) - PyObject* py_repr(PyObject* obj); // x -> repr(x) - PyObject* py_json(PyObject* obj); // x -> json.dumps(x) + Str py_str(PyObject* obj); // x -> str(x) + Str py_repr(PyObject* obj); // x -> repr(x) + Str py_json(PyObject* obj); // x -> json.dumps(x) + PyObject* py_iter(PyObject* obj); // x -> iter(x) PyObject* py_next(PyObject*); // x -> next(x) PyObject* _py_next(const PyTypeInfo*, PyObject*); // x -> next(x) with type info cache @@ -269,9 +269,10 @@ public: #endif #if PK_REGION("Magic Bindings") - void bind__repr__(Type type, PyObject* (*f)(VM*, PyObject*)); - void bind__str__(Type type, PyObject* (*f)(VM*, PyObject*)); + void bind__repr__(Type type, Str (*f)(VM*, PyObject*)); + void bind__str__(Type type, Str (*f)(VM*, PyObject*)); void bind__iter__(Type type, PyObject* (*f)(VM*, PyObject*)); + void bind__next__(Type type, unsigned (*f)(VM*, PyObject*)); [[deprecated]] void bind__next__(Type type, PyObject* (*f)(VM*, PyObject*)); void bind__neg__(Type type, PyObject* (*f)(VM*, PyObject*)); @@ -356,7 +357,7 @@ public: bool issubclass(Type cls, Type base); void check_type(PyObject* obj, Type type){ if(!is_type(obj, type)) TypeError(type, _tp(obj)); } void check_compatible_type(PyObject* obj, Type type){ if(!isinstance(obj, type)) TypeError(type, _tp(obj)); } - + Type _tp(PyObject* obj){ return is_small_int(obj) ? tp_int : obj->type; } const PyTypeInfo* _tp_info(PyObject* obj) { return &_all_types[_tp(obj)]; } const PyTypeInfo* _tp_info(Type type) { return &_all_types[type]; } diff --git a/src/array2d.cpp b/src/array2d.cpp index 98beb5a2..1ada8563 100644 --- a/src/array2d.cpp +++ b/src/array2d.cpp @@ -181,9 +181,9 @@ struct Array2d{ return (i64)self.numel; }); - vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0){ + vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0) -> Str{ Array2d& self = PK_OBJ_GET(Array2d, _0); - return VAR(_S("array2d(", self.n_cols, ", ", self.n_rows, ')')); + return _S("array2d(", self.n_cols, ", ", self.n_rows, ')'); }); vm->bind(type, "map(self, f)", [](VM* vm, ArgsView args){ diff --git a/src/ceval.cpp b/src/ceval.cpp index 4d993cba..34263d5c 100644 --- a/src/ceval.cpp +++ b/src/ceval.cpp @@ -134,7 +134,7 @@ __NEXT_STEP:; THIRD() = _0; } DISPATCH() case OP_PRINT_EXPR:{ - if(TOP() != None) stdout_write(CAST(Str&, py_repr(TOP())) + "\n"); + if(TOP() != None) stdout_write(py_repr(TOP()) + "\n"); POP(); } DISPATCH() /*****************************************/ @@ -383,7 +383,7 @@ __NEXT_STEP:; case OP_BUILD_STRING: { SStream ss; ArgsView view = STACK_VIEW(byte.arg); - for(PyObject* obj : view) ss << CAST(Str&, py_str(obj)); + for(PyObject* obj : view) ss << py_str(obj); STACK_SHRINK(byte.arg); PUSH(VAR(ss.str())); } DISPATCH() @@ -657,7 +657,7 @@ __NEXT_STEP:; PUSH(_0); } DISPATCH() case OP_REPR: - TOP() = py_repr(TOP()); + TOP() = VAR(py_repr(TOP())); DISPATCH() case OP_CALL:{ if(heap._should_auto_collect()) heap._auto_collect(); @@ -933,8 +933,9 @@ __NEXT_STEP:; } DISPATCH() case OP_RAISE_ASSERT: if(byte.arg){ - PyObject* _0 = py_str(POPX()); - AssertionError(CAST(Str, _0)); + Str msg = py_str(TOP()); + POP(); + AssertionError(msg); }else{ AssertionError(); } diff --git a/src/cffi.cpp b/src/cffi.cpp index d76f25c4..906d9678 100644 --- a/src/cffi.cpp +++ b/src/cffi.cpp @@ -14,9 +14,9 @@ namespace pkpy{ return reinterpret_cast(self.ptr); }); - vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ + vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{ VoidP& self = PK_OBJ_GET(VoidP, obj); - return VAR(_S("")); + return _S(""); }); #define BIND_CMP(name, op) \ @@ -76,7 +76,7 @@ namespace pkpy{ Struct& self = _CAST(Struct&, obj); SStream ss; ss << ""; - return VAR(ss.str()); + return ss.str(); }); vm->bind_func(type, "addr", 1, [](VM* vm, ArgsView args){ @@ -234,9 +234,9 @@ void add_module_c(VM* vm){ T* target = (T*)voidp.ptr; \ return vm->heap.gcnew(lhs->type, target - offset); \ }); \ - vm->bind__repr__(type_t, [](VM* vm, PyObject* obj){ \ + vm->bind__repr__(type_t, [](VM* vm, PyObject* obj) -> Str{ \ VoidP& self = _CAST(VoidP&, obj); \ - return VAR(_S("<", CNAME, "* at ", self.hex(), ">")); \ + return _S("<", CNAME, "* at ", self.hex(), ">"); \ }); \ BIND_PRIMITIVE(char, "char") diff --git a/src/collections.cpp b/src/collections.cpp index 9fe2c823..2a7c9b23 100644 --- a/src/collections.cpp +++ b/src/collections.cpp @@ -105,21 +105,21 @@ namespace pkpy return vm->new_user_object(_0, self.dequeItems.begin(), self.dequeItems.end()); }); - vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0) + vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0) -> Str { - if(vm->_repr_recursion_set.count(_0)) return VAR("[...]"); + if(vm->_repr_recursion_set.count(_0)) return "[...]"; const PyDeque &self = _CAST(PyDeque&, _0); SStream ss; ss << "deque(["; vm->_repr_recursion_set.insert(_0); for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it) { - ss << CAST(Str&, vm->py_repr(*it)); + ss << vm->py_repr(*it); if (it != self.dequeItems.end() - 1) ss << ", "; } vm->_repr_recursion_set.erase(_0); self.bounded ? ss << "], maxlen=" << self.maxlen << ")" : ss << "])"; - return VAR(ss.str()); + return ss.str(); }); // enables comparison between two deques, == and != are supported @@ -253,7 +253,7 @@ namespace pkpy int start = CAST_DEFAULT(int, args[2], 0); int stop = CAST_DEFAULT(int, args[3], self.dequeItems.size()); int index = self.findIndex(vm, obj, start, stop); - if (index < 0) vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in deque"); + if (index < 0) vm->ValueError(vm->py_repr(obj) + " is not in deque"); return VAR(index); }); // NEW: returns the index of the given object in the deque @@ -290,7 +290,7 @@ namespace pkpy PyObject *obj = args[1]; PyObject *removed = self.popObj(false, false, obj, vm); if (removed == nullptr) - vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in list"); + vm->ValueError(vm->py_repr(obj) + " is not in list"); return vm->None; }); // NEW: reverses the deque diff --git a/src/dataclasses.cpp b/src/dataclasses.cpp index 38d7d082..8b70580f 100644 --- a/src/dataclasses.cpp +++ b/src/dataclasses.cpp @@ -45,8 +45,7 @@ static void patch__init__(VM* vm, Type cls){ } static void patch__repr__(VM* vm, Type cls){ - vm->bind__repr__(cls, [](VM* vm, PyObject* _0){ - auto _lock = vm->heap.gc_scope_lock(); + vm->bind__repr__(cls, [](VM* vm, PyObject* _0) -> Str{ const PyTypeInfo* cls_info = &vm->_all_types[vm->_tp(_0)]; const auto& fields = cls_info->annotated_fields; const NameDict& obj_d = _0->attr(); @@ -56,10 +55,10 @@ static void patch__repr__(VM* vm, Type cls){ for(StrName field: fields){ if(first) first = false; else ss << ", "; - ss << field << "=" << CAST(Str&, vm->py_repr(obj_d[field])); + ss << field << "=" << vm->py_repr(obj_d[field]); } ss << ")"; - return VAR(ss.str()); + return ss.str(); }); } diff --git a/src/linalg.cpp b/src/linalg.cpp index 34ce0703..054f733a 100644 --- a/src/linalg.cpp +++ b/src/linalg.cpp @@ -149,12 +149,12 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s return VAR(val); }, {}, BindType::STATICMETHOD); - vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ + vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{ Vec2 self = _CAST(Vec2&, obj); SStream ss; ss.setprecision(3); ss << "vec2(" << self.x << ", " << self.y << ")"; - return VAR(ss.str()); + return ss.str(); }); vm->bind_func(type, "rotate", 2, [](VM* vm, ArgsView args){ @@ -200,12 +200,12 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), x, y, z); }); - vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ + vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{ Vec3 self = _CAST(Vec3&, obj); SStream ss; ss.setprecision(3); ss << "vec3(" << self.x << ", " << self.y << ", " << self.z << ")"; - return VAR(ss.str()); + return ss.str(); }); PY_FIELD(Vec3, "x", x) @@ -239,12 +239,12 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s return vm->heap.gcnew(PK_OBJ_GET(Type, args[0]), x, y, z, w); }); - vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ + vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{ Vec4 self = _CAST(Vec4&, obj); SStream ss; ss.setprecision(3); ss << "vec4(" << self.x << ", " << self.y << ", " << self.z << ", " << self.w << ")"; - return VAR(ss.str()); + return ss.str(); }); PY_FIELD(Vec4, "x", x) @@ -298,14 +298,14 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s return vm->None; }); - vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj){ + vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj) -> Str{ const Mat3x3& self = _CAST(Mat3x3&, obj); SStream ss; ss.setprecision(3); ss << "mat3x3([" << self._11 << ", " << self._12 << ", " << self._13 << ",\n"; ss << " " << self._21 << ", " << self._22 << ", " << self._23 << ",\n"; ss << " " << self._31 << ", " << self._32 << ", " << self._33 << "])"; - return VAR(ss.str()); + return ss.str(); }); vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* obj, PyObject* index){ diff --git a/src/modules.cpp b/src/modules.cpp index 2b4ed13e..d6f14089 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -103,7 +103,7 @@ void add_module_json(VM* vm){ }); vm->bind_func(mod, "dumps", 1, [](VM* vm, ArgsView args) { - return vm->py_json(args[0]); + return VAR(vm->py_json(args[0])); }); } diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index 658fcf67..bb1da213 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -245,7 +245,7 @@ void __init_builtins(VM* _vm) { }); _vm->bind_func(_vm->builtins, "repr", 1, [](VM* vm, ArgsView args){ - return vm->py_repr(args[0]); + return VAR(vm->py_repr(args[0])); }); _vm->bind_func(_vm->builtins, "len", 1, [](VM* vm, ArgsView args){ @@ -343,13 +343,13 @@ void __init_builtins(VM* _vm) { }); // tp_object - _vm->bind__repr__(VM::tp_object, [](VM* vm, PyObject* obj) { + _vm->bind__repr__(VM::tp_object, [](VM* vm, PyObject* obj) -> Str{ if(is_tagged(obj)) PK_FATAL_ERROR(); SStream ss; ss << "<" << _type_name(vm, vm->_tp(obj)) << " object at "; ss.write_hex(obj); ss << ">"; - return VAR(ss.str()); + return ss.str(); }); _vm->bind__eq__(VM::tp_object, [](VM* vm, PyObject* _0, PyObject* _1) { @@ -382,8 +382,8 @@ void __init_builtins(VM* _vm) { _vm->bind__iter__(VM::tp_range, [](VM* vm, PyObject* obj) { return vm->new_user_object(PK_OBJ_GET(Range, obj)); }); // tp_nonetype - _vm->bind__repr__(_vm->_tp(_vm->None), [](VM* vm, PyObject* _0) { - return VAR("None"); + _vm->bind__repr__(_vm->_tp(_vm->None), [](VM* vm, PyObject* _0) -> Str { + return "None"; }); // tp_float / tp_float @@ -475,7 +475,9 @@ void __init_builtins(VM* _vm) { return VAR(bits); }); - _vm->bind__repr__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(std::to_string(_CAST(i64, obj))); }); + _vm->bind__repr__(VM::tp_int, [](VM* vm, PyObject* obj) -> Str{ + return std::to_string(_CAST(i64, obj)); + }); _vm->bind__neg__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(-_CAST(i64, obj)); }); _vm->bind__hash__(VM::tp_int, [](VM* vm, PyObject* obj) { return _CAST(i64, obj); }); _vm->bind__invert__(VM::tp_int, [](VM* vm, PyObject* obj) { return VAR(~_CAST(i64, obj)); }); @@ -532,18 +534,18 @@ void __init_builtins(VM* _vm) { _vm->bind__neg__(VM::tp_float, [](VM* vm, PyObject* _0) { return VAR(-_CAST(f64, _0)); }); - _vm->bind__repr__(VM::tp_float, [](VM* vm, PyObject* _0) { + _vm->bind__repr__(VM::tp_float, [](VM* vm, PyObject* _0) -> Str { f64 val = _CAST(f64, _0); SStream ss; ss << val; - return VAR(ss.str()); + return ss.str(); }); // tp_str _vm->bind_func(VM::tp_str, __new__, -1, [](VM* vm, ArgsView args) { if(args.size() == 1) return VAR(Str()); if(args.size() > 2) vm->TypeError("str() takes at most 1 argument"); - return vm->py_str(args[1]); + return VAR(vm->py_str(args[1])); }); _vm->bind__hash__(VM::tp_str, [](VM* vm, PyObject* _0) { @@ -574,11 +576,12 @@ void __init_builtins(VM* _vm) { const Str& self = _CAST(Str&, _0); return VAR(self.index(CAST(Str&, _1)) != -1); }); - _vm->bind__str__(VM::tp_str, [](VM* vm, PyObject* _0) { return _0; }); + + _vm->bind_func(VM::tp_str, __str__, 1, [](VM* vm, ArgsView args) { return args[0]; }); _vm->bind__iter__(VM::tp_str, [](VM* vm, PyObject* _0) { return vm->new_user_object(_0); }); - _vm->bind__repr__(VM::tp_str, [](VM* vm, PyObject* _0) { + _vm->bind__repr__(VM::tp_str, [](VM* vm, PyObject* _0) -> Str { const Str& self = _CAST(Str&, _0); - return VAR(self.escape()); + return self.escape(); }); #define BIND_CMP_STR(name, op) \ @@ -799,36 +802,36 @@ void __init_builtins(VM* _vm) { return vm->None; }); - _vm->bind__repr__(VM::tp_list, [](VM* vm, PyObject* _0){ - if(vm->_repr_recursion_set.count(_0)) return VAR("[...]"); + _vm->bind__repr__(VM::tp_list, [](VM* vm, PyObject* _0) -> Str{ + if(vm->_repr_recursion_set.count(_0)) return "[...]"; List& iterable = _CAST(List&, _0); SStream ss; ss << '['; vm->_repr_recursion_set.insert(_0); for(int i=0; ipy_repr(iterable[i])); + ss << vm->py_repr(iterable[i]); if(i != iterable.size()-1) ss << ", "; } vm->_repr_recursion_set.erase(_0); ss << ']'; - return VAR(ss.str()); + return ss.str(); }); - _vm->bind__repr__(VM::tp_tuple, [](VM* vm, PyObject* _0){ + _vm->bind__repr__(VM::tp_tuple, [](VM* vm, PyObject* _0) -> Str{ Tuple& iterable = _CAST(Tuple&, _0); SStream ss; ss << '('; if(iterable.size() == 1){ - ss << CAST(Str&, vm->py_repr(iterable[0])); + ss << vm->py_repr(iterable[0]); ss << ','; }else{ for(int i=0; ipy_repr(iterable[i])); + ss << vm->py_repr(iterable[i]); if(i != iterable.size()-1) ss << ", "; } } ss << ')'; - return VAR(ss.str()); + return ss.str(); }); _vm->bind_func(VM::tp_list, __new__, -1, [](VM* vm, ArgsView args) { @@ -869,7 +872,7 @@ void __init_builtins(VM* _vm) { for(int i=start; ipy_eq(self[i], obj)) return VAR(i); } - vm->ValueError(_CAST(Str&, vm->py_repr(obj)) + " is not in list"); + vm->ValueError(vm->py_repr(obj) + " is not in list"); return vm->None; }); @@ -882,7 +885,7 @@ void __init_builtins(VM* _vm) { return vm->None; } } - vm->ValueError(_CAST(Str&, vm->py_repr(obj)) + " is not in list"); + vm->ValueError(vm->py_repr(obj) + " is not in list"); return vm->None; }); @@ -1075,9 +1078,9 @@ void __init_builtins(VM* _vm) { _vm->bind__hash__(VM::tp_bool, [](VM* vm, PyObject* _0) { return (i64)_CAST(bool, _0); }); - _vm->bind__repr__(VM::tp_bool, [](VM* vm, PyObject* _0) { + _vm->bind__repr__(VM::tp_bool, [](VM* vm, PyObject* _0) -> Str{ bool val = _CAST(bool, _0); - return VAR(val ? "True" : "False"); + return val ? "True" : "False"; }); _vm->bind__and__(VM::tp_bool, [](VM* vm, PyObject* _0, PyObject* _1) { @@ -1096,11 +1099,11 @@ void __init_builtins(VM* _vm) { }); // tp_ellipsis / tp_NotImplementedType - _vm->bind__repr__(_vm->_tp(_vm->Ellipsis), [](VM* vm, PyObject* _0) { - return VAR("..."); + _vm->bind__repr__(_vm->_tp(_vm->Ellipsis), [](VM* vm, PyObject* _0) -> Str{ + return "..."; }); - _vm->bind__repr__(_vm->_tp(_vm->NotImplemented), [](VM* vm, PyObject* _0) { - return VAR("NotImplemented"); + _vm->bind__repr__(_vm->_tp(_vm->NotImplemented), [](VM* vm, PyObject* _0) -> Str{ + return "NotImplemented"; }); // tp_bytes @@ -1148,7 +1151,7 @@ void __init_builtins(VM* _vm) { return (i64)std::hash()(view); }); - _vm->bind__repr__(VM::tp_bytes, [](VM* vm, PyObject* _0) { + _vm->bind__repr__(VM::tp_bytes, [](VM* vm, PyObject* _0) -> Str { const Bytes& self = _CAST(Bytes&, _0); SStream ss; ss << "b'"; @@ -1157,7 +1160,7 @@ void __init_builtins(VM* _vm) { ss.write_hex((unsigned char)self[i]); } ss << "'"; - return VAR(ss.str()); + return ss.str(); }); _vm->bind__len__(VM::tp_bytes, [](VM* vm, PyObject* _0) { return (i64)_CAST(Bytes&, _0).size(); @@ -1189,14 +1192,14 @@ void __init_builtins(VM* _vm) { return vm->True; }); - _vm->bind__repr__(VM::tp_slice, [](VM* vm, PyObject* _0) { + _vm->bind__repr__(VM::tp_slice, [](VM* vm, PyObject* _0) -> Str { const Slice& self = _CAST(Slice&, _0); SStream ss; ss << "slice("; - ss << CAST(Str, vm->py_repr(self.start)) << ", "; - ss << CAST(Str, vm->py_repr(self.stop)) << ", "; - ss << CAST(Str, vm->py_repr(self.step)) << ")"; - return VAR(ss.str()); + ss << vm->py_repr(self.start) << ", "; + ss << vm->py_repr(self.stop) << ", "; + ss << vm->py_repr(self.step) << ")"; + return ss.str(); }); // tp_mappingproxy @@ -1251,8 +1254,8 @@ void __init_builtins(VM* _vm) { return ret; }); - _vm->bind__repr__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0) { - if(vm->_repr_recursion_set.count(_0)) return VAR("{...}"); + _vm->bind__repr__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0) -> Str{ + if(vm->_repr_recursion_set.count(_0)) return "{...}"; MappingProxy& self = _CAST(MappingProxy&, _0); SStream ss; ss << "mappingproxy({"; @@ -1262,11 +1265,11 @@ void __init_builtins(VM* _vm) { if(!first) ss << ", "; first = false; ss << k.escape() << ": "; - ss << CAST(Str, vm->py_repr(v)); + ss << vm->py_repr(v); } vm->_repr_recursion_set.erase(_0); ss << "})"; - return VAR(ss.str()); + return ss.str(); }); _vm->bind__contains__(VM::tp_mappingproxy, [](VM* vm, PyObject* _0, PyObject* _1) { @@ -1412,8 +1415,8 @@ void __init_builtins(VM* _vm) { return vm->None; }); - _vm->bind__repr__(VM::tp_dict, [](VM* vm, PyObject* _0) { - if(vm->_repr_recursion_set.count(_0)) return VAR("{...}"); + _vm->bind__repr__(VM::tp_dict, [](VM* vm, PyObject* _0) -> Str{ + if(vm->_repr_recursion_set.count(_0)) return "{...}"; Dict& self = _CAST(Dict&, _0); SStream ss; ss << "{"; @@ -1422,11 +1425,11 @@ void __init_builtins(VM* _vm) { self.apply([&](PyObject* k, PyObject* v){ if(!first) ss << ", "; first = false; - ss << CAST(Str&, vm->py_repr(k)) << ": " << CAST(Str&, vm->py_repr(v)); + ss << vm->py_repr(k) << ": " << vm->py_repr(v); }); vm->_repr_recursion_set.erase(_0); ss << "}"; - return VAR(ss.str()); + return ss.str(); }); _vm->bind__eq__(VM::tp_dict, [](VM* vm, PyObject* _0, PyObject* _1) { @@ -1444,9 +1447,9 @@ void __init_builtins(VM* _vm) { return vm->True; }); - _vm->bind__repr__(VM::tp_module, [](VM* vm, PyObject* _0) { + _vm->bind__repr__(VM::tp_module, [](VM* vm, PyObject* _0) -> Str { const Str& path = CAST(Str&, _0->attr(__path__)); - return VAR(_S("")); + return _S(""); }); // tp_property @@ -1493,14 +1496,14 @@ void __init_builtins(VM* _vm) { return vm->None; }); - _vm->bind__repr__(VM::tp_exception, [](VM* vm, PyObject* _0) { + _vm->bind__repr__(VM::tp_exception, [](VM* vm, PyObject* _0) -> Str { Exception& self = _CAST(Exception&, _0); - return VAR(_S(_type_name(vm, _0->type), '(', self.msg.escape(), ')')); + return _S(_type_name(vm, _0->type), '(', self.msg.escape(), ')'); }); - _vm->bind__str__(VM::tp_exception, [](VM* vm, PyObject* _0) { + _vm->bind__str__(VM::tp_exception, [](VM* vm, PyObject* _0) -> Str{ Exception& self = _CAST(Exception&, _0); - return VAR(self.msg); + return self.msg; }); _vm->register_user_class(_vm->builtins, "_range_iter"); @@ -1533,11 +1536,11 @@ void VM::__post_init_builtin_types(){ return self; // for generics }); - bind__repr__(tp_type, [](VM* vm, PyObject* self){ + bind__repr__(tp_type, [](VM* vm, PyObject* self) -> Str{ SStream ss; const PyTypeInfo& info = vm->_all_types[PK_OBJ_GET(Type, self)]; ss << ""; - return VAR(ss.str()); + return ss.str(); }); bind_property(_t(tp_object), "__class__", PK_LAMBDA(vm->_t(args[0]))); @@ -1589,7 +1592,7 @@ void VM::__post_init_builtin_types(){ const Str& _2 = CAST(Str&, args[2]); SStream ss; for(int i=0; i<_0.size(); i++){ - ss << CAST(Str&, vm->py_str(_0[i])); + ss << vm->py_str(_0[i]); if(i != _0.size()-1) ss << _1; } ss << _2; diff --git a/src/pocketpy_c.cpp b/src/pocketpy_c.cpp index e5a33f82..bc8b794f 100644 --- a/src/pocketpy_c.cpp +++ b/src/pocketpy_c.cpp @@ -473,7 +473,7 @@ bool pkpy_py_repr(pkpy_vm* vm_handle) { PK_ASSERT_N_EXTRA_ELEMENTS(1) PyObject* item = vm->s_data.top(); PK_PROTECTED( - item = vm->py_repr(item); + item = VAR(vm->py_repr(item)); ) vm->s_data.top() = item; return true; @@ -485,7 +485,7 @@ bool pkpy_py_str(pkpy_vm* vm_handle) { PK_ASSERT_N_EXTRA_ELEMENTS(1) PyObject* item = vm->s_data.top(); PK_PROTECTED( - item = vm->py_str(item); + item = VAR(vm->py_str(item)); ) vm->s_data.top() = item; return true; diff --git a/src/vm.cpp b/src/vm.cpp index 7c9054a0..126d10f8 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -83,24 +83,34 @@ namespace pkpy{ __init_builtin_types(); } - PyObject* VM::py_str(PyObject* obj){ + Str VM::py_str(PyObject* obj){ const PyTypeInfo* ti = _tp_info(obj); if(ti->m__str__) return ti->m__str__(this, obj); PyObject* self; PyObject* f = get_unbound_method(obj, __str__, &self, false); - if(self != PY_NULL) return call_method(self, f); + if(self != PY_NULL){ + PyObject* retval = call_method(self, f); + if(!is_type(retval, tp_str)){ + throw std::runtime_error("object.__str__ must return str"); + } + return PK_OBJ_GET(Str, retval); + } return py_repr(obj); } - PyObject* VM::py_repr(PyObject* obj){ + Str VM::py_repr(PyObject* obj){ const PyTypeInfo* ti = _tp_info(obj); if(ti->m__repr__) return ti->m__repr__(this, obj); - return call_method(obj, __repr__); + PyObject* retval = call_method(obj, __repr__); + if(!is_type(retval, tp_str)){ + throw std::runtime_error("object.__repr__ must return str"); + } + return PK_OBJ_GET(Str, retval); } - PyObject* VM::py_json(PyObject* obj){ + Str VM::py_json(PyObject* obj){ auto j = JsonSerializer(this, obj); - return VAR(j.serialize()); + return j.serialize(); } PyObject* VM::py_iter(PyObject* obj){ @@ -495,7 +505,7 @@ i64 VM::py_hash(PyObject* obj){ } PyObject* VM::__format_object(PyObject* obj, Str spec){ - if(spec.empty()) return py_str(obj); + if(spec.empty()) return VAR(py_str(obj)); char type; switch(spec.end()[-1]){ case 'f': case 'd': case 's': @@ -560,7 +570,7 @@ PyObject* VM::__format_object(PyObject* obj, Str spec){ }else if(type == 's'){ ret = CAST(Str&, obj); }else{ - ret = CAST(Str&, py_str(obj)); + ret = py_str(obj); } if(width != -1 && width > ret.length()){ int pad = width - ret.length(); @@ -602,7 +612,7 @@ static std::string _opcode_argstr(VM* vm, Bytecode byte, const CodeObject* co){ switch(byte.op){ case OP_LOAD_CONST: case OP_FORMAT_STRING: case OP_IMPORT_PATH: if(vm != nullptr){ - argStr += _S(" (", CAST(Str, vm->py_repr(co->consts[byte.arg])), ")").sv(); + argStr += _S(" (", vm->py_repr(co->consts[byte.arg]), ")").sv(); } break; case OP_LOAD_NAME: case OP_LOAD_GLOBAL: case OP_LOAD_NONLOCAL: case OP_STORE_GLOBAL: @@ -1398,14 +1408,27 @@ void VM::bind__next__(Type type, PyObject* (*f)(VM*, PyObject*)){ return lambda_get_userdata(args.begin())(vm, args[0]); \ }, f); \ } - - BIND_UNARY_SPECIAL(__repr__) - BIND_UNARY_SPECIAL(__str__) BIND_UNARY_SPECIAL(__iter__) BIND_UNARY_SPECIAL(__neg__) BIND_UNARY_SPECIAL(__invert__) #undef BIND_UNARY_SPECIAL +void VM::bind__str__(Type type, Str (*f)(VM*, PyObject*)){ + _all_types[type].m__str__ = f; + bind_func(type, __str__, 1, [](VM* vm, ArgsView args){ + Str s = lambda_get_userdata(args.begin())(vm, args[0]); + return VAR(s); + }, f); +} + +void VM::bind__repr__(Type type, Str (*f)(VM*, PyObject*)){ + _all_types[type].m__repr__ = f; + bind_func(type, __repr__, 1, [](VM* vm, ArgsView args){ + Str s = lambda_get_userdata(args.begin())(vm, args[0]); + return VAR(s); + }, f); +} + void VM::bind__hash__(Type type, i64 (*f)(VM*, PyObject*)){ _all_types[type].m__hash__ = f; bind_func(type, __hash__, 1, [](VM* vm, ArgsView args){ @@ -1460,7 +1483,6 @@ void Dict::_probe_0(PyObject *key, bool &ok, int &i) const{ ok = false; i64 hash = vm->py_hash(key); i = hash & _mask; - // std::cout << CAST(Str, vm->py_repr(key)) << " " << hash << " " << i << std::endl; for(int j=0; j<_capacity; j++) { if(_items[i].first != nullptr){ if(vm->py_eq(_items[i].first, key)) { ok = true; break; } @@ -1469,7 +1491,6 @@ void Dict::_probe_0(PyObject *key, bool &ok, int &i) const{ } // https://github.com/python/cpython/blob/3.8/Objects/dictobject.c#L166 i = ((5*i) + 1) & _mask; - // std::cout << CAST(Str, vm->py_repr(key)) << " next: " << i << std::endl; } } @@ -1600,7 +1621,7 @@ void VM::__breakpoint(){ for(PyObject* obj: frame_0->_locals){ if(obj == PY_NULL) continue; StrName name = frame_0->co->varnames[i++]; - stdout_write(_S(name.sv(), " = ", CAST(Str&, vm->py_repr(obj)), '\n')); + stdout_write(_S(name.sv(), " = ", vm->py_repr(obj), '\n')); } continue; } @@ -1644,7 +1665,7 @@ void VM::__breakpoint(){ if(cmd == "p" || cmd == "print"){ CodeObject_ code = compile(arg, "", EVAL_MODE, true); PyObject* retval = vm->_exec(code.get(), frame_0->_module, frame_0->_callable, frame_0->_locals); - stdout_write(CAST(Str&, vm->py_repr(retval))); + stdout_write(vm->py_repr(retval)); stdout_write("\n"); }else if(cmd == "!"){ CodeObject_ code = compile(arg, "", EXEC_MODE, true); diff --git a/tests/07_dict.py b/tests/07_dict.py index 1ab6da6c..6a0d3b49 100644 --- a/tests/07_dict.py +++ b/tests/07_dict.py @@ -166,7 +166,7 @@ assert repr(a) == "{1: 2, 3: 4, 'a': {...}}" # test gc import gc gc.collect() -x = gc.collect() for k, v in a.items(): pass -assert x+1 == gc.collect() +assert gc.collect() == 1 +