update dev

This commit is contained in:
blueloveTH 2023-04-11 22:45:22 +08:00
parent c403252aec
commit 9c5b3167c8
14 changed files with 183 additions and 304 deletions

BIN
.github/workflows/main.rar vendored Normal file

Binary file not shown.

View File

@ -1,118 +0,0 @@
name: build
on: [push, pull_request]
jobs:
build_win:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: ilammy/msvc-dev-cmd@v1
- name: Compile
shell: bash
run: |
python3 build.py windows
python3 build.py windows -lib
mkdir -p output/windows/x86_64
cp pocketpy.exe output/windows/x86_64
cp pocketpy.dll output/windows/x86_64
- uses: actions/upload-artifact@v3
with:
path: output
- name: Unit Test
run: python3 scripts/run_tests.py
- name: Benchmark
run: python3 scripts/run_tests.py benchmark
build_linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Clang
uses: egor-tensin/setup-clang@v1
with:
version: 15
platform: x64
- name: Install libc++
run: sudo apt install -y libc++-15-dev libc++1-15 libc++abi-15-dev libc++abi1-15
- name: Compile
run: |
python3 build.py linux
python3 build.py linux -lib
mkdir -p output/linux/x86_64
cp pocketpy output/linux/x86_64
cp pocketpy.so output/linux/x86_64
- uses: actions/upload-artifact@v3
with:
path: output
- name: Unit Test
run: python3 scripts/run_tests.py
- name: Benchmark
run: python3 scripts/run_tests.py benchmark
build_macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- run: |
python3 amalgamate.py
cd plugins/macos/pocketpy
mkdir -p output/macos
xcodebuild clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
cp -r build/Release/pocketpy.bundle output/macos
- uses: actions/upload-artifact@v3
with:
path: plugins/macos/pocketpy/output
build_android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.3.0'
channel: 'stable'
cache: true
- run: flutter --version
- name: Compile
run: |
python3 amalgamate.py
cd plugins/flutter/example
flutter build apk --split-debug-info=.debug-info --split-per-abi
cd build/app/outputs/flutter-apk
mkdir -p output/android/arm64-v8a
mkdir -p output/android/armeabi-v7a
mkdir -p output/android/x86_64
unzip -q app-arm64-v8a-release.apk -d tmp
mv tmp/lib/arm64-v8a/libpocketpy.so output/android/arm64-v8a/libpocketpy.so
rm -rf tmp
unzip -q app-armeabi-v7a-release.apk -d tmp
mv tmp/lib/armeabi-v7a/libpocketpy.so output/android/armeabi-v7a/libpocketpy.so
rm -rf tmp
unzip -q app-x86_64-release.apk -d tmp
mv tmp/lib/x86_64/libpocketpy.so output/android/x86_64/libpocketpy.so
rm -rf tmp
- uses: actions/upload-artifact@v3
with:
path: plugins/flutter/example/build/app/outputs/flutter-apk/output
build_web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v12
with:
version: 3.1.25
actions-cache-folder: 'emsdk-cache'
- name: Verify emsdk
run: emcc -v
- name: Compile
run: |
mkdir -p output/web/lib
python3 build.py web
cp web/lib/* output/web/lib
- uses: crazy-max/ghaction-github-pages@v3
with:
target_branch: gh-pages
build_dir: web
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
- uses: actions/upload-artifact@v3
with:
path: output

View File

@ -79,9 +79,9 @@ __NEXT_STEP:;
FuncDecl_ decl = co->func_decls[byte.arg];
PyObject* obj;
if(decl->nested){
obj = VAR(Function({decl, frame->_module, frame->locals_to_namedict()}));
obj = VAR(Function({decl, frame->_module, frame->_locals}));
}else{
obj = VAR(Function({decl, frame->_module, nullptr}));
obj = VAR(Function({decl, frame->_module}));
}
frame->push(obj);
} DISPATCH();
@ -97,9 +97,9 @@ __NEXT_STEP:;
heap._auto_collect();
StrName name = co_names[byte.arg];
PyObject* val;
val = frame->f_locals_try_get(name);
val = frame->_locals.try_get(name);
if(val != nullptr) { frame->push(val); DISPATCH(); }
val = frame->f_closure_try_get(name);
val = frame->_closure.try_get(name);
if(val != nullptr) { frame->push(val); DISPATCH(); }
val = frame->f_globals().try_get(name);
if(val != nullptr) { frame->push(val); DISPATCH(); }
@ -322,10 +322,10 @@ __NEXT_STEP:;
frame->jump_abs_break(target);
} DISPATCH();
TARGET(GOTO) {
StrName label = co_names[byte.arg];
auto it = co->labels.find(label);
if(it == co->labels.end()) _error("KeyError", fmt("label ", label.escape(), " not found"));
frame->jump_abs_break(it->second);
StrName name = co_names[byte.arg];
int index = co->labels->try_get(name);
if(index < 0) _error("KeyError", fmt("label ", name.escape(), " not found"));
frame->jump_abs_break(index);
} DISPATCH();
/*****************************************/
TARGET(CALL)

