mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-24 13:30:18 +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];
|
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)
|
||||||
|
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{
|
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
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
14
src/expr.h
14
src/expr.h
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
141
src/frame.h
141
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 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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
28
src/gc.h
28
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){
|
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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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
|
15
src/obj.h
15
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();
|
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) {
|
||||||
|
@ -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);
|
||||||
|
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)){
|
} 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){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user