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]; 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]; StrName name = co_names[byte.arg];
auto it = co->labels.find(label); int index = co->labels->try_get(name);
if(it == co->labels.end()) _error("KeyError", fmt("label ", label.escape(), " not found")); if(index < 0) _error("KeyError", fmt("label ", name.escape(), " not found"));
frame->jump_abs_break(it->second); frame->jump_abs_break(index);
} DISPATCH(); } DISPATCH();
/*****************************************/ /*****************************************/
TARGET(CALL) 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{ 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

View File

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

View File

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

View File

@ -91,10 +91,8 @@ struct CodeEmitContext{
co->codes[index].arg = target; co->codes[index].arg = target;
} }
bool add_label(StrName label){ bool add_label(StrName name){
if(co->labels.count(label)) return false; return co->labels->try_set(name, co->codes.size());
co->labels[label] = co->codes.size();
return true;
} }
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); int index = co->varnames_inv->try_get(name);
if(it != co->varnames_inv.end()) return it->second; if(index >= 0) return index;
co->varnames.push_back(name); co->varnames.push_back(name);
int index = co->varnames.size() - 1; index = co->varnames.size() - 1;
co->varnames_inv[name] = index; co->varnames_inv->set(name, index);
return index; return index;
} }

View File

@ -7,49 +7,123 @@
namespace pkpy{ namespace pkpy{
using ValueStack = pod_vector<PyObject*>; 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 { struct Frame {
ValueStack _data; ValueStack _data;
int _ip = -1; int _ip = -1;
int _next_ip = 0; int _next_ip = 0;
const CodeObject* co; const CodeObject* co;
PyObject* _module; PyObject* _module;
FastLocals _locals;
NameDict_ _closure;
PyObject* f_locals_try_get(StrName name){ FastLocals _locals;
auto it = co->varnames_inv.find(name); FastLocals _closure;
if(it == co->varnames_inv.end()) return nullptr;
return _locals[it->second];
}
NameDict& f_globals() noexcept { return _module->attr(); } 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{ Frame(const CodeObject* co, PyObject* _module, FastLocals&& _locals, const FastLocals& _closure)
NameDict_ dict = make_sp<NameDict>(); : co(co), _module(_module), _locals(std::move(_locals)), _closure(_closure) { }
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) 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` _locals._gc_mark();
if(_locals._data != nullptr){ _closure._gc_mark();
for(PyObject* obj: _locals) OBJ_MARK(obj);
}
if(_closure != nullptr) _closure->_gc_mark();
co->_gc_mark(); co->_gc_mark();
} }
}; };

View File

@ -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){ template<> inline void gc_mark<NameDict>(NameDict& t){
t.decl->_gc_mark(); if(t.size() == 0) return;
if(t._module != nullptr) OBJ_MARK(t._module); for(uint16_t i=0; i<t._capacity; i++){
if(t._closure != nullptr) t._closure->_gc_mark(); 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

View File

@ -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();
} }

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}; 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 { template<typename T>
using Item = std::pair<StrName, PyObject*>; struct NameDictImpl {
static constexpr uint16_t __Capacity = 128/sizeof(Item); using Item = std::pair<StrName, T>;
static_assert( (__Capacity & (__Capacity-1)) == 0, "__Capacity must be power of 2" ); 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) { NameDictImpl(const NameDictImpl& other) {
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];
} }
} }
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; NameDictImpl(NameDictImpl&&) = delete;
NameDict& operator=(NameDict&&) = 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

View File

@ -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(); if(_attr != nullptr) pkpy::gc_mark<NameDict>(*_attr);
pkpy::_gc_mark<T>(_value); // handle PyObject* inside _value `T` 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) {

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 // 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);

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)){ } 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);
FastLocals locals(co->varnames.size());
// zero init
for(auto& v: locals) v = nullptr;
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); // try_set has nullptr check
if(it == co->varnames_inv.end()){ // 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){