diff --git a/docs/features/basic.md b/docs/features/basic.md index 376c552c..04312fa0 100644 --- a/docs/features/basic.md +++ b/docs/features/basic.md @@ -1,6 +1,7 @@ --- icon: dot order: 100 +title: Basic Features --- # basic diff --git a/docs/features/goto.md b/docs/features/goto.md index 1f338691..e694ae43 100644 --- a/docs/features/goto.md +++ b/docs/features/goto.md @@ -1,5 +1,6 @@ --- icon: dot +title: Goto Statement --- # goto/label @@ -25,7 +26,3 @@ for i in range(10): label .exit ``` - -!!! -If we detect an illegal divert, you will get an `UnexpectedError` or the behaviour is undefined. -!!! diff --git a/docs/features/ub.md b/docs/features/ub.md new file mode 100644 index 00000000..6eadedac --- /dev/null +++ b/docs/features/ub.md @@ -0,0 +1,10 @@ +--- +icon: dot +title: Undefined Behaviour +--- + +These are the undefined behaviours of pkpy. The behaviour of pkpy is undefined if you do the following things. + +1. Delete a builtin object. For example, `del int.__add__`. +2. Call an unbound method with the wrong type of `self`. For example, `int.__add__('1', 2)`. +3. Use goto statement to jump out of a context block. \ No newline at end of file diff --git a/src/ceval.h b/src/ceval.h index f14a5373..bd5a4197 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -83,11 +83,12 @@ __NEXT_STEP:; TARGET(LOAD_FUNCTION) { FuncDecl_ decl = co->func_decls[byte.arg]; bool is_simple = decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator; + int argc = decl->args.size(); PyObject* obj; if(decl->nested){ - obj = VAR(Function({decl, is_simple, frame->_module, frame->_locals.to_namedict()})); + obj = VAR(Function({decl, is_simple, argc, frame->_module, frame->_locals.to_namedict()})); }else{ - obj = VAR(Function({decl, is_simple, frame->_module})); + obj = VAR(Function({decl, is_simple, argc, frame->_module})); } PUSH(obj); } DISPATCH(); diff --git a/src/frame.h b/src/frame.h index c5f0164a..5a3228e8 100644 --- a/src/frame.h +++ b/src/frame.h @@ -63,8 +63,9 @@ struct ValueStackImpl { // We allocate extra MAX_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`. PyObject* _begin[MAX_SIZE + MAX_SIZE/128]; PyObject** _sp; + PyObject** _max_end; - ValueStackImpl(): _sp(_begin) {} + ValueStackImpl(): _sp(_begin), _max_end(_begin + MAX_SIZE) {} PyObject*& top(){ return _sp[-1]; } PyObject* top() const { return _sp[-1]; } @@ -90,7 +91,7 @@ struct ValueStackImpl { _sp = sp; } void clear() { _sp = _begin; } - bool is_overflow() const { return _sp >= _begin + MAX_SIZE; } + bool is_overflow() const { return _sp >= _max_end; } ValueStackImpl(const ValueStackImpl&) = delete; ValueStackImpl(ValueStackImpl&&) = delete; diff --git a/src/obj.h b/src/obj.h index a504b597..7dce1a56 100644 --- a/src/obj.h +++ b/src/obj.h @@ -47,6 +47,7 @@ using FuncDecl_ = shared_ptr; struct Function{ FuncDecl_ decl; bool is_simple; + int argc; // cached argc PyObject* _module; NameDict_ _closure; }; diff --git a/src/vm.h b/src/vm.h index f4e88bc3..682b1569 100644 --- a/src/vm.h +++ b/src/vm.h @@ -1001,12 +1001,11 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args, const Function& fn = CAST(Function&, callable); const CodeObject* co = fn.decl->code.get(); - PyObject* _module = fn._module != nullptr ? fn._module : callstack.top()._module; - if(args.size() < fn.decl->args.size()){ + if(args.size() < fn.argc){ vm->TypeError(fmt( "expected ", - fn.decl->args.size(), + fn.argc, " positional arguments, but got ", args.size(), " (", fn.decl->code->name, ')' @@ -1015,11 +1014,11 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args, // if this function is simple, a.k.a, no kwargs and no *args and not a generator // we can use a fast path to avoid using buffer copy - if(fn.is_simple && kwargs.size()==0){ - if(args.size() > fn.decl->args.size()) TypeError("too many positional arguments"); - int spaces = co->varnames.size() - fn.decl->args.size(); + if(fn.is_simple){ + if(args.size() > fn.argc) TypeError("too many positional arguments"); + int spaces = co->varnames.size() - fn.argc; for(int j=0; jis_generator){ PyObject* ret = PyIter(Generator( this, - Frame(&s_data, nullptr, co, _module, callable), + Frame(&s_data, nullptr, co, fn._module, callable), ArgsView(buffer, buffer + co->varnames.size()) )); return ret; @@ -1070,7 +1069,7 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args, // copy buffer to stack for(int i=0; ivarnames.size(); i++) PUSH(buffer[i]); - callstack.emplace(&s_data, p0, co, _module, callable); + callstack.emplace(&s_data, p0, co, fn._module, callable); return nullptr; }