View File

@ -327,57 +327,6 @@ struct Pointer{
};
struct Value {
PY_CLASS(Value, c, _value)
char* data;
Pointer head;
const TypeInfo* ctype() const { return head.ctype; }
Value(const TypeInfo* type) {
data = new char[type->size];
memset(data, 0, type->size);
this->head = Pointer(type, data);
}
Value(const TypeInfo* type, void* src) {
data = new char[type->size];
memcpy(data, src, type->size);
this->head = Pointer(type, data);
}
Value(Value&& other) noexcept {
data = other.data;
head = other.head;
other.data = nullptr;
}
Value& operator=(Value&& other) noexcept = delete;
Value& operator=(const Value& other) = delete;
Value(const Value& other) = delete;
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_static_method<-1>(type, "__new__", CPP_NOT_IMPLEMENTED());
vm->bind_method<0>(type, "ptr", [](VM* vm, Args& args) {
Value& self = CAST(Value&, args[0]);
return VAR_T(Pointer, self.head);
});
vm->bind_method<1>(type, "__getattr__", [](VM* vm, Args& args) {
Value& self = CAST(Value&, args[0]);
const Str& name = CAST(Str&, args[1]);
return self.head._to(vm, name).get(vm);
});
}
~Value(){
delete[] data;
}
};
struct CType{
PY_CLASS(CType, c, ctype)
@ -393,18 +342,12 @@ struct CType{
if(type == nullptr) vm->TypeError("unknown type: " + name.escape());
return VAR_T(CType, type);
});
vm->bind_method<0>(type, "__call__", [](VM* vm, Args& args) {
CType& self = CAST(CType&, args[0]);
return VAR_T(Value, self.type);
});
}
};
inline void add_module_c(VM* vm){
PyObject* mod = vm->new_module("c");
Pointer::register_class(vm, mod);
Value::register_class(vm, mod);
CType::register_class(vm, mod);
vm->setattr(mod, "nullptr", VAR_T(Pointer));
@ -500,13 +443,6 @@ T py_pointer_cast(VM* vm, PyObject* var){
return reinterpret_cast<T>(p.ptr);
}
template<typename T>
T py_value_cast(VM* vm, PyObject* var){
static_assert(std::is_pod_v<T>);
Value& v = CAST(Value&, var);
return *reinterpret_cast<T*>(v.data);
}
template<typename T>
std::enable_if_t<std::is_pointer_v<std::decay_t<T>>, PyObject*>
py_var(VM* vm, T p){
@ -514,13 +450,4 @@ py_var(VM* vm, T p){
if(type == nullptr) type = _type_db.get<void>();
return VAR_T(Pointer, type, pointer<T>::level, (char*)p);
}
template<typename T>
std::enable_if_t<!std::is_pointer_v<std::decay_t<T>>, PyObject*>
py_var(VM* vm, T p){
if constexpr(std::is_same_v<T, PyObject*>) return p;
const TypeInfo* type = _type_db.get<T>();
return VAR_T(Value, type, &p);
}
} // namespace pkpy

View File

@ -53,17 +53,20 @@ struct CodeObject {
bool is_generator = false;
CodeObject(shared_ptr<SourceData> src, const Str& name):
src(src), name(name) {}
src(src), name(name) {
varnames_inv = make_sp<NameDictInt>();
labels = make_sp<NameDictInt>();
}
std::vector<Bytecode> codes;
std::vector<int> lines; // line number for each bytecode
List consts;
std::vector<StrName> names; // other names
std::vector<StrName> varnames; // local variables
std::map<StrName, int> varnames_inv;
NameDictInt_ varnames_inv;
std::set<Str> global_names;
std::vector<CodeBlock> blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) };
std::map<StrName, int> labels;
NameDictInt_ labels;
std::vector<FuncDecl_> func_decls;
void optimize(VM* vm);

