blueloveTH 2 лет назад
Родитель
Сommit
e124f635c1
9 измененных файлов с 73 добавлено и 15 удалено
  1. 1 1
      include/pocketpy/common.h
  2. 1 0
      include/pocketpy/opcodes.h
  3. 2 1
      include/pocketpy/vm.h
  4. 24 9
      src/ceval.cpp
  5. 9 2
      src/expr.cpp
  6. 6 0
      src/pocketpy.cpp
  7. 1 0
      src/vm.cpp
  8. 10 0
      tests/40_class.py
  9. 19 2
      tests/99_builtin_func.py

+ 1 - 1
include/pocketpy/common.h

@@ -23,7 +23,7 @@
 #include <bitset>
 #include <deque>
 
-#define PK_VERSION				"1.2.9"
+#define PK_VERSION				"1.3.0"
 
 #include "config.h"
 #include "export.h"

+ 1 - 0
include/pocketpy/opcodes.h

@@ -23,6 +23,7 @@ OPCODE(LOAD_NAME)
 OPCODE(LOAD_NONLOCAL)
 OPCODE(LOAD_GLOBAL)
 OPCODE(LOAD_ATTR)
+OPCODE(LOAD_CLASS_GLOBAL)
 OPCODE(LOAD_METHOD)
 OPCODE(LOAD_SUBSCR)
 

+ 2 - 1
include/pocketpy/vm.h

@@ -133,7 +133,8 @@ public:
     PyObject* StopIteration;
     PyObject* _main;            // __main__ module
 
-    PyObject* _last_exception;
+    PyObject* _last_exception;  // last exception
+    PyObject* _curr_class;      // current class being defined
 
 #if PK_ENABLE_CEVAL_CALLBACK
     void (*_ceval_on_step)(VM*, Frame*, Bytecode bc) = nullptr;

+ 24 - 9
src/ceval.cpp

@@ -182,10 +182,22 @@ __NEXT_STEP:;
         _0 = vm->builtins->attr().try_get_likely_found(_name);
         if(_0 != nullptr) { PUSH(_0); DISPATCH(); }
         vm->NameError(_name);
-    }DISPATCH();
-    TARGET(LOAD_ATTR)
+    } DISPATCH();
+    TARGET(LOAD_ATTR){
         TOP() = getattr(TOP(), StrName(byte.arg));
-        DISPATCH();
+    } DISPATCH();
+    TARGET(LOAD_CLASS_GLOBAL){
+        PK_ASSERT(_curr_class != nullptr);
+        StrName _name(byte.arg);
+        PyObject* _0 = getattr(_curr_class, _name, false);
+        if(_0 != nullptr) { PUSH(_0); DISPATCH(); }
+        // load global if attribute not found
+        _0 = frame->f_globals().try_get_likely_found(_name);
+        if(_0 != nullptr) { PUSH(_0); DISPATCH(); }
+        _0 = vm->builtins->attr().try_get_likely_found(_name);
+        if(_0 != nullptr) { PUSH(_0); DISPATCH(); }
+        vm->NameError(_name);
+    } DISPATCH();
     TARGET(LOAD_METHOD){
         PyObject* _0;
         TOP() = get_unbound_method(TOP(), StrName(byte.arg), &_0, true, true);
@@ -723,15 +735,18 @@ __NEXT_STEP:;
         PyObject* _0 = POPX();   // super
         if(_0 == None) _0 = _t(tp_object);
         check_non_tagged_type(_0, tp_type);
-        PyObject* _1 = new_type_object(frame->_module, _name, PK_OBJ_GET(Type, _0));
-        PUSH(_1);
+        _curr_class = new_type_object(frame->_module, _name, PK_OBJ_GET(Type, _0));
+    } DISPATCH();
+    TARGET(END_CLASS) {
+        PK_ASSERT(_curr_class != nullptr);
+        _curr_class = nullptr;
     } DISPATCH();
-    TARGET(END_CLASS) POP(); DISPATCH();
     TARGET(STORE_CLASS_ATTR){
+        PK_ASSERT(_curr_class != nullptr);
         StrName _name(byte.arg);
         PyObject* _0 = POPX();
         if(is_non_tagged_type(_0, tp_function)){
-            PK_OBJ_GET(Function, _0)._class = TOP();
+            PK_OBJ_GET(Function, _0)._class = _curr_class;
         }
         TOP()->attr().set(_name, _0);
     } DISPATCH();
