Просмотр исходного кода

add `py_exec` and `py_eval` prototype

blueloveTH 1 год назад
Родитель
Сommit
331dedcd28
5 измененных файлов с 69 добавлено и 25 удалено
  1. 28 1
      include/pocketpy/any.h
  2. 4 1
      include/pocketpy/vm.h
  3. 7 2
      src/ceval.cpp
  4. 5 21
      src/pocketpy.cpp
  5. 25 0
      src/vm.cpp

+ 28 - 1
include/pocketpy/any.h

@@ -90,7 +90,34 @@ struct function<Ret(Params...)>{
     template<typename F>
     function(F&& f) : _impl(std::forward<F>(f)){
         _wrapper = [](const any& impl, Params... params) -> Ret{
-            return impl.cast<std::decay_t<F>>()(std::forward<Params>(params)...);
+            return impl._cast<std::decay_t<F>>()(std::forward<Params>(params)...);
+        };
+    }
+
+    Ret operator()(Params... params) const{
+        if(!_wrapper) throw std::runtime_error("empty function");
+        return _wrapper(_impl, std::forward<Params>(params)...);
+    }
+};
+
+template<typename T>
+struct lightfunction;
+
+template<typename Ret, typename... Params>
+struct lightfunction<Ret(Params...)>{
+    void* _impl;
+    Ret (*_wrapper)(void*, Params...);
+
+    lightfunction() : _impl(nullptr), _wrapper(nullptr) {}
+
+    operator bool() const { return _wrapper != nullptr; }
+
+    template<typename F>
+    lightfunction(const F& f){
+        _impl = (F*)(&f);
+        _wrapper = [](void* impl, Params... params) -> Ret{
+            F* f = (F*)(impl);
+            return (*f)(std::forward<Params>(params)...);
         };
     }
 

+ 4 - 1
include/pocketpy/vm.h

@@ -207,6 +207,9 @@ public:
     bool py_ne(PyObject* lhs, PyObject* rhs) {              // (lhs, rhs) -> lhs != rhs
         return !py_eq(lhs, rhs);
     }
+
+    void py_exec(std::string_view, PyObject*, PyObject*);       // exec(source, globals, locals)
+    PyObject* py_eval(std::string_view, PyObject*, PyObject*);  // eval(source, globals, locals)
 #endif
 
 #if PK_REGION("Utility Methods")
@@ -408,7 +411,7 @@ public:
 #endif
     void __breakpoint();
     PyObject* __format_object(PyObject*, Str);
-    PyObject* __run_top_frame();
+    PyObject* __run_top_frame(lightfunction<void(Frame*)> on_will_pop_base_frame = {});
     void __pop_frame();
     PyObject* __py_generator(Frame&& frame, ArgsView buffer);
     void __op_unpack_sequence(uint16_t arg);

+ 7 - 2
src/ceval.cpp

@@ -80,7 +80,7 @@ bool VM::py_ge(PyObject* _0, PyObject* _1){
 
 #undef BINARY_F_COMPARE
 
-PyObject* VM::__run_top_frame(){
+PyObject* VM::__run_top_frame(lightfunction<void(Frame*)> on_will_pop_base_frame){
     Frame* frame = &callstack.top();
     const Frame* base_frame = frame;
     bool need_raise = false;
@@ -709,10 +709,12 @@ __NEXT_STEP:;
     } DISPATCH()
     case OP_RETURN_VALUE:{
         PyObject* _0 = byte.arg == BC_NOARG ? POPX() : None;
-        __pop_frame();
         if(frame == base_frame){       // [ frameBase<- ]
+            if(on_will_pop_base_frame) on_will_pop_base_frame(frame);
+            __pop_frame();
             return _0;
         }else{
+            __pop_frame();
             frame = &callstack.top();
             PUSH(_0);
             goto __NEXT_FRAME;
@@ -982,6 +984,9 @@ __NEXT_STEP:;
             PyObject* e_obj = POPX();
             Exception& _e = PK_OBJ_GET(Exception, e_obj);
             bool is_base_frame_to_be_popped = frame == base_frame;
+            if(is_base_frame_to_be_popped){
+                if(on_will_pop_base_frame) on_will_pop_base_frame(frame);
+            }
             __pop_frame();
             if(callstack.empty()) throw _e;   // propagate to the top level
             frame = &callstack.top();

+ 5 - 21
src/pocketpy.cpp

@@ -197,29 +197,13 @@ void __init_builtins(VM* _vm) {
         }
     });
 
-    _vm->bind(_vm->builtins, "eval(__source, __globals=None)", [](VM* vm, ArgsView args) {
-        CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<eval>", EVAL_MODE, true);
-        PyObject* globals = args[1];
-        if(globals == vm->None){
-            Frame* frame = &vm->callstack.top();
-            return vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
-        }
-        vm->check_type(globals, VM::tp_mappingproxy);
-        PyObject* obj = PK_OBJ_GET(MappingProxy, globals).obj;
-        return vm->_exec(code, obj);
+    // we use `_0`, `_1` and `_2` here to disable keyword arguments (but with default values)
+    _vm->bind(_vm->builtins, "eval(_0, _1=None, _2=None)", [](VM* vm, ArgsView args) {
+        return vm->py_eval(CAST(Str&, args[0]), args[1], args[2]);
     });
 
-    _vm->bind(_vm->builtins, "exec(__source, __globals=None)", [](VM* vm, ArgsView args) {
-        CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<exec>", EXEC_MODE, true);
-        PyObject* globals = args[1];
-        if(globals == vm->None){
-            Frame* frame = &vm->callstack.top();
-            vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
-            return vm->None;
-        }
-        vm->check_type(globals, VM::tp_mappingproxy);
-        PyObject* obj = PK_OBJ_GET(MappingProxy, globals).obj;
-        vm->_exec(code, obj);
+    _vm->bind(_vm->builtins, "exec(_0, _1=None, _2=None)", [](VM* vm, ArgsView args) {
+        vm->py_exec(CAST(Str&, args[0]), args[1], args[2]);
         return vm->None;
     });
 

+ 25 - 0
src/vm.cpp

@@ -505,6 +505,31 @@ i64 VM::py_hash(PyObject* obj){
     }
 }
 
+void VM::py_exec(std::string_view source, PyObject* globals, PyObject* locals){
+    (void)(locals);
+    CodeObject_ code = vm->compile(source, "<exec>", EXEC_MODE, true);
+    if(globals == vm->None){
+        Frame* frame = &vm->callstack.top();
+        vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
+        return;
+    }
+    vm->check_type(globals, VM::tp_mappingproxy);
+    PyObject* obj = PK_OBJ_GET(MappingProxy, globals).obj;
+    vm->_exec(code, obj);
+}
+
+PyObject* VM::py_eval(std::string_view source, PyObject* globals, PyObject* locals){
+    (void)(locals);
+    CodeObject_ code = vm->compile(source, "<eval>", EVAL_MODE, true);
+    if(globals == vm->None){
+        Frame* frame = &vm->callstack.top();
+        return vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
+    }
+    vm->check_type(globals, VM::tp_mappingproxy);
+    PyObject* obj = PK_OBJ_GET(MappingProxy, globals).obj;
+    return vm->_exec(code, obj);
+}
+
 PyObject* VM::__format_object(PyObject* obj, Str spec){
     if(spec.empty()) return VAR(py_str(obj));
     char type;