impl py_exec and py_eval

This commit is contained in:
blueloveTH 2024-05-10 14:03:00 +08:00
parent 331dedcd28
commit 5cfd66f8b2
5 changed files with 111 additions and 31 deletions

View File

@ -156,6 +156,7 @@ public:
PyObject* __curr_class;
PyObject* __cached_object_new;
std::map<std::string_view, CodeObject_> __cached_codes;
FuncDecl_ __dynamic_func_decl;
#if PK_ENABLE_PROFILER
LineProfiler* _profiler = nullptr;
@ -409,9 +410,10 @@ public:
#if PK_DEBUG_CEVAL_STEP
void __log_s_data(const char* title = nullptr);
#endif
PyObject* __py_exec_internal(const CodeObject_& code, PyObject* globals, PyObject* locals);
void __breakpoint();
PyObject* __format_object(PyObject*, Str);
PyObject* __run_top_frame(lightfunction<void(Frame*)> on_will_pop_base_frame = {});
PyObject* __run_top_frame();
void __pop_frame();
PyObject* __py_generator(Frame&& frame, ArgsView buffer);
void __op_unpack_sequence(uint16_t arg);

View File

@ -80,7 +80,7 @@ bool VM::py_ge(PyObject* _0, PyObject* _1){
#undef BINARY_F_COMPARE
PyObject* VM::__run_top_frame(lightfunction<void(Frame*)> on_will_pop_base_frame){
PyObject* VM::__run_top_frame(){
Frame* frame = &callstack.top();
const Frame* base_frame = frame;
bool need_raise = false;
@ -260,8 +260,17 @@ __NEXT_STEP:;
PyObject* _0 = POPX();
if(frame->_callable != nullptr){
PyObject** slot = frame->_locals.try_get_name(_name);
if(slot == nullptr) vm->UnboundLocalError(_name);
*slot = _0;
if(slot != nullptr){
*slot = _0; // store in locals if possible
}else{
Function& func = PK_OBJ_GET(Function, frame->_callable);
if(func.decl == __dynamic_func_decl){
PK_DEBUG_ASSERT(func._closure != nullptr);
func._closure->set(_name, _0);
}else{
vm->UnboundLocalError(_name);
}
}
}else{
frame->f_globals().set(_name, _0);
}
@ -307,8 +316,18 @@ __NEXT_STEP:;
StrName _name(byte.arg);
if(frame->_callable != nullptr){
PyObject** slot = frame->_locals.try_get_name(_name);
if(slot == nullptr) vm->UnboundLocalError(_name);
if(slot != nullptr){
*slot = PY_NULL;
}else{
Function& func = PK_OBJ_GET(Function, frame->_callable);
if(func.decl == __dynamic_func_decl){
PK_DEBUG_ASSERT(func._closure != nullptr);
bool ok = func._closure->del(_name);
if(!ok) vm->UnboundLocalError(_name);
}else{
vm->UnboundLocalError(_name);
}
}
}else{
if(!frame->f_globals().del(_name)) vm->NameError(_name);
}
@ -709,12 +728,10 @@ __NEXT_STEP:;
} DISPATCH()
case OP_RETURN_VALUE:{
PyObject* _0 = byte.arg == BC_NOARG ? POPX() : None;
if(frame == base_frame){ // [ frameBase<- ]
if(on_will_pop_base_frame) on_will_pop_base_frame(frame);
__pop_frame();
if(frame == base_frame){ // [ frameBase<- ]
return _0;
}else{
__pop_frame();
frame = &callstack.top();
PUSH(_0);
goto __NEXT_FRAME;
@ -984,9 +1001,6 @@ __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();

View File

@ -1609,6 +1609,10 @@ void VM::__post_init_builtin_types(){
_lazy_modules["itertools"] = kPythonLibs_itertools;
try{
// initialize dummy func_decl for exec/eval
CodeObject_ dynamic_co = compile("def _(): pass", "<dynamic>", EXEC_MODE);
__dynamic_func_decl = dynamic_co->func_decls.at(0);
// initialize builtins
CodeObject_ code = compile(kPythonLibs_builtins, "<builtins>", EXEC_MODE);
this->_exec(code, this->builtins);
code = compile(kPythonLibs__set, "<set>", EXEC_MODE);

View File

@ -505,29 +505,76 @@ 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){
PyObject* VM::__py_exec_internal(const CodeObject_& code, PyObject* globals, PyObject* locals){
Frame* frame = &vm->callstack.top();
vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
return;
// fast path
if(globals == vm->None && locals == vm->None){
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;
vm->_exec(code, obj);
PyObject* globals_obj = nullptr;
Dict* globals_dict = nullptr;
NameDict_ locals_closure = nullptr;
Dict* locals_dict = nullptr;
if(globals == vm->None){
globals_obj = frame->_module;
}else{
if(is_type(globals, VM::tp_mappingproxy)){
globals_obj = PK_OBJ_GET(MappingProxy, globals).obj;
}else{
check_compatible_type(globals, VM::tp_dict);
// make a temporary object and copy globals into it
globals_obj = heap.gcnew<DummyInstance>(VM::tp_object);
globals_obj->_enable_instance_dict();
globals_dict = &PK_OBJ_GET(Dict, globals);
globals_dict->apply([&](PyObject* k, PyObject* v){
globals_obj->attr().set(CAST(Str&, k), v);
});
}
}
PyObject* retval = nullptr;
if(locals == vm->None){
retval = vm->_exec(code, globals_obj); // only globals
}else{
check_compatible_type(locals, VM::tp_dict);
locals_dict = &PK_OBJ_GET(Dict, locals);
locals_closure = std::make_shared<NameDict>();
locals_dict->apply([&](PyObject* k, PyObject* v){
locals_closure->set(CAST(Str&, k), v);
});
PyObject* _callable = VAR(Function(__dynamic_func_decl, globals_obj, nullptr, locals_closure));
retval = vm->_exec(code.get(), globals_obj, _callable, vm->s_data._sp);
}
if(globals_dict){
globals_dict->clear();
globals_obj->attr().apply([&](StrName k, PyObject* v){
globals_dict->set(VAR(k.sv()), v);
});
}
if(locals_dict){
locals_dict->clear();
locals_closure->apply([&](StrName k, PyObject* v){
locals_dict->set(VAR(k.sv()), v);
});
}
return retval;
}
void VM::py_exec(std::string_view source, PyObject* globals, PyObject* locals){
CodeObject_ code = vm->compile(source, "<exec>", EXEC_MODE, true);
__py_exec_internal(code, globals, locals);
}
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);
return __py_exec_internal(code, globals, locals);
}
PyObject* VM::__format_object(PyObject* obj, Str spec){

View File

@ -38,4 +38,17 @@ def abc():
exec('a=1', g.__dict__)
return g.a
assert abc() == 1
res = abc()
assert (res==1), res
# test locals and globals
globals = {'a': 1}
locals = {'a': 1}
exec('a=2', globals, locals)
assert locals == {'a': 2}
assert globals == {'a': 1}
exec('a=2', globals)
assert globals == {'a': 2}