@@ -793,8 +808,8 @@ __NEXT_STEP:;
     } DISPATCH();
 
 #if !PK_ENABLE_COMPUTED_GOTO
-        static_assert(OP_DEC_GLOBAL == 106);
-        case 107: case 108: case 109: case 110: case 111: case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: case 120: case 121: case 122: case 123: case 124: case 125: case 126: case 127: case 128: case 129: case 130: case 131: case 132: case 133: case 134: case 135: case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: case 147: case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179: case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: case 192: case 193: case 194: case 195: case 196: case 197: case 198: case 199: case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: case 208: case 209: case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: case 224: case 225: case 226: case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239: case 240: case 241: case 242: case 243: case 244: case 245: case 246: case 247: case 248: case 249: case 250: case 251: case 252: case 253: case 254: case 255: FATAL_ERROR(); break;
+        static_assert(OP_DEC_GLOBAL == 107);
+        case 108: case 109: case 110: case 111: case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: case 120: case 121: case 122: case 123: case 124: case 125: case 126: case 127: case 128: case 129: case 130: case 131: case 132: case 133: case 134: case 135: case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: case 147: case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: case 176: case 177: case 178: case 179: case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: case 192: case 193: case 194: case 195: case 196: case 197: case 198: case 199: case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: case 208: case 209: case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: case 224: case 225: case 226: case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239: case 240: case 241: case 242: case 243: case 244: case 245: case 246: case 247: case 248: case 249: case 250: case 251: case 252: case 253: case 254: case 255: FATAL_ERROR(); break;
     }
 #endif
 }

+ 9 - 2
src/expr.cpp

@@ -127,8 +127,15 @@ namespace pkpy{
             ctx->emit_(OP_LOAD_FAST, index, line);
         }else{
             Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL;
-            // we cannot determine the scope when calling exec()/eval()
-            if(scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME;
+            if(ctx->is_compiling_class && scope == NAME_GLOBAL){
+                // if we are compiling a class, we should use OP_LOAD_ATTR_GLOBAL instead of OP_LOAD_GLOBAL
+                // this supports @property.setter
+                op = OP_LOAD_CLASS_GLOBAL;
+                // exec()/eval() won't work with OP_LOAD_ATTR_GLOBAL in class body
+            }else{
+                // we cannot determine the scope when calling exec()/eval()
+                if(scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME;
+            }
             ctx->emit_(op, StrName(name).index, line);
         }
     }

+ 6 - 0
src/pocketpy.cpp

@@ -1653,6 +1653,12 @@ void add_module_gc(VM* vm){
 void VM::post_init(){
     init_builtins(this);
 
+    bind_method<1>("property", "setter", [](VM* vm, ArgsView args) {
+        Property& self = _CAST(Property&, args[0]);
+        self.setter = args[1];
+        return args[0];
+    });
+
     // type
     bind__getitem__(tp_type, [](VM* vm, PyObject* self, PyObject* _){
         PK_UNUSED(_);

+ 1 - 0
src/vm.cpp

@@ -1192,6 +1192,7 @@ void ManagedHeap::mark() {
     for(PyObject* obj: vm->s_data) PK_OBJ_MARK(obj);
     if(_gc_marker_ex) _gc_marker_ex(vm);
     if(vm->_last_exception) PK_OBJ_MARK(vm->_last_exception);
+    if(vm->_curr_class) PK_OBJ_MARK(vm->_curr_class);
     if(vm->_c.error != nullptr) PK_OBJ_MARK(vm->_c.error);
 }
 

+ 10 - 0
tests/40_class.py

@@ -109,3 +109,13 @@ class B(A):
 # assert B.a == 1  ...bug here
 assert B.b == 3
 assert B.c == 4
+
+class A:
+    x = 1
+    x = x + 1
+    y = 1
+    z = x + y
+
+assert A.x == 2
+assert A.y == 1
+assert A.z == 3

+ 19 - 2
tests/99_builtin_func.py

@@ -810,12 +810,29 @@ try:
 except:
     pass
 
+class Vector2:
+    def __init__(self) -> None:
+        self._x = 0
+
+    @property
+    def x(self):
+        return self._x
+    
+    @x.setter
+    def x(self, val):
+        self._x = val
+
+v = Vector2()
+assert v.x == 0
+v.x = 10
+assert v.x == 10
+
 # /************ module timeit ************/
 import timeit
 
 def aaa():
-    for i in range(100):
-        for j in range(100):
+    for i in range(10):
+        for j in range(10):
             pass
     
 assert type(timeit.timeit(aaa, 2)) is float