blueloveTH 2 лет назад
Родитель
Сommit
5d6d109164
7 измененных файлов с 91 добавлено и 46 удалено
  1. 20 1
      src/ceval.h
  2. 2 2
      src/common.h
  3. 2 0
      src/compiler.h
  4. 17 39
      src/expr.h
  5. 2 0
      src/opcodes.h
  6. 4 1
      src/pocketpy.h
  7. 44 3
      src/vm.h

+ 20 - 1
src/ceval.h

@@ -48,6 +48,7 @@ __NEXT_STEP:;
         PyObject* obj = VAR(Function({decl, frame->_module, frame->_locals}));
         frame->push(obj);
     } DISPATCH();
+    case OP_LOAD_NULL: frame->push(_py_null); DISPATCH();
     /*****************************************/
     case OP_LOAD_NAME: {
         StrName name = frame->co->names[byte.arg];
@@ -67,6 +68,13 @@ __NEXT_STEP:;
         StrName name = frame->co->names[byte.arg];
         frame->top() = getattr(a, name);
     } DISPATCH();
+    case OP_LOAD_METHOD: {
+        PyObject* a = frame->top();
+        StrName name = frame->co->names[byte.arg];
+        PyObject* self;
+        frame->top() = get_unbound_method(a, name, &self);
+        frame->push(self);
+    } DISPATCH();
     case OP_LOAD_SUBSCR: {
         Args args(2);
         args[1] = frame->popx();    // b
@@ -222,7 +230,13 @@ __NEXT_STEP:;
     /*****************************************/
     // TODO: examine this later
     case OP_CALL: case OP_CALL_UNPACK: {
-        Args args = frame->popx_n_reversed(byte.arg);
+        int ARGC = byte.arg;
+
+        bool method_call = frame->top_n(ARGC) != _py_null;
+        if(method_call) ARGC++;         // add self into args
+        Args args = frame->popx_n_reversed(ARGC);
+        if(!method_call) frame->pop();
+
         if(byte.op == OP_CALL_UNPACK) unpack_args(args);
         PyObject* callable = frame->popx();
         PyObject* ret = call(callable, std::move(args), no_arg(), true);
@@ -233,7 +247,12 @@ __NEXT_STEP:;
         int ARGC = byte.arg & 0xFFFF;
         int KWARGC = (byte.arg >> 16) & 0xFFFF;
         Args kwargs = frame->popx_n_reversed(KWARGC*2);
+
+        bool method_call = frame->top_n(ARGC) != _py_null;
+        if(method_call) ARGC++;         // add self into args
         Args args = frame->popx_n_reversed(ARGC);
+        if(!method_call) frame->pop();
+
         if(byte.op == OP_CALL_KWARGS_UNPACK) unpack_args(args);
         PyObject* callable = frame->popx();
         PyObject* ret = call(callable, std::move(args), kwargs, true);

+ 2 - 2
src/common.h

@@ -33,12 +33,12 @@
 // debug macros
 #define DEBUG_NO_BUILTIN_MODULES	0
 #define DEBUG_EXTRA_CHECK			0
-#define DEBUG_DIS_EXEC				1
+#define DEBUG_DIS_EXEC				0
 #define DEBUG_DIS_EXEC_MIN			1
 #define DEBUG_CEVAL_STEP			0
 #define DEBUG_FULL_EXCEPTION		0
 #define DEBUG_NO_AUTO_GC			1
-#define DEBUG_GC_STATS				0
+#define DEBUG_GC_STATS				1
 
 #if (defined(__ANDROID__) && __ANDROID_API__ <= 22) || defined(__EMSCRIPTEN__)
 #define PK_ENABLE_FILEIO 		0

+ 2 - 0
src/compiler.h

@@ -884,6 +884,8 @@ __SUBSCR_END:
         for(auto it=decorators.rbegin(); it!=decorators.rend(); ++it){
             (*it)->emit(ctx());
             ctx()->emit(OP_ROT_TWO, BC_NOARG, (*it)->line);
+            ctx()->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
+            ctx()->emit(OP_ROT_TWO, BC_NOARG, BC_KEEPLINE);
             ctx()->emit(OP_CALL, 1, (*it)->line);
         }
         if(!ctx()->is_compiling_class){

+ 17 - 39
src/expr.h

@@ -17,10 +17,10 @@ struct Expr{
     virtual void emit(CodeEmitContext* ctx) = 0;
     virtual Str str() const = 0;
 
-    virtual std::vector<const Expr*> children() const { return {}; }
     virtual bool is_starred() const { return false; }
     virtual bool is_literal() const { return false; }
     virtual bool is_json_object() const { return false; }
+    virtual bool is_attrib() const { return false; }
 
     // for OP_DELETE_XXX
     virtual bool emit_del(CodeEmitContext* ctx) { return false; }
@@ -163,8 +163,6 @@ struct StarredExpr: Expr{
     StarredExpr(Expr_&& child): child(std::move(child)) {}
     Str str() const override { return "*"; }
 
-    std::vector<const Expr*> children() const override { return {child.get()}; }
-
     bool is_starred() const override { return true; }
 
     void emit(CodeEmitContext* ctx) override {
@@ -184,8 +182,6 @@ struct NotExpr: Expr{
     NotExpr(Expr_&& child): child(std::move(child)) {}
     Str str() const override { return "not"; }
 
-    std::vector<const Expr*> children() const override { return {child.get()}; }
-
     void emit(CodeEmitContext* ctx) override {
         child->emit(ctx);
         ctx->emit(OP_UNARY_NOT, BC_NOARG, line);
@@ -198,8 +194,6 @@ struct AndExpr: Expr{
     Expr_ rhs;
     Str str() const override { return "and"; }
 
-    std::vector<const Expr*> children() const override { return {lhs.get(), rhs.get()}; }
-
     void emit(CodeEmitContext* ctx) override {
         lhs->emit(ctx);
         int patch = ctx->emit(OP_JUMP_IF_FALSE_OR_POP, BC_NOARG, line);
@@ -214,8 +208,6 @@ struct OrExpr: Expr{
     Expr_ rhs;
     Str str() const override { return "or"; }
 
-    std::vector<const Expr*> children() const override { return {lhs.get(), rhs.get()}; }
-
     void emit(CodeEmitContext* ctx) override {
         lhs->emit(ctx);
         int patch = ctx->emit(OP_JUMP_IF_TRUE_OR_POP, BC_NOARG, line);
@@ -295,8 +287,6 @@ struct NegatedExpr: Expr{
     NegatedExpr(Expr_&& child): child(std::move(child)) {}
     Str str() const override { return "-"; }
 
-    std::vector<const Expr*> children() const override { return {child.get()}; }
-
     void emit(CodeEmitContext* ctx) override {
         VM* vm = ctx->vm;
         // if child is a int of float, do constant folding
@@ -330,11 +320,6 @@ struct SliceExpr: Expr{
     Expr_ step;
     Str str() const override { return "slice()"; }
 
-    std::vector<const Expr*> children() const override {
-        // may contain nullptr
-        return {start.get(), stop.get(), step.get()};
-    }
-
     void emit(CodeEmitContext* ctx) override {
         if(start){
             start->emit(ctx);
@@ -362,7 +347,6 @@ struct DictItemExpr: Expr{
     Expr_ key;
     Expr_ value;
     Str str() const override { return "k:v"; }
-    std::vector<const Expr*> children() const override { return {key.get(), value.get()}; }
 
     void emit(CodeEmitContext* ctx) override {
         value->emit(ctx);
@@ -376,12 +360,6 @@ struct SequenceExpr: Expr{
     SequenceExpr(std::vector<Expr_>&& items): items(std::move(items)) {}
     virtual Opcode opcode() const = 0;
 
-    std::vector<const Expr*> children() const override {
-        std::vector<const Expr*> ret;
-        for(auto& item: items) ret.push_back(item.get());
-        return ret;
-    }
-
     void emit(CodeEmitContext* ctx) override {
         for(auto& item: items) item->emit(ctx);
         ctx->emit(opcode(), items.size(), line);
@@ -543,6 +521,7 @@ struct FStringExpr: Expr{
                 size++;
             }
             ctx->emit(OP_LOAD_BUILTIN_EVAL, BC_NOARG, line);
+            ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
             ctx->emit(OP_LOAD_CONST, ctx->add_const(VAR(m[1].str())), line);
             ctx->emit(OP_CALL, 1, line);
             size++;
@@ -609,6 +588,14 @@ struct AttribExpr: Expr{
         ctx->emit(OP_STORE_ATTR, index, line);
         return true;
     }
+
+    void emit_method(CodeEmitContext* ctx) {
+        a->emit(ctx);
+        int index = ctx->add_name(b);
+        ctx->emit(OP_LOAD_METHOD, index, line);
+    }
+
+    bool is_attrib() const override { return true; }
 };
 
 // PASS
@@ -618,13 +605,6 @@ struct CallExpr: Expr{
     std::vector<std::pair<Str, Expr_>> kwargs;
     Str str() const override { return "call(...)"; }
 
-    std::vector<const Expr*> children() const override {
-        std::vector<const Expr*> ret;
-        for(auto& item: args) ret.push_back(item.get());
-        // ...ignore kwargs for simplicity
-        return ret;
-    }
-
     bool need_unpack() const {
         for(auto& item: args) if(item->is_starred()) return true;
         return false;
@@ -634,7 +614,13 @@ struct CallExpr: Expr{
         VM* vm = ctx->vm;
         // TODO: if callable is a AttrExpr, we should try to use `fast_call`
         // instead of use `boundmethod` proxy
-        callable->emit(ctx);
+        if(callable->is_attrib()){
+            auto p = static_cast<AttribExpr*>(callable.get());
+            p->emit_method(ctx);
+        }else{
+            callable->emit(ctx);
+            ctx->emit(OP_LOAD_NULL, BC_NOARG, BC_KEEPLINE);
+        }
         // emit args
         for(auto& item: args) item->emit(ctx);
         // emit kwargs
@@ -659,10 +645,6 @@ struct BinaryExpr: Expr{
     Expr_ rhs;
     Str str() const override { return TK_STR(op); }
 
-    std::vector<const Expr*> children() const override {
-        return {lhs.get(), rhs.get()};
-    }
-
     void emit(CodeEmitContext* ctx) override {
         lhs->emit(ctx);
         rhs->emit(ctx);
@@ -706,10 +688,6 @@ struct TernaryExpr: Expr{
         return "cond ? t : f";
     }
 
-    std::vector<const Expr*> children() const override {
-        return {cond.get(), true_expr.get(), false_expr.get()};
-    }
-
     void emit(CodeEmitContext* ctx) override {
         cond->emit(ctx);
         int patch = ctx->emit(OP_POP_JUMP_IF_FALSE, BC_NOARG, cond->line);

+ 2 - 0
src/opcodes.h

@@ -15,9 +15,11 @@ OPCODE(LOAD_FALSE)
 OPCODE(LOAD_ELLIPSIS)
 OPCODE(LOAD_BUILTIN_EVAL)
 OPCODE(LOAD_FUNCTION)
+OPCODE(LOAD_NULL)
 /**************************/
 OPCODE(LOAD_NAME)
 OPCODE(LOAD_ATTR)
+OPCODE(LOAD_METHOD)
 OPCODE(LOAD_SUBSCR)
 
 OPCODE(STORE_LOCAL)

+ 4 - 1
src/pocketpy.h

@@ -2,6 +2,7 @@
 
 #include "ceval.h"
 #include "compiler.h"
+#include "obj.h"
 #include "repl.h"
 #include "iter.h"
 #include "cffi.h"
@@ -68,7 +69,9 @@ inline void init_builtins(VM* _vm) {
         vm->check_type(args[0], vm->tp_type);
         Type type = OBJ_GET(Type, args[0]);
         if(!vm->isinstance(args[1], type)){
-            vm->TypeError("super(type, obj): obj must be an instance or subtype of type");
+            Str _0 = obj_type_name(vm, OBJ_GET(Type, vm->_t(args[1])));
+            Str _1 = obj_type_name(vm, type);
+            vm->TypeError("super(): " + _0.escape(true) + " is not an instance of " + _1.escape(true));
         }
         Type base = vm->_all_types[type].base;
         return vm->heap.gcnew(vm->tp_super, Super(args[1], base));

+ 44 - 3
src/vm.h

@@ -60,6 +60,7 @@ public:
 
     PyObject* _py_op_call;
     PyObject* _py_op_yield;
+    PyObject* _py_null;
     PyObject* None;
     PyObject* True;
     PyObject* False;
@@ -337,6 +338,7 @@ public:
     PyObject* call(PyObject* callable, Args args, const Args& kwargs, bool opCall);
     void unpack_args(Args& args);
     PyObject* getattr(PyObject* obj, StrName name, bool throw_err=true, bool class_only=false);
+    PyObject* get_unbound_method(PyObject* obj, StrName name, PyObject** self);
     template<typename T>
     void setattr(PyObject* obj, StrName name, T&& value);
     template<int ARGC>
@@ -594,7 +596,7 @@ inline Str VM::disassemble(CodeObject_ co){
                 argStr += " (" + CAST(Str, asRepr(co->consts[byte.arg])) + ")";
                 break;
             case OP_LOAD_NAME: case OP_STORE_LOCAL: case OP_STORE_GLOBAL:
-            case OP_LOAD_ATTR: case OP_STORE_ATTR: case OP_DELETE_ATTR:
+            case OP_LOAD_ATTR: case OP_LOAD_METHOD: case OP_STORE_ATTR: case OP_DELETE_ATTR:
             case OP_IMPORT_NAME: case OP_BEGIN_CLASS:
             case OP_DELETE_LOCAL: case OP_DELETE_GLOBAL:
                 argStr += " (" + co->names[byte.arg].str().escape(true) + ")";
@@ -663,6 +665,7 @@ inline void VM::init_builtin_types(){
     this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {});
     this->True = heap._new<Dummy>(tp_bool, {});
     this->False = heap._new<Dummy>(tp_bool, {});
+    this->_py_null = heap._new<Dummy>(_new_type_object("_py_null"), {});
     this->_py_op_call = heap._new<Dummy>(_new_type_object("_py_op_call"), {});
     this->_py_op_yield = heap._new<Dummy>(_new_type_object("_py_op_yield"), {});
 
@@ -682,7 +685,6 @@ inline void VM::init_builtin_types(){
 
     post_init();
     for(int i=0; i<_all_types.size(); i++){
-        // std::cout << i << ": " << _all_types[i].name << std::endl;
         _all_types[i].obj->attr()._try_perfect_rehash();
     }
     for(auto [k, v]: _modules.items()) v->attr()._try_perfect_rehash();
@@ -786,6 +788,7 @@ inline void VM::unpack_args(Args& args){
 
 // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
 inline PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err, bool class_only){
+    // TODO: class_only impl may not be correct
     PyObject* objtype = _t(obj);
     // handle super() proxy
     if(is_type(obj, tp_super)){
@@ -815,6 +818,39 @@ inline PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err, bool c
     return nullptr;
 }
 
+// used by OP_LOAD_METHOD
+// try to load a unbound method (fallback to `getattr` if not found)
+inline PyObject* VM::get_unbound_method(PyObject* obj, StrName name, PyObject** self){
+    *self = _py_null;
+    // TODO: class_only impl may not be correct
+    PyObject* objtype = _t(obj);
+    // handle super() proxy
+    if(is_type(obj, tp_super)){
+        const Super& super = OBJ_GET(Super, obj);
+        obj = super.first;
+        objtype = _t(super.second);
+    }
+    PyObject* cls_var = find_name_in_mro(objtype, name);
+    if(cls_var != nullptr){
+        // handle descriptor
+        PyObject* descr_get = _t(cls_var)->attr().try_get(__get__);
+        if(descr_get != nullptr) return call(descr_get, Args{cls_var, obj});
+    }
+    // handle instance __dict__
+    if(!is_tagged(obj) && obj->is_attr_valid()){
+        PyObject* val = obj->attr().try_get(name);
+        if(val != nullptr) return val;
+    }
+    if(cls_var != nullptr){
+        if(is_type(cls_var, tp_function) || is_type(cls_var, tp_native_function)){
+            *self = obj;
+        }
+        return cls_var;
+    }
+    AttributeError(obj, name);
+    return nullptr;
+}
+
 template<typename T>
 inline void VM::setattr(PyObject* obj, StrName name, T&& value){
     static_assert(std::is_same_v<std::decay_t<T>, PyObject*>);
@@ -894,7 +930,12 @@ inline PyObject* VM::_exec(){
             Exception& _e = CAST(Exception&, obj);
             _e.st_push(frame->snapshot());
             callstack.pop();
-            if(callstack.empty()) throw _e;
+            if(callstack.empty()){
+#if DEBUG_FULL_EXCEPTION
+                std::cerr << _e.summary() << std::endl;
+#endif
+                throw _e;
+            }
             frame = callstack.top().get();
             frame->push(obj);
             if(frame->id < base_id) throw ToBeRaisedException();