mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-23 21:10:19 +00:00
update dev
This commit is contained in:
parent
c403252aec
commit
9c5b3167c8
BIN
.github/workflows/main.rar
vendored
Normal file
BIN
.github/workflows/main.rar
vendored
Normal file
Binary file not shown.
118
.github/workflows/main.yml
vendored
118
.github/workflows/main.yml
vendored
@ -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
|
||||
16
src/ceval.h
16
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)
|
||||
|
||||
73
src/cffi.h
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{
|
||||
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
|
||||
@ -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);
|
||||
|
||||
@ -75,6 +75,7 @@ namespace std = ::std;
|
||||
struct Dummy { };
|
||||
struct DummyInstance { };
|
||||
struct DummyModule { };
|
||||
struct Discarded { };
|
||||
|
||||
struct Type {
|
||||
int index;
|
||||
|
||||
14
src/expr.h
14
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;
|
||||
}
|
||||
|
||||
|
||||
141
src/frame.h
141
src/frame.h
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
28
src/gc.h
28
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<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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
15
src/obj.h
15
src/obj.h
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
12
src/vm.h
12
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; 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){
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user