View File

@ -75,6 +75,7 @@ namespace std = ::std;
struct Dummy { };
struct DummyInstance { };
struct DummyModule { };
struct Discarded { };
struct Type {
int index;

View File

@ -91,10 +91,8 @@ struct CodeEmitContext{
co->codes[index].arg = target;
}
bool add_label(StrName label){
if(co->labels.count(label)) return false;
co->labels[label] = co->codes.size();
return true;
bool add_label(StrName name){
return co->labels->try_set(name, co->codes.size());
}
int add_name(StrName name){
@ -106,11 +104,11 @@ struct CodeEmitContext{
}
int add_varname(StrName name){
auto it = co->varnames_inv.find(name);
if(it != co->varnames_inv.end()) return it->second;
int index = co->varnames_inv->try_get(name);
if(index >= 0) return index;
co->varnames.push_back(name);
int index = co->varnames.size() - 1;
co->varnames_inv[name] = index;
index = co->varnames.size() - 1;
co->varnames_inv->set(name, index);
return index;
}

View File

@ -7,49 +7,123 @@
namespace pkpy{
using ValueStack = pod_vector<PyObject*>;
using FastLocals = pod_vector<PyObject*>;
struct FastLocals{
NameDictInt_ varnames_inv;
PyObject** a;
int size() const{ return varnames_inv->size(); }
PyObject*& operator[](int i){ return a[i]; }
PyObject* operator[](int i) const { return a[i]; }
FastLocals(const CodeObject* co): varnames_inv(co->varnames_inv){
size_t size = co->varnames.size() * sizeof(void*);
int* counter = (int*)pool128.alloc(sizeof(int) + size);
*counter = 1;
a = (PyObject**)(counter + 1);
memset(a, 0, size);
}
PyObject* try_get(StrName name){
if(!is_valid()) return nullptr;
int index = varnames_inv->try_get(name);
if(index == -1) return nullptr;
return a[index];
}
bool try_set(StrName name, PyObject* value){
if(!is_valid()) return false;
int index = varnames_inv->try_get(name);
if(index == -1) return false;
a[index] = value;
return true;
}
FastLocals(): varnames_inv(nullptr), a(nullptr) {}
FastLocals(const FastLocals& other){
a = other.a;
inc_counter();
}
FastLocals(FastLocals&& other){
a = other.a;
other.a = nullptr;
}
FastLocals& operator=(const FastLocals& other){
dec_counter();
a = other.a;
inc_counter();
return *this;
}
FastLocals& operator=(FastLocals&& other) noexcept{
dec_counter();
a = other.a;
other.a = nullptr;
return *this;
}
bool is_valid() const{ return a != nullptr; }
void inc_counter(){
if(a == nullptr) return;
int* counter = (int*)a - 1;
(*counter)++;
}
void dec_counter(){
if(a == nullptr) return;
int* counter = (int*)a - 1;
(*counter)--;
if(*counter == 0){
pool128.dealloc(counter);
}
}
~FastLocals(){
dec_counter();
}
void _gc_mark() const{
if(a == nullptr) return;
for(int i=0; i<size(); i++){
if(a[i] != nullptr) OBJ_MARK(a[i]);
}
}
};
struct Function{
FuncDecl_ decl;
PyObject* _module;
FastLocals _closure;
};
template<> inline void gc_mark<Function>(Function& t){
t.decl->_gc_mark();
if(t._module != nullptr) OBJ_MARK(t._module);
t._closure._gc_mark();
}
struct Frame {
ValueStack _data;
int _ip = -1;
int _next_ip = 0;
const CodeObject* co;
PyObject* _module;
FastLocals _locals;
NameDict_ _closure;
PyObject* f_locals_try_get(StrName name){
auto it = co->varnames_inv.find(name);
if(it == co->varnames_inv.end()) return nullptr;
return _locals[it->second];
}
FastLocals _locals;
FastLocals _closure;
NameDict& f_globals() noexcept { return _module->attr(); }
PyObject* f_closure_try_get(StrName name){
if(_closure == nullptr) return nullptr;
return _closure->try_get(name);
}
NameDict_ locals_to_namedict() const{
NameDict_ dict = make_sp<NameDict>();
for(int i=0; i<co->varnames.size(); i++){
if(_locals[i] != nullptr) dict->set(co->varnames[i], _locals[i]);
}
return dict;
}
Frame(const CodeObject* co, PyObject* _module, FastLocals&& _locals, const NameDict_& _closure)
: co(co), _module(_module), _locals(std::move(_locals)), _closure(_closure) {
}
Frame(const CodeObject* co, PyObject* _module)
: co(co), _module(_module), _locals(), _closure(nullptr) {
}
Frame(const CodeObject* co, PyObject* _module, FastLocals&& _locals, const FastLocals& _closure)
: co(co), _module(_module), _locals(std::move(_locals)), _closure(_closure) { }
Frame(const CodeObject_& co, PyObject* _module)
: co(co.get()), _module(_module), _locals(), _closure(nullptr) {
}
: co(co.get()), _module(_module), _locals(), _closure() { }
Frame(const Frame& other) = delete;
Frame& operator=(const Frame& other) = delete;
@ -177,11 +251,8 @@ struct Frame {
if(_data._data == nullptr) return;
for(PyObject* obj : _data) OBJ_MARK(obj);
OBJ_MARK(_module);
// _locals may be move for `eval/exec`
if(_locals._data != nullptr){
for(PyObject* obj: _locals) OBJ_MARK(obj);
}
if(_closure != nullptr) _closure->_gc_mark();
_locals._gc_mark();
_closure._gc_mark();
co->_gc_mark();
}
};

View File

@ -110,43 +110,37 @@ struct ManagedHeap{
void mark();
};
inline void NameDict::_gc_mark() const{
if(size() == 0) return;
for(uint16_t i=0; i<_capacity; i++){
if(_items[i].first.empty()) continue;
OBJ_MARK(_items[i].second);
}
}
inline void FuncDecl::_gc_mark() const{
code->_gc_mark();
for(int i=0; i<kwargs.size(); i++) OBJ_MARK(kwargs[i].value);
}
template<> inline void _gc_mark<List>(List& t){
template<> inline void gc_mark<List>(List& t){
for(PyObject* obj: t) OBJ_MARK(obj);
}
template<> inline void _gc_mark<Tuple>(Tuple& t){
template<> inline void gc_mark<Tuple>(Tuple& t){
for(int i=0; i<t.size(); i++) OBJ_MARK(t[i]);
}
template<> inline void _gc_mark<Function>(Function& t){
t.decl->_gc_mark();
if(t._module != nullptr) OBJ_MARK(t._module);
if(t._closure != nullptr) t._closure->_gc_mark();
template<> inline void gc_mark<NameDict>(NameDict& t){
if(t.size() == 0) return;
for(uint16_t i=0; i<t._capacity; i++){
if(t._items[i].first.empty()) continue;
OBJ_MARK(t._items[i].second);
}
}
template<> inline void _gc_mark<BoundMethod>(BoundMethod& t){
template<> inline void gc_mark<BoundMethod>(BoundMethod& t){
OBJ_MARK(t.obj);
OBJ_MARK(t.method);
}
template<> inline void _gc_mark<StarWrapper>(StarWrapper& t){
template<> inline void gc_mark<StarWrapper>(StarWrapper& t){
OBJ_MARK(t.obj);
}
template<> inline void _gc_mark<Super>(Super& t){
template<> inline void gc_mark<Super>(Super& t){
OBJ_MARK(t.first);
}
// NOTE: std::function may capture some PyObject*, they can not be marked

View File

@ -81,7 +81,7 @@ inline void Generator::_gc_mark() const{
}
template<typename T>
void _gc_mark(T& t) {
void gc_mark(T& t) {
if constexpr(std::is_base_of_v<BaseIter, T>){
t._gc_mark();
}

View File

@ -8,7 +8,7 @@ namespace pkpy{
const std::vector<uint16_t> kHashSeeds = {9629, 43049, 13267, 59509, 39251, 1249, 35803, 54469, 27689, 9719, 34897, 18973, 30661, 19913, 27919, 32143, 3467, 28019, 1051, 39419, 1361, 28547, 48197, 2609, 24317, 22861, 41467, 17623, 52837, 59053, 33589, 32117};
inline static uint16_t find_next_capacity(uint16_t n){
inline uint16_t find_next_power_of_2(uint16_t n){
uint16_t x = 2;
while(x < n) x <<= 1;
return x;
@ -16,7 +16,7 @@ inline static uint16_t find_next_capacity(uint16_t n){
#define _hash(key, mask, hash_seed) ( ( (key).index * (hash_seed) >> 8 ) & (mask) )
inline static uint16_t find_perfect_hash_seed(uint16_t capacity, const std::vector<StrName>& keys){
inline uint16_t find_perfect_hash_seed(uint16_t capacity, const std::vector<StrName>& keys){
if(keys.empty()) return kHashSeeds[0];
std::set<uint16_t> indices;
std::pair<uint16_t, float> best_score = {kHashSeeds[0], 0.0f};
@ -32,10 +32,14 @@ inline static uint16_t find_perfect_hash_seed(uint16_t capacity, const std::vect
return best_score.first;
}
struct NameDict {
using Item = std::pair<StrName, PyObject*>;
static constexpr uint16_t __Capacity = 128/sizeof(Item);
static_assert( (__Capacity & (__Capacity-1)) == 0, "__Capacity must be power of 2" );
template<typename T>
struct NameDictImpl {
using Item = std::pair<StrName, T>;
static constexpr uint16_t __Capacity = 8;
// ensure the initial capacity is ok for memory pool
static_assert(std::is_pod_v<T>);
static_assert(sizeof(Item) * __Capacity <= 128);
float _load_factor;
uint16_t _capacity;
uint16_t _size;
@ -48,23 +52,23 @@ struct NameDict {
memset(_items, 0, cap * sizeof(Item));
}
NameDict(float load_factor=0.67, uint16_t capacity=__Capacity, uint16_t hash_seed=kHashSeeds[0]):
NameDictImpl(float load_factor=0.67, uint16_t capacity=__Capacity, uint16_t hash_seed=kHashSeeds[0]):
_load_factor(load_factor), _capacity(capacity), _size(0),
_hash_seed(hash_seed), _mask(capacity-1) {
_alloc(capacity);
}
NameDict(const NameDict& other) {
memcpy(this, &other, sizeof(NameDict));
NameDictImpl(const NameDictImpl& other) {
memcpy(this, &other, sizeof(NameDictImpl));
_alloc(_capacity);
for(int i=0; i<_capacity; i++){
_items[i] = other._items[i];
}
}
NameDict& operator=(const NameDict& other) {
NameDictImpl& operator=(const NameDictImpl& other) {
pool128.dealloc(_items);
memcpy(this, &other, sizeof(NameDict));
memcpy(this, &other, sizeof(NameDictImpl));
_alloc(_capacity);
for(int i=0; i<_capacity; i++){
_items[i] = other._items[i];
@ -72,10 +76,10 @@ struct NameDict {
return *this;
}
~NameDict(){ pool128.dealloc(_items); }
~NameDictImpl(){ pool128.dealloc(_items); }
NameDict(NameDict&&) = delete;
NameDict& operator=(NameDict&&) = delete;
NameDictImpl(NameDictImpl&&) = delete;
NameDictImpl& operator=(NameDictImpl&&) = delete;
uint16_t size() const { return _size; }
#define HASH_PROBE(key, ok, i) \
@ -86,14 +90,14 @@ while(!_items[i].first.empty()) { \
i = (i + 1) & _mask; \
}
PyObject* operator[](StrName key) const {
T operator[](StrName key) const {
bool ok; uint16_t i;
HASH_PROBE(key, ok, i);
if(!ok) throw std::out_of_range(fmt("NameDict key not found: ", key));
return _items[i].second;
}
void set(StrName key, PyObject* val){
void set(StrName key, T val){
bool ok; uint16_t i;
HASH_PROBE(key, ok, i);
if(!ok) {
@ -111,7 +115,7 @@ while(!_items[i].first.empty()) { \
Item* old_items = _items;
uint16_t old_capacity = _capacity;
if(resize){
_capacity = find_next_capacity(_capacity * 2);
_capacity = find_next_power_of_2(_capacity * 2);
_mask = _capacity - 1;
}
_alloc(_capacity);
@ -130,14 +134,18 @@ while(!_items[i].first.empty()) { \
_rehash(false); // do not resize
}
PyObject* try_get(StrName key) const{
T try_get(StrName key) const{
bool ok; uint16_t i;
HASH_PROBE(key, ok, i);
if(!ok) return nullptr;
if(!ok){
if constexpr(std::is_pointer_v<T>) return nullptr;
else if constexpr(std::is_same_v<int, T>) return -1;
else return Discarded();
}
return _items[i].second;
}
bool try_set(StrName key, PyObject* val){
bool try_set(StrName key, T val){
bool ok; uint16_t i;
HASH_PROBE(key, ok, i);
if(!ok) return false;
@ -151,7 +159,7 @@ while(!_items[i].first.empty()) { \
return ok;
}
void update(const NameDict& other){
void update(const NameDictImpl& other){
for(uint16_t i=0; i<other._capacity; i++){
auto& item = other._items[i];
if(!item.first.empty()) set(item.first, item.second);
@ -184,10 +192,13 @@ while(!_items[i].first.empty()) { \
}
return v;
}
void _gc_mark() const;
#undef HASH_PROBE
#undef _hash
};
using NameDict = NameDictImpl<PyObject*>;
using NameDict_ = shared_ptr<NameDict>;
using NameDictInt = NameDictImpl<int>;
using NameDictInt_ = shared_ptr<NameDictInt>;
} // namespace pkpy

View File

@ -8,11 +8,11 @@ namespace pkpy {
struct CodeObject;
struct Frame;
struct Function;
class VM;
typedef std::function<PyObject*(VM*, Args&)> NativeFuncRaw;
typedef shared_ptr<CodeObject> CodeObject_;
typedef shared_ptr<NameDict> NameDict_;
struct NativeFunc {
NativeFuncRaw f;
@ -38,12 +38,6 @@ struct FuncDecl {
using FuncDecl_ = shared_ptr<FuncDecl>;
struct Function{
FuncDecl_ decl;
PyObject* _module;
NameDict_ _closure;
};
struct BoundMethod {
PyObject* obj;
PyObject* method;
@ -114,7 +108,7 @@ struct PyObject {
};
template<typename T>
void _gc_mark(T& t);
void gc_mark(T& t);
template <typename T>
struct Py_ : PyObject {
@ -139,8 +133,8 @@ struct Py_ : PyObject {
void _obj_gc_mark() override {
if(gc.marked) return;
gc.marked = true;
if(_attr != nullptr) _attr->_gc_mark();
pkpy::_gc_mark<T>(_value); // handle PyObject* inside _value `T`
if(_attr != nullptr) pkpy::gc_mark<NameDict>(*_attr);
pkpy::gc_mark<T>(_value); // handle PyObject* inside _value `T`
}
};
@ -196,7 +190,6 @@ template <typename T> struct is_py_class<T, std::void_t<decltype(T::_type)>> : s
template<typename T> void _check_py_class(VM*, PyObject*);
template<typename T> T py_pointer_cast(VM*, PyObject*);
template<typename T> T py_value_cast(VM*, PyObject*);
struct Discarded { };
template<typename __T>
__T py_cast(VM* vm, PyObject* obj) {

View File

@ -114,6 +114,7 @@ inline void init_builtins(VM* _vm) {
// moving _locals is dangerous since OP_LOAD_FAST's arg is index of _locals
// the new opcode may not generate the same index, or even not a OP_LOAD_FAST call
// we should do some special handling here
// seems LOAD_NAME / STORE_NAME / DELETE_NAME are designed for this?
vm->_push_new_frame(code.get(), frame->_module, std::move(frame->_locals), nullptr);
vm->_run_top_frame(true);
frame->_locals = std::move(vm->top_frame()->_locals);

View File

@ -700,10 +700,7 @@ inline PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, boo
} else if(is_type(callable, tp_function)){
const Function& fn = CAST(Function&, callable);
const CodeObject* co = fn.decl->code.get();
// create a FastLocals with the same size as co->varnames
FastLocals locals(co->varnames.size());
// zero init
for(auto& v: locals) v = nullptr;
FastLocals locals(co);
int i = 0;
for(int index: fn.decl->args){
@ -737,11 +734,12 @@ inline PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, boo
for(int i=0; i<kwargs.size(); i+=2){
StrName key = CAST(int, kwargs[i]);
auto it = co->varnames_inv.find(key);
if(it == co->varnames_inv.end()){
// try_set has nullptr check
// TODO: optimize this
bool ok = locals.try_set(key, kwargs[i+1]);
if(!ok){
TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()"));
}
locals[it->second] = kwargs[i+1];
}
PyObject* _module = fn._module != nullptr ? fn._module : top_frame()->_module;
if(co->is_generator){