blueloveTH 2 lat temu
rodzic
commit
9c5b3167c8
14 zmienionych plików z 181 dodań i 302 usunięć
  1. BIN
      .github/workflows/main.rar
  2. 0 118
      .github/workflows/main.yml
  3. 8 8
      src/ceval.h
  4. 0 73
      src/cffi.h
  5. 6 3
      src/codeobject.h
  6. 1 0
      src/common.h
  7. 6 8
      src/expr.h
  8. 104 33
      src/frame.h
  9. 11 17
      src/gc.h
  10. 1 1
      src/iter.h
  11. 34 23
      src/namedict.h
  12. 4 11
      src/obj.h
  13. 1 0
      src/pocketpy.h
  14. 5 7
      src/vm.h

BIN
.github/workflows/main.rar


+ 0 - 118
.github/workflows/main.yml

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

+ 8 - 8
src/ceval.h

@@ -79,9 +79,9 @@ __NEXT_STEP:;
         FuncDecl_ decl = co->func_decls[byte.arg];
         FuncDecl_ decl = co->func_decls[byte.arg];
         PyObject* obj;
         PyObject* obj;
         if(decl->nested){
         if(decl->nested){
-            obj = VAR(Function({decl, frame->_module, frame->locals_to_namedict()}));
+            obj = VAR(Function({decl, frame->_module, frame->_locals}));
         }else{
         }else{
-            obj = VAR(Function({decl, frame->_module, nullptr}));
+            obj = VAR(Function({decl, frame->_module}));
         }
         }
         frame->push(obj);
         frame->push(obj);
     } DISPATCH();
     } DISPATCH();
@@ -97,9 +97,9 @@ __NEXT_STEP:;
         heap._auto_collect();
         heap._auto_collect();
         StrName name = co_names[byte.arg];
         StrName name = co_names[byte.arg];
         PyObject* val;
         PyObject* val;
-        val = frame->f_locals_try_get(name);
+        val = frame->_locals.try_get(name);
         if(val != nullptr) { frame->push(val); DISPATCH(); }
         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(); }
         if(val != nullptr) { frame->push(val); DISPATCH(); }
         val = frame->f_globals().try_get(name);
         val = frame->f_globals().try_get(name);
         if(val != nullptr) { frame->push(val); DISPATCH(); }
         if(val != nullptr) { frame->push(val); DISPATCH(); }
@@ -322,10 +322,10 @@ __NEXT_STEP:;
         frame->jump_abs_break(target);
         frame->jump_abs_break(target);
     } DISPATCH();
     } DISPATCH();
     TARGET(GOTO) {
     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();
     } DISPATCH();
     /*****************************************/
     /*****************************************/
     TARGET(CALL)
     TARGET(CALL)

+ 0 - 73
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{
 struct CType{
     PY_CLASS(CType, c, ctype)
     PY_CLASS(CType, c, ctype)
 
 
@@ -393,18 +342,12 @@ struct CType{
             if(type == nullptr) vm->TypeError("unknown type: " + name.escape());
             if(type == nullptr) vm->TypeError("unknown type: " + name.escape());
             return VAR_T(CType, type);
             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){
 inline void add_module_c(VM* vm){
     PyObject* mod = vm->new_module("c");
     PyObject* mod = vm->new_module("c");
     Pointer::register_class(vm, mod);
     Pointer::register_class(vm, mod);
-    Value::register_class(vm, mod);
     CType::register_class(vm, mod);
     CType::register_class(vm, mod);
 
 
     vm->setattr(mod, "nullptr", VAR_T(Pointer));
     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);
     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>
 template<typename T>
 std::enable_if_t<std::is_pointer_v<std::decay_t<T>>, PyObject*>
 std::enable_if_t<std::is_pointer_v<std::decay_t<T>>, PyObject*>
 py_var(VM* vm, T p){
 py_var(VM* vm, T p){
@@ -514,13 +450,4 @@ py_var(VM* vm, T p){
     if(type == nullptr) type = _type_db.get<void>();
     if(type == nullptr) type = _type_db.get<void>();
     return VAR_T(Pointer, type, pointer<T>::level, (char*)p);
     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
 }   // namespace pkpy

+ 6 - 3
src/codeobject.h

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

+ 1 - 0
src/common.h

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

+ 6 - 8
src/expr.h

@@ -91,10 +91,8 @@ struct CodeEmitContext{
         co->codes[index].arg = target;
         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){
     int add_name(StrName name){
@@ -106,11 +104,11 @@ struct CodeEmitContext{
     }
     }
 
 
     int add_varname(StrName name){
     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);
         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;
         return index;
     }
     }
 
 

+ 104 - 33
src/frame.h

@@ -7,49 +7,123 @@
 namespace pkpy{
 namespace pkpy{
 
 
 using ValueStack = pod_vector<PyObject*>;
 using ValueStack = pod_vector<PyObject*>;
-using FastLocals = pod_vector<PyObject*>;
 
 
-struct Frame {
-    ValueStack _data;
-    int _ip = -1;
-    int _next_ip = 0;
+struct FastLocals{
+    NameDictInt_ varnames_inv;
+    PyObject** a;
 
 
-    const CodeObject* co;
-    PyObject* _module;
-    FastLocals _locals;
-    NameDict_ _closure;
+    int size() const{ return varnames_inv->size(); }
+
+    PyObject*& operator[](int i){ return a[i]; }
+    PyObject* operator[](int i) const { return a[i]; }
 
 
-    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(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);
     }
     }
 
 
-    NameDict& f_globals() noexcept { return _module->attr(); }
-    PyObject* f_closure_try_get(StrName name){
-        if(_closure == nullptr) return nullptr;
-        return _closure->try_get(name);
+    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)++;
     }
     }
 
 
-    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]);
+    void dec_counter(){
+        if(a == nullptr) return;
+        int* counter = (int*)a - 1;
+        (*counter)--;
+        if(*counter == 0){
+            pool128.dealloc(counter);
         }
         }
-        return dict;
     }
     }
 
 
-    Frame(const CodeObject* co, PyObject* _module, FastLocals&& _locals, const NameDict_& _closure)
-            : co(co), _module(_module), _locals(std::move(_locals)), _closure(_closure) {
+    ~FastLocals(){
+        dec_counter();
     }
     }
 
 
-    Frame(const CodeObject* co, PyObject* _module)
-            : co(co), _module(_module), _locals(), _closure(nullptr) {
+    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;
+    FastLocals _closure;
+
+    NameDict& f_globals() noexcept { return _module->attr(); }
+
+    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)
     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(const Frame& other) = delete;
     Frame& operator=(const Frame& other) = delete;
     Frame& operator=(const Frame& other) = delete;
@@ -177,11 +251,8 @@ struct Frame {
         if(_data._data == nullptr) return;
         if(_data._data == nullptr) return;
         for(PyObject* obj : _data) OBJ_MARK(obj);
         for(PyObject* obj : _data) OBJ_MARK(obj);
         OBJ_MARK(_module);
         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();
         co->_gc_mark();
     }
     }
 };
 };

+ 11 - 17
src/gc.h

@@ -110,43 +110,37 @@ struct ManagedHeap{
     void mark();
     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{
 inline void FuncDecl::_gc_mark() const{
     code->_gc_mark();
     code->_gc_mark();
     for(int i=0; i<kwargs.size(); i++) OBJ_MARK(kwargs[i].value);
     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);
     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]);
     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.obj);
     OBJ_MARK(t.method);
     OBJ_MARK(t.method);
 }
 }
 
 
-template<> inline void _gc_mark<StarWrapper>(StarWrapper& t){
+template<> inline void gc_mark<StarWrapper>(StarWrapper& t){
     OBJ_MARK(t.obj);
     OBJ_MARK(t.obj);
 }
 }
 
 
-template<> inline void _gc_mark<Super>(Super& t){
+template<> inline void gc_mark<Super>(Super& t){
     OBJ_MARK(t.first);
     OBJ_MARK(t.first);
 }
 }
 // NOTE: std::function may capture some PyObject*, they can not be marked
 // NOTE: std::function may capture some PyObject*, they can not be marked

+ 1 - 1
src/iter.h

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

+ 34 - 23
src/namedict.h

@@ -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};
 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;
     uint16_t x = 2;
     while(x < n) x <<= 1;
     while(x < n) x <<= 1;
     return x;
     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) )
 #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];
     if(keys.empty()) return kHashSeeds[0];
     std::set<uint16_t> indices;
     std::set<uint16_t> indices;
     std::pair<uint16_t, float> best_score = {kHashSeeds[0], 0.0f};
     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;
     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;
     float _load_factor;
     uint16_t _capacity;
     uint16_t _capacity;
     uint16_t _size;
     uint16_t _size;
@@ -48,23 +52,23 @@ struct NameDict {
         memset(_items, 0, cap * sizeof(Item));
         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), 
         _load_factor(load_factor), _capacity(capacity), _size(0), 
         _hash_seed(hash_seed), _mask(capacity-1) {
         _hash_seed(hash_seed), _mask(capacity-1) {
         _alloc(capacity);
         _alloc(capacity);
     }
     }
 
 
-    NameDict(const NameDict& other) {
-        memcpy(this, &other, sizeof(NameDict));
+    NameDictImpl(const NameDictImpl& other) {
+        memcpy(this, &other, sizeof(NameDictImpl));
         _alloc(_capacity);
         _alloc(_capacity);
         for(int i=0; i<_capacity; i++){
         for(int i=0; i<_capacity; i++){
             _items[i] = other._items[i];
             _items[i] = other._items[i];
         }
         }
     }
     }
 
 
-    NameDict& operator=(const NameDict& other) {
+    NameDictImpl& operator=(const NameDictImpl& other) {
         pool128.dealloc(_items);
         pool128.dealloc(_items);
-        memcpy(this, &other, sizeof(NameDict));
+        memcpy(this, &other, sizeof(NameDictImpl));
         _alloc(_capacity);
         _alloc(_capacity);
         for(int i=0; i<_capacity; i++){
         for(int i=0; i<_capacity; i++){
             _items[i] = other._items[i];
             _items[i] = other._items[i];
@@ -72,10 +76,10 @@ struct NameDict {
         return *this;
         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; }
     uint16_t size() const { return _size; }
 
 
 #define HASH_PROBE(key, ok, i)          \
 #define HASH_PROBE(key, ok, i)          \
@@ -86,14 +90,14 @@ while(!_items[i].first.empty()) {       \
     i = (i + 1) & _mask;                                \
     i = (i + 1) & _mask;                                \
 }
 }
 
 
-    PyObject* operator[](StrName key) const {
+    T operator[](StrName key) const {
         bool ok; uint16_t i;
         bool ok; uint16_t i;
         HASH_PROBE(key, ok, i);
         HASH_PROBE(key, ok, i);
         if(!ok) throw std::out_of_range(fmt("NameDict key not found: ", key));
         if(!ok) throw std::out_of_range(fmt("NameDict key not found: ", key));
         return _items[i].second;
         return _items[i].second;
     }
     }
 
 
-    void set(StrName key, PyObject* val){
+    void set(StrName key, T val){
         bool ok; uint16_t i;
         bool ok; uint16_t i;
         HASH_PROBE(key, ok, i);
         HASH_PROBE(key, ok, i);
         if(!ok) {
         if(!ok) {
@@ -111,7 +115,7 @@ while(!_items[i].first.empty()) {       \
         Item* old_items = _items;
         Item* old_items = _items;
         uint16_t old_capacity = _capacity;
         uint16_t old_capacity = _capacity;
         if(resize){
         if(resize){
-            _capacity = find_next_capacity(_capacity * 2);
+            _capacity = find_next_power_of_2(_capacity * 2);
             _mask = _capacity - 1;
             _mask = _capacity - 1;
         }
         }
         _alloc(_capacity);
         _alloc(_capacity);
@@ -130,14 +134,18 @@ while(!_items[i].first.empty()) {       \
         _rehash(false); // do not resize
         _rehash(false); // do not resize
     }
     }
 
 
-    PyObject* try_get(StrName key) const{
+    T try_get(StrName key) const{
         bool ok; uint16_t i;
         bool ok; uint16_t i;
         HASH_PROBE(key, ok, 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;
         return _items[i].second;
     }
     }
 
 
-    bool try_set(StrName key, PyObject* val){
+    bool try_set(StrName key, T val){
         bool ok; uint16_t i;
         bool ok; uint16_t i;
         HASH_PROBE(key, ok, i);
         HASH_PROBE(key, ok, i);
         if(!ok) return false;
         if(!ok) return false;
@@ -151,7 +159,7 @@ while(!_items[i].first.empty()) {       \
         return ok;
         return ok;
     }
     }
 
 
-    void update(const NameDict& other){
+    void update(const NameDictImpl& other){
         for(uint16_t i=0; i<other._capacity; i++){
         for(uint16_t i=0; i<other._capacity; i++){
             auto& item = other._items[i];
             auto& item = other._items[i];
             if(!item.first.empty()) set(item.first, item.second);
             if(!item.first.empty()) set(item.first, item.second);
@@ -184,10 +192,13 @@ while(!_items[i].first.empty()) {       \
         }
         }
         return v;
         return v;
     }
     }
-
-    void _gc_mark() const;
 #undef HASH_PROBE
 #undef HASH_PROBE
 #undef _hash
 #undef _hash
 };
 };
 
 
+using NameDict = NameDictImpl<PyObject*>;
+using NameDict_ = shared_ptr<NameDict>;
+using NameDictInt = NameDictImpl<int>;
+using NameDictInt_ = shared_ptr<NameDictInt>;
+
 } // namespace pkpy
 } // namespace pkpy

+ 4 - 11
src/obj.h

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

+ 1 - 0
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
         // 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
         // the new opcode may not generate the same index, or even not a OP_LOAD_FAST call
         // we should do some special handling here
         // 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->_push_new_frame(code.get(), frame->_module, std::move(frame->_locals), nullptr);
         vm->_run_top_frame(true);
         vm->_run_top_frame(true);
         frame->_locals = std::move(vm->top_frame()->_locals);
         frame->_locals = std::move(vm->top_frame()->_locals);

+ 5 - 7
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)){
     } else if(is_type(callable, tp_function)){
         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();
-        // 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;
         int i = 0;
         for(int index: fn.decl->args){
         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){
         for(int i=0; i<kwargs.size(); i+=2){
             StrName key = CAST(int, kwargs[i]);
             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, "()"));
                 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;
         PyObject* _module = fn._module != nullptr ? fn._module : top_frame()->_module;
         if(co->is_generator){
         if(co->is_generator){