diff --git a/c_bindings/test.c b/c_bindings/test.c index 5d5c7349..c1c78905 100644 --- a/c_bindings/test.c +++ b/c_bindings/test.c @@ -66,6 +66,32 @@ int test_multiple_return(pkpy_vm* vm) { return 2; } +int test_minus(pkpy_vm* vm) { + int a, b; + pkpy_to_int(vm, 0, &a); + pkpy_to_int(vm, 1, &b); + pkpy_push_int(vm, a - b); + return 1; +} + +int test_fib(pkpy_vm* vm) { + int n; + pkpy_to_int(vm, 0, &n); + if (n == 1) { + pkpy_push_int(vm, n); + } else { + pkpy_getglobal(vm, pkpy_name("test_fib")); + pkpy_push_null(vm); + pkpy_push_int(vm, n-1); + pkpy_vectorcall(vm, 1); + int r_int; + pkpy_to_int(vm, -1, &r_int); + pkpy_pop_top(vm); + pkpy_push_int(vm, r_int + n); + } + return 1; +} + int test_default_argument(pkpy_vm* vm){ int x; pkpy_to_int(vm, -1, &x); @@ -311,6 +337,19 @@ int main(int argc, char** argv) { check(pkpy_pop_top(vm)); check(pkpy_stack_size(vm) == 0); + PRINT_TITLE("test bindings 2"); + check(pkpy_push_function(vm, "test_minus(a, b)", test_minus)); + check(pkpy_setglobal(vm, pkpy_name("test_minus"))); + check(pkpy_exec(vm, "print(test_minus(5, 3))")); + check(pkpy_exec(vm, "for i in range(5): print(test_minus(5, i))")); + check(pkpy_stack_size(vm) == 0); + + PRINT_TITLE("test bindings fib"); + check(pkpy_push_function(vm, "test_fib(n: int) -> int", test_fib)); + check(pkpy_setglobal(vm, pkpy_name("test_fib"))); + check(pkpy_exec(vm, "print(test_fib(10))")); + check(pkpy_stack_size(vm) == 0); + PRINT_TITLE("test error propagate"); check(pkpy_push_function(vm, "test_error_propagate()", test_error_propagate)); check(pkpy_setglobal(vm, pkpy_name("test_error_propagate"))); diff --git a/c_bindings/test_answers.txt b/c_bindings/test_answers.txt index 03b3b13a..f1de76e8 100644 --- a/c_bindings/test_answers.txt +++ b/c_bindings/test_answers.txt @@ -59,6 +59,17 @@ TypeError: expected 2 positional arguments, got 0 (x) ====== test bindings ====== 12 +====== test bindings 2 ====== +2 +5 +4 +3 +2 +1 + +====== test bindings fib ====== +55 + ====== test error propagate ====== successfully errored with this message: Traceback (most recent call last): @@ -81,6 +92,10 @@ successfully errored with this message: TypeError: expected 'int', got 'float' ====== test complicated errors ====== +Traceback (most recent call last): + File "main.py", line 1 + test_error_propagate() +NameError: catch me successfully errored with this message: Traceback (most recent call last): _: test direct error mechanism diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index 44e1b756..6bcb4b33 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -119,6 +119,7 @@ public: struct{ PyObject* error; + stack s_view; } _c; PyObject* None; diff --git a/src/pocketpy_c.cpp b/src/pocketpy_c.cpp index 86439a4e..ed4ed4c1 100644 --- a/src/pocketpy_c.cpp +++ b/src/pocketpy_c.cpp @@ -21,23 +21,24 @@ static int count_extra_elements(VM* vm, int n){ if(vm->callstack.empty()){ return vm->s_data.size(); } - PyObject** base = vm->top_frame()->_locals.end(); - return vm->s_data._sp - base; + PK_ASSERT(!vm->_c.s_view.empty()); + return vm->s_data._sp - vm->_c.s_view.top().end(); } static PyObject* stack_item(VM* vm, int index){ PyObject** begin; - PyObject** end; + PyObject** end = vm->s_data.end(); if(vm->callstack.empty()){ begin = vm->s_data.begin(); - end = vm->s_data.end(); }else{ - Frame* frame = vm->top_frame().get(); - begin = frame->_locals.begin(); - end = frame->_locals.end(); + PK_ASSERT(!vm->_c.s_view.empty()); + begin = vm->_c.s_view.top().begin(); + } + int size = end - begin; + if(index < 0) index += size; + if(index < 0 || index >= size){ + throw std::runtime_error("stack_item() => index out of range"); } - // may raise - index = vm->normalized_index(index, end-begin); return begin[index]; } @@ -126,7 +127,8 @@ int pkpy_stack_size(pkpy_vm* vm_handle){ if(vm->callstack.empty()){ return vm->s_data.size(); } - return vm->top_frame()->stack_size(); + PK_ASSERT(!vm->_c.s_view.empty()); + return vm->s_data._sp - vm->_c.s_view.top().begin(); } // int @@ -299,11 +301,31 @@ bool pkpy_push_null(pkpy_vm* vm_handle) { return true; } +struct TempViewPopper{ + VM* vm; + bool used; + + TempViewPopper(VM* vm): vm(vm), used(false) {} + + void restore() noexcept{ + if(used) return; + vm->_c.s_view.pop(); + used = true; + } + + ~TempViewPopper(){ restore(); } +}; + // function static PyObject* c_function_wrapper(VM* vm, ArgsView args) { LuaStyleFuncC f = lambda_get_userdata(args.begin()); PyObject** curr_sp = vm->s_data._sp; - int retc = f(vm); + + vm->_c.s_view.push(args); + TempViewPopper _tvp(vm); + int retc = f(vm); // may raise, _tvp will handle this via RAII + _tvp.restore(); + // propagate_if_errored if (vm->_c.error != nullptr){ Exception e = _py_cast(vm, vm->_c.error); @@ -472,11 +494,14 @@ bool pkpy_clear_error(pkpy_vm* vm_handle, char** message) { if (message != nullptr) *message = e.summary().c_str_dup(); else - std::cerr << e.summary() << std::endl; + std::cout << e.summary() << std::endl; vm->_c.error = nullptr; - // clear the whole stack?? - vm->callstack.clear(); - vm->s_data.clear(); + if(vm->callstack.empty()){ + vm->s_data.clear(); + }else{ + PK_ASSERT(!vm->_c.s_view.empty()); + vm->s_data.reset(vm->_c.s_view.top().end()); + } return true; }