This commit is contained in:
blueloveTH 2023-05-03 14:20:18 +08:00
parent 468f7a3c22
commit 849c6aabb5
7 changed files with 27 additions and 17 deletions

View File

@ -1,6 +1,7 @@
--- ---
icon: dot icon: dot
order: 100 order: 100
title: Basic Features
--- ---
# basic # basic

View File

@ -1,5 +1,6 @@
--- ---
icon: dot icon: dot
title: Goto Statement
--- ---
# goto/label # goto/label
@ -25,7 +26,3 @@ for i in range(10):
label .exit label .exit
``` ```
!!!
If we detect an illegal divert, you will get an `UnexpectedError` or the behaviour is undefined.
!!!

10
docs/features/ub.md Normal file
View File

@ -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.

View File

@ -83,11 +83,12 @@ __NEXT_STEP:;
TARGET(LOAD_FUNCTION) { TARGET(LOAD_FUNCTION) {
FuncDecl_ decl = co->func_decls[byte.arg]; FuncDecl_ decl = co->func_decls[byte.arg];
bool is_simple = decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator; bool is_simple = decl->starred_arg==-1 && decl->kwargs.size()==0 && !decl->code->is_generator;
int argc = decl->args.size();
PyObject* obj; PyObject* obj;
if(decl->nested){ 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{ }else{
obj = VAR(Function({decl, is_simple, frame->_module})); obj = VAR(Function({decl, is_simple, argc, frame->_module}));
} }
PUSH(obj); PUSH(obj);
} DISPATCH(); } DISPATCH();

View File

@ -63,8 +63,9 @@ struct ValueStackImpl {
// We allocate extra MAX_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`. // We allocate extra MAX_SIZE/128 places to keep `_sp` valid when `is_overflow() == true`.
PyObject* _begin[MAX_SIZE + MAX_SIZE/128]; PyObject* _begin[MAX_SIZE + MAX_SIZE/128];
PyObject** _sp; PyObject** _sp;
PyObject** _max_end;
ValueStackImpl(): _sp(_begin) {} ValueStackImpl(): _sp(_begin), _max_end(_begin + MAX_SIZE) {}
PyObject*& top(){ return _sp[-1]; } PyObject*& top(){ return _sp[-1]; }
PyObject* top() const { return _sp[-1]; } PyObject* top() const { return _sp[-1]; }
@ -90,7 +91,7 @@ struct ValueStackImpl {
_sp = sp; _sp = sp;
} }
void clear() { _sp = _begin; } 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(const ValueStackImpl&) = delete;
ValueStackImpl(ValueStackImpl&&) = delete; ValueStackImpl(ValueStackImpl&&) = delete;

View File

@ -47,6 +47,7 @@ using FuncDecl_ = shared_ptr<FuncDecl>;
struct Function{ struct Function{
FuncDecl_ decl; FuncDecl_ decl;
bool is_simple; bool is_simple;
int argc; // cached argc
PyObject* _module; PyObject* _module;
NameDict_ _closure; NameDict_ _closure;
}; };

View File

@ -1001,12 +1001,11 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args,
const Function& fn = CAST(Function&, callable); const Function& fn = CAST(Function&, callable);
const CodeObject* co = fn.decl->code.get(); 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( vm->TypeError(fmt(
"expected ", "expected ",
fn.decl->args.size(), fn.argc,
" positional arguments, but got ", " positional arguments, but got ",
args.size(), args.size(),
" (", fn.decl->code->name, ')' " (", 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 // 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 // we can use a fast path to avoid using buffer copy
if(fn.is_simple && kwargs.size()==0){ if(fn.is_simple){
if(args.size() > fn.decl->args.size()) TypeError("too many positional arguments"); if(args.size() > fn.argc) TypeError("too many positional arguments");
int spaces = co->varnames.size() - fn.decl->args.size(); int spaces = co->varnames.size() - fn.argc;
for(int j=0; j<spaces; j++) PUSH(nullptr); for(int j=0; j<spaces; j++) PUSH(nullptr);
callstack.emplace(&s_data, p0, co, _module, callable, FastLocals(co, args.begin())); callstack.emplace(&s_data, p0, co, fn._module, callable, FastLocals(co, args.begin()));
return nullptr; return nullptr;
} }
@ -1062,7 +1061,7 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args,
if(co->is_generator){ if(co->is_generator){
PyObject* ret = PyIter(Generator( PyObject* ret = PyIter(Generator(
this, this,
Frame(&s_data, nullptr, co, _module, callable), Frame(&s_data, nullptr, co, fn._module, callable),
ArgsView(buffer, buffer + co->varnames.size()) ArgsView(buffer, buffer + co->varnames.size())
)); ));
return ret; return ret;
@ -1070,7 +1069,7 @@ inline PyObject* VM::_py_call(PyObject** p0, PyObject* callable, ArgsView args,
// copy buffer to stack // copy buffer to stack
for(int i=0; i<co->varnames.size(); i++) PUSH(buffer[i]); for(int i=0; i<co->varnames.size(); i++) PUSH(buffer[i]);
callstack.emplace(&s_data, p0, co, _module, callable); callstack.emplace(&s_data, p0, co, fn._module, callable);
return nullptr; return nullptr;
} }