diff --git a/.github/workflows/main.rar b/.github/workflows/main.rar new file mode 100644 index 00000000..13d23159 Binary files /dev/null and b/.github/workflows/main.rar differ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 3f310555..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/ceval.h b/src/ceval.h index 277859a9..86cb8d23 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -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) diff --git a/src/cffi.h b/src/cffi.h index 1cb5e36b..521ecb0b 100644 --- a/src/cffi.h +++ b/src/cffi.h @@ -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(p.ptr); } -template -T py_value_cast(VM* vm, PyObject* var){ - static_assert(std::is_pod_v); - Value& v = CAST(Value&, var); - return *reinterpret_cast(v.data); -} - template std::enable_if_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(); return VAR_T(Pointer, type, pointer::level, (char*)p); } - -template -std::enable_if_t>, PyObject*> -py_var(VM* vm, T p){ - if constexpr(std::is_same_v) return p; - const TypeInfo* type = _type_db.get(); - return VAR_T(Value, type, &p); -} - } // namespace pkpy \ No newline at end of file diff --git a/src/codeobject.h b/src/codeobject.h index 8520b70f..4ebadb3e 100644 --- a/src/codeobject.h +++ b/src/codeobject.h @@ -53,17 +53,20 @@ struct CodeObject { bool is_generator = false; CodeObject(shared_ptr src, const Str& name): - src(src), name(name) {} + src(src), name(name) { + varnames_inv = make_sp(); + labels = make_sp(); + } std::vector codes; std::vector lines; // line number for each bytecode List consts; std::vector names; // other names std::vector varnames; // local variables - std::map varnames_inv; + NameDictInt_ varnames_inv; std::set global_names; std::vector blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) }; - std::map labels; + NameDictInt_ labels; std::vector func_decls; void optimize(VM* vm); diff --git a/src/common.h b/src/common.h index ce726b1a..a0eb92b1 100644 --- a/src/common.h +++ b/src/common.h @@ -75,6 +75,7 @@ namespace std = ::std; struct Dummy { }; struct DummyInstance { }; struct DummyModule { }; +struct Discarded { }; struct Type { int index; diff --git a/src/expr.h b/src/expr.h index 39f4d02d..20d7cb4f 100644 --- a/src/expr.h +++ b/src/expr.h @@ -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; } diff --git a/src/frame.h b/src/frame.h index f7ac487f..ffb4fd9b 100644 --- a/src/frame.h +++ b/src/frame.h @@ -7,49 +7,123 @@ namespace pkpy{ using ValueStack = pod_vector; -using FastLocals = pod_vector; + +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 inline void gc_mark(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(); - for(int i=0; ivarnames.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(); } }; diff --git a/src/gc.h b/src/gc.h index 98a8b4ef..bfa9320d 100644 --- a/src/gc.h +++ b/src/gc.h @@ -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 inline void _gc_mark(List& t){ +template<> inline void gc_mark(List& t){ for(PyObject* obj: t) OBJ_MARK(obj); } -template<> inline void _gc_mark(Tuple& t){ +template<> inline void gc_mark(Tuple& t){ for(int i=0; i inline void _gc_mark(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& t){ + if(t.size() == 0) return; + for(uint16_t i=0; i inline void _gc_mark(BoundMethod& t){ +template<> inline void gc_mark(BoundMethod& t){ OBJ_MARK(t.obj); OBJ_MARK(t.method); } -template<> inline void _gc_mark(StarWrapper& t){ +template<> inline void gc_mark(StarWrapper& t){ OBJ_MARK(t.obj); } -template<> inline void _gc_mark(Super& t){ +template<> inline void gc_mark(Super& t){ OBJ_MARK(t.first); } // NOTE: std::function may capture some PyObject*, they can not be marked diff --git a/src/iter.h b/src/iter.h index 457cb010..975ab441 100644 --- a/src/iter.h +++ b/src/iter.h @@ -81,7 +81,7 @@ inline void Generator::_gc_mark() const{ } template -void _gc_mark(T& t) { +void gc_mark(T& t) { if constexpr(std::is_base_of_v){ t._gc_mark(); } diff --git a/src/namedict.h b/src/namedict.h index c98e348e..6406a809 100644 --- a/src/namedict.h +++ b/src/namedict.h @@ -8,7 +8,7 @@ namespace pkpy{ const std::vector 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& keys){ +inline uint16_t find_perfect_hash_seed(uint16_t capacity, const std::vector& keys){ if(keys.empty()) return kHashSeeds[0]; std::set indices; std::pair 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; - static constexpr uint16_t __Capacity = 128/sizeof(Item); - static_assert( (__Capacity & (__Capacity-1)) == 0, "__Capacity must be power of 2" ); +template +struct NameDictImpl { + using Item = std::pair; + static constexpr uint16_t __Capacity = 8; + // ensure the initial capacity is ok for memory pool + static_assert(std::is_pod_v); + 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) return nullptr; + else if constexpr(std::is_same_v) 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; +using NameDict_ = shared_ptr; +using NameDictInt = NameDictImpl; +using NameDictInt_ = shared_ptr; + } // namespace pkpy \ No newline at end of file diff --git a/src/obj.h b/src/obj.h index 21f19992..4d41c4fe 100644 --- a/src/obj.h +++ b/src/obj.h @@ -8,11 +8,11 @@ namespace pkpy { struct CodeObject; struct Frame; +struct Function; class VM; typedef std::function NativeFuncRaw; typedef shared_ptr CodeObject_; -typedef shared_ptr NameDict_; struct NativeFunc { NativeFuncRaw f; @@ -38,12 +38,6 @@ struct FuncDecl { using FuncDecl_ = shared_ptr; -struct Function{ - FuncDecl_ decl; - PyObject* _module; - NameDict_ _closure; -}; - struct BoundMethod { PyObject* obj; PyObject* method; @@ -114,7 +108,7 @@ struct PyObject { }; template -void _gc_mark(T& t); +void gc_mark(T& t); template 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(_value); // handle PyObject* inside _value `T` + if(_attr != nullptr) pkpy::gc_mark(*_attr); + pkpy::gc_mark(_value); // handle PyObject* inside _value `T` } }; @@ -196,7 +190,6 @@ template struct is_py_class> : s template void _check_py_class(VM*, PyObject*); template T py_pointer_cast(VM*, PyObject*); template T py_value_cast(VM*, PyObject*); -struct Discarded { }; template __T py_cast(VM* vm, PyObject* obj) { diff --git a/src/pocketpy.h b/src/pocketpy.h index 4ae67c7e..806f65e6 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -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); diff --git a/src/vm.h b/src/vm.h index 7f2b29cc..547fbb3a 100644 --- a/src/vm.h +++ b/src/vm.h @@ -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; ivarnames_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){