This commit is contained in:
blueloveTH 2023-04-17 16:43:30 +08:00
parent adc2f2cc3f
commit 51cf6d42e8
13 changed files with 525 additions and 518 deletions

View File

@ -1,118 +1,118 @@
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
# 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

@ -1,9 +1,5 @@
import sys
sys.setrecursionlimit(5000)
def f(n):
if n == 4000:
if n == 900:
return -1
return f(n + 1)

View File

@ -25,18 +25,6 @@ inline PyObject* VM::_run_top_frame(){
* `Args` containing strong references is safe if it is passed to `call` or `fast_call`
*/
{
/* Stack manipulation macros */
// https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123
#define TOP() (s_data.top())
#define SECOND() (s_data.second())
#define PEEK(n) (s_data.peek(n))
#define STACK_SHRINK(n) (s_data.shrink(n))
#define PUSH(v) (s_data.push(v))
#define POP() (s_data.pop())
#define POPX() (s_data.popx())
#define STACK_VIEW(n) (s_data.view(n))
#define DISPATCH_OP_CALL() { frame = top_frame(); goto __NEXT_FRAME; }
__NEXT_FRAME:
Bytecode byte = frame->next_bytecode();
@ -157,10 +145,7 @@ __NEXT_STEP:;
PUSH(self);
} DISPATCH();
TARGET(LOAD_SUBSCR) {
Args args(2);
args[1] = POPX(); // b
args[0] = TOP(); // a
TOP() = fast_call(__getitem__, std::move(args));
TOP() = fast_call_method(SECOND(), __getitem__, 2);
} DISPATCH();
TARGET(STORE_FAST)
frame->_locals[byte.arg] = POPX();
@ -186,13 +171,12 @@ __NEXT_STEP:;
setattr(a, name, val);
STACK_SHRINK(2);
} DISPATCH();
TARGET(STORE_SUBSCR) {
Args args(3);
args[1] = POPX(); // b
args[0] = POPX(); // a
args[2] = POPX(); // val
fast_call(__setitem__, std::move(args));
} DISPATCH();
TARGET(STORE_SUBSCR)
// val a b -> a b val
std::swap(SECOND(), THIRD());
std::swap(TOP(), SECOND());
fast_call_method(THIRD(), __setitem__, 3);
DISPATCH();
TARGET(DELETE_FAST) {
PyObject* val = frame->_locals[byte.arg];
if(val == nullptr) vm->NameError(co->varnames[byte.arg]);
@ -223,11 +207,9 @@ __NEXT_STEP:;
if(!a->attr().contains(name)) AttributeError(a, name);
a->attr().erase(name);
} DISPATCH();
TARGET(DELETE_SUBSCR) {
PyObject* b = POPX();
PyObject* a = POPX();
fast_call(__delitem__, Args{a, b});
} DISPATCH();
TARGET(DELETE_SUBSCR)
fast_call_method(SECOND(), __delitem__, 2);
DISPATCH();
/*****************************************/
TARGET(BUILD_LIST) {
PyObject* obj = VAR(STACK_VIEW(byte.arg).to_list());
@ -236,13 +218,13 @@ __NEXT_STEP:;
} DISPATCH();
TARGET(BUILD_DICT) {
PyObject* t = VAR(STACK_VIEW(byte.arg).to_tuple());
PyObject* obj = call(builtins->attr(m_dict), Args{t});
PyObject* obj = call_(builtins->attr(m_dict), t);
STACK_SHRINK(byte.arg);
PUSH(obj);
} DISPATCH();
TARGET(BUILD_SET) {
PyObject* t = VAR(STACK_VIEW(byte.arg).to_tuple());
PyObject* obj = call(builtins->attr(m_set), Args{t});
PyObject* obj = call_(builtins->attr(m_set), t);
STACK_SHRINK(byte.arg);
PUSH(obj);
} DISPATCH();
@ -269,24 +251,18 @@ __NEXT_STEP:;
PUSH(VAR(ss.str()));
} DISPATCH();
/*****************************************/
TARGET(BINARY_OP) {
Args args(2);
args[1] = POPX(); // lhs
args[0] = TOP(); // rhs
TOP() = fast_call(BINARY_SPECIAL_METHODS[byte.arg], std::move(args));
} DISPATCH();
TARGET(BINARY_OP)
TOP() = fast_call_method(SECOND(), BINARY_SPECIAL_METHODS[byte.arg], 2);
DISPATCH();
#define INT_BINARY_OP(op, func) \
if(is_both_int(TOP(), SECOND())){ \
i64 b = _CAST(i64, TOP()); \
i64 a = _CAST(i64, SECOND()); \
POP(); \
TOP() = VAR(a op b); \
}else{ \
Args args(2); \
args[1] = POPX(); \
args[0] = TOP(); \
TOP() = fast_call(func, std::move(args)); \
#define INT_BINARY_OP(op, func) \
if(is_both_int(TOP(), SECOND())){ \
i64 b = _CAST(i64, TOP()); \
i64 a = _CAST(i64, SECOND()); \
POP(); \
TOP() = VAR(a op b); \
}else{ \
TOP() = fast_call_method(SECOND(), func, 2); \
}
TARGET(BINARY_ADD)
@ -346,10 +322,9 @@ __NEXT_STEP:;
TOP() = VAR(ret_c);
} DISPATCH();
TARGET(CONTAINS_OP) {
Args args(2);
args[0] = POPX();
args[1] = TOP();
PyObject* ret = fast_call(__contains__, std::move(args));
// a in b -> b __contains__ a
std::swap(TOP(), SECOND());
PyObject* ret = fast_call_method(SECOND(), __contains__, 2);
bool ret_c = CAST(bool, ret);
if(byte.arg == 1) ret_c = !ret_c;
TOP() = VAR(ret_c);
@ -382,66 +357,28 @@ __NEXT_STEP:;
frame->jump_abs_break(index);
} DISPATCH();
/*****************************************/
TARGET(CALL)
TARGET(CALL_UNPACK) {
int ARGC = byte.arg;
PyObject* callable = PEEK(ARGC+2);
bool method_call = PEEK(ARGC+1) != _py_null;
// fast path
if(byte.op==OP_CALL && is_type(callable, tp_function)){
PyObject* ret = _py_call(callable, STACK_VIEW(ARGC + int(method_call)), {});
STACK_SHRINK(ARGC + 2);
// TODO: _sp_base is incorrect
top_frame()->_sp_base = s_data._sp;
if(ret == nullptr) { DISPATCH_OP_CALL(); }
else PUSH(ret); // a generator
DISPATCH();
}
Args args = STACK_VIEW(ARGC + int(method_call)).to_tuple();
if(byte.op == OP_CALL_UNPACK) unpack_args(args);
PyObject* ret = call(callable, std::move(args), no_arg(), true);
STACK_SHRINK(ARGC + 2);
if(ret == _py_op_call) { DISPATCH_OP_CALL(); }
PUSH(ret);
} DISPATCH();
TARGET(CALL_KWARGS)
TARGET(CALL_KWARGS_UNPACK) {
// TODO: poor performance, refactor needed
TARGET(CALL) {
int ARGC = byte.arg & 0xFFFF;
int KWARGC = (byte.arg >> 16) & 0xFFFF;
Args kwargs = STACK_VIEW(KWARGC*2).to_tuple();
STACK_SHRINK(KWARGC*2);
bool method_call = PEEK(ARGC+1) != _py_null;
if(method_call) ARGC++; // add self into args
Args args = STACK_VIEW(ARGC).to_tuple();
STACK_SHRINK(ARGC);
if(!method_call) POP();
if(byte.op == OP_CALL_KWARGS_UNPACK) unpack_args(args);
PyObject* callable = POPX();
PyObject* ret = call(callable, std::move(args), kwargs, true);
PyObject* ret = _vectorcall(ARGC, KWARGC, true);
if(ret == _py_op_call) { DISPATCH_OP_CALL(); }
PUSH(ret);
} DISPATCH();
DISPATCH();
}
TARGET(RETURN_VALUE) {
#if DEBUG_EXTRA_CHECK
if(frame->stack_size() != 1) FATAL_ERROR();
#endif
PyObject* __ret = POPX();
// cleanup the stack on return
callstack.pop();
s_data.reset(frame->_sp_base);
if(frame.index == base_id){ // [ frameBase<- ]
callstack.pop();
return POPX();
return __ret;
}else{
callstack.pop();
frame = top_frame();
PUSH(__ret);
goto __NEXT_FRAME;
}
}
TARGET(YIELD_VALUE)
#if DEBUG_EXTRA_CHECK
if(frame->stack_size() != 1) FATAL_ERROR();
#endif
return _py_op_yield;
/*****************************************/
TARGET(LIST_APPEND) {
@ -451,12 +388,16 @@ __NEXT_STEP:;
} DISPATCH();
TARGET(DICT_ADD) {
PyObject* kv = POPX();
Tuple& t = CAST(Tuple& ,kv);
fast_call(__setitem__, Args{SECOND(), t[0], t[1]});
Tuple& t = CAST(Tuple&, kv);
PyObject* self;
PyObject* callable = get_unbound_method(SECOND(), __setitem__, &self);
call_method(self, callable, t[0], t[1]);
} DISPATCH();
TARGET(SET_ADD) {
PyObject* obj = POPX();
fast_call(m_add, Args{SECOND(), obj});
PyObject* val = POPX();
PyObject* self;
PyObject* callable = get_unbound_method(SECOND(), m_add, &self);
call_method(self, callable, val);
} DISPATCH();
/*****************************************/
TARGET(UNARY_NEGATIVE)

View File

@ -12,7 +12,7 @@ struct NativeProxyFunc {
_Fp func;
NativeProxyFunc(_Fp func) : func(func) {}
PyObject* operator()(VM* vm, Args& args) {
PyObject* operator()(VM* vm, ArgsView args) {
if (args.size() != N) {
vm->TypeError("expected " + std::to_string(N) + " arguments, but got " + std::to_string(args.size()));
}
@ -20,13 +20,13 @@ struct NativeProxyFunc {
}
template<typename __Ret, size_t... Is>
std::enable_if_t<std::is_void_v<__Ret>, PyObject*> call(VM* vm, Args& args, std::index_sequence<Is...>) {
std::enable_if_t<std::is_void_v<__Ret>, PyObject*> call(VM* vm, ArgsView args, std::index_sequence<Is...>) {
func(py_cast<Params>(vm, args[Is])...);
return vm->None;
}
template<typename __Ret, size_t... Is>
std::enable_if_t<!std::is_void_v<__Ret>, PyObject*> call(VM* vm, Args& args, std::index_sequence<Is...>) {
std::enable_if_t<!std::is_void_v<__Ret>, PyObject*> call(VM* vm, ArgsView args, std::index_sequence<Is...>) {
__Ret ret = func(py_cast<Params>(vm, args[Is])...);
return VAR(std::move(ret));
}
@ -39,7 +39,7 @@ struct NativeProxyMethod {
_Fp func;
NativeProxyMethod(_Fp func) : func(func) {}
PyObject* operator()(VM* vm, Args& args) {
PyObject* operator()(VM* vm, ArgsView args) {
int actual_size = args.size() - 1;
if (actual_size != N) {
vm->TypeError("expected " + std::to_string(N) + " arguments, but got " + std::to_string(actual_size));
@ -48,14 +48,14 @@ struct NativeProxyMethod {
}
template<typename __Ret, size_t... Is>
std::enable_if_t<std::is_void_v<__Ret>, PyObject*> call(VM* vm, Args& args, std::index_sequence<Is...>) {
std::enable_if_t<std::is_void_v<__Ret>, PyObject*> call(VM* vm, ArgsView args, std::index_sequence<Is...>) {
T& self = py_cast<T&>(vm, args[0]);
(self.*func)(py_cast<Params>(vm, args[Is+1])...);
return vm->None;
}
template<typename __Ret, size_t... Is>
std::enable_if_t<!std::is_void_v<__Ret>, PyObject*> call(VM* vm, Args& args, std::index_sequence<Is...>) {
std::enable_if_t<!std::is_void_v<__Ret>, PyObject*> call(VM* vm, ArgsView args, std::index_sequence<Is...>) {
T& self = py_cast<T&>(vm, args[0]);
__Ret ret = (self.*func)(py_cast<Params>(vm, args[Is+1])...);
return VAR(std::move(ret));
@ -201,7 +201,7 @@ struct Pointer{
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_static_method<-1>(type, "__new__", CPP_NOT_IMPLEMENTED());
vm->bind_method<0>(type, "__repr__", [](VM* vm, Args& args) {
vm->bind_method<0>(type, "__repr__", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
std::stringstream ss;
ss << "<" << self.ctype->name;
@ -210,53 +210,53 @@ struct Pointer{
return VAR(ss.str());
});
vm->bind_method<1>(type, "__add__", [](VM* vm, Args& args) {
vm->bind_method<1>(type, "__add__", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
return VAR_T(Pointer, self + CAST(i64, args[1]));
});
vm->bind_method<1>(type, "__sub__", [](VM* vm, Args& args) {
vm->bind_method<1>(type, "__sub__", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
return VAR_T(Pointer, self - CAST(i64, args[1]));
});
vm->bind_method<1>(type, "__eq__", [](VM* vm, Args& args) {
vm->bind_method<1>(type, "__eq__", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
Pointer& other = CAST(Pointer&, args[1]);
return VAR(self.ptr == other.ptr);
});
vm->bind_method<1>(type, "__ne__", [](VM* vm, Args& args) {
vm->bind_method<1>(type, "__ne__", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
Pointer& other = CAST(Pointer&, args[1]);
return VAR(self.ptr != other.ptr);
});
vm->bind_method<1>(type, "__getitem__", [](VM* vm, Args& args) {
vm->bind_method<1>(type, "__getitem__", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
i64 index = CAST(i64, args[1]);
return (self+index).get(vm);
});
vm->bind_method<2>(type, "__setitem__", [](VM* vm, Args& args) {
vm->bind_method<2>(type, "__setitem__", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
i64 index = CAST(i64, args[1]);
(self+index).set(vm, args[2]);
return vm->None;
});
vm->bind_method<1>(type, "__getattr__", [](VM* vm, Args& args) {
vm->bind_method<1>(type, "__getattr__", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
const Str& name = CAST(Str&, args[1]);
return VAR_T(Pointer, self._to(vm, name));
});
vm->bind_method<0>(type, "get", [](VM* vm, Args& args) {
vm->bind_method<0>(type, "get", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
return self.get(vm);
});
vm->bind_method<1>(type, "set", [](VM* vm, Args& args) {
vm->bind_method<1>(type, "set", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
self.set(vm, args[1]);
return vm->None;
@ -336,7 +336,7 @@ struct CType{
CType(const TypeInfo* type) : type(type) {}
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_static_method<1>(type, "__new__", [](VM* vm, Args& args) {
vm->bind_static_method<1>(type, "__new__", [](VM* vm, ArgsView args) {
const Str& name = CAST(Str&, args[0]);
const TypeInfo* type = _type_db.get(name);
if(type == nullptr) vm->TypeError("unknown type: " + name.escape());
@ -352,18 +352,18 @@ inline void add_module_c(VM* vm){
vm->setattr(mod, "nullptr", VAR_T(Pointer));
vm->bind_func<1>(mod, "malloc", [](VM* vm, Args& args) {
vm->bind_func<1>(mod, "malloc", [](VM* vm, ArgsView args) {
i64 size = CAST(i64, args[0]);
return VAR_T(Pointer, _type_db.get<void>(), (char*)malloc(size));
});
vm->bind_func<1>(mod, "free", [](VM* vm, Args& args) {
vm->bind_func<1>(mod, "free", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
free(self.ptr);
return vm->None;
});
vm->bind_func<3>(mod, "memcpy", [](VM* vm, Args& args) {
vm->bind_func<3>(mod, "memcpy", [](VM* vm, ArgsView args) {
Pointer& dst = CAST(Pointer&, args[0]);
Pointer& src = CAST(Pointer&, args[1]);
i64 size = CAST(i64, args[2]);
@ -371,7 +371,7 @@ inline void add_module_c(VM* vm){
return vm->None;
});
vm->bind_func<2>(mod, "cast", [](VM* vm, Args& args) {
vm->bind_func<2>(mod, "cast", [](VM* vm, ArgsView args) {
Pointer& self = CAST(Pointer&, args[0]);
const Str& name = CAST(Str&, args[1]);
int level = 0;
@ -386,7 +386,7 @@ inline void add_module_c(VM* vm){
return VAR_T(Pointer, type, level, self.ptr);
});
vm->bind_func<1>(mod, "sizeof", [](VM* vm, Args& args) {
vm->bind_func<1>(mod, "sizeof", [](VM* vm, ArgsView args) {
const Str& name = CAST(Str&, args[0]);
if(name.index("*") != -1) return VAR(sizeof(void*));
const TypeInfo* type = _type_db.get(name);
@ -394,7 +394,7 @@ inline void add_module_c(VM* vm){
return VAR(type->size);
});
vm->bind_func<3>(mod, "memset", [](VM* vm, Args& args) {
vm->bind_func<3>(mod, "memset", [](VM* vm, ArgsView args) {
Pointer& dst = CAST(Pointer&, args[0]);
i64 val = CAST(i64, args[1]);
i64 size = CAST(i64, args[2]);

View File

@ -88,8 +88,8 @@ struct Type {
};
#define THREAD_LOCAL // thread_local
#define CPP_LAMBDA(x) ([](VM* vm, Args& args) { return x; })
#define CPP_NOT_IMPLEMENTED() ([](VM* vm, Args& args) { vm->NotImplementedError(); return vm->None; })
#define CPP_LAMBDA(x) ([](VM* vm, ArgsView args) { return x; })
#define CPP_NOT_IMPLEMENTED() ([](VM* vm, ArgsView args) { vm->NotImplementedError(); return vm->None; })
#ifdef POCKETPY_H
#define FATAL_ERROR() throw std::runtime_error( "L" + std::to_string(__LINE__) + " FATAL_ERROR()!");

View File

@ -48,14 +48,18 @@ struct FastLocals{
a[index] = nullptr;
}
bool try_set(StrName name, PyObject* value){
if(!is_valid()) return false;
bool _try_set(StrName name, PyObject* value){
int index = varnames_inv->try_get(name);
if(index == -1) return false;
a[index] = value;
return true;
}
bool try_set(StrName name, PyObject* value){
if(!is_valid()) return false;
return _try_set(name, value);
}
FastLocals(const FastLocals& other){
varnames_inv = other.varnames_inv;
a = other.a;
@ -126,8 +130,9 @@ template<> inline void gc_mark<Function>(Function& t){
}
struct ValueStack {
static const int MAX_SIZE = 32768;
PyObject* _begin[MAX_SIZE];
static const size_t MAX_SIZE = 32768;
// We allocate 512 more bytes to keep `_sp` valid when `is_overflow() == true`.
PyObject* _begin[MAX_SIZE + 512];
PyObject** _sp;
ValueStack(): _sp(_begin) {}
@ -136,6 +141,8 @@ struct ValueStack {
PyObject* top() const { return _sp[-1]; }
PyObject*& second(){ return _sp[-2]; }
PyObject* second() const { return _sp[-2]; }
PyObject*& third(){ return _sp[-3]; }
PyObject* third() const { return _sp[-3]; }
PyObject*& peek(int n){ return _sp[-n]; }
PyObject* peek(int n) const { return _sp[-n]; }
void push(PyObject* v){ *_sp++ = v; }
@ -143,13 +150,22 @@ struct ValueStack {
PyObject* popx(){ return *--_sp; }
ArgsView view(int n){ return ArgsView(_sp-n, _sp); }
void shrink(int n){ _sp -= n; }
// int size() const { return _sp - _begin; }
// bool empty() const { return _sp == _begin; }
int size() const { return _sp - _begin; }
bool empty() const { return _sp == _begin; }
PyObject** begin() { return _begin; }
PyObject** end() { return _sp; }
void reset(PyObject** sp) { _sp = sp; }
// void resize(int n) { _sp = _begin + n; }
void clear() { _sp = _begin; }
bool is_overflow() const { return _sp >= _begin + MAX_SIZE; }
void dup_top_n(int n) {
// [a, b, c]
// ^p0 ^p1
PyObject** p0 = _sp - n;
PyObject** p1 = _sp;
memcpy(p1, p0, n * sizeof(void*));
_sp += n;
}
ValueStack(const ValueStack&) = delete;
ValueStack(ValueStack&&) = delete;
@ -170,14 +186,14 @@ struct Frame {
NameDict& f_globals() noexcept { return _module->attr(); }
Frame(ValueStack* _s, const CodeObject* co, PyObject* _module, FastLocals&& _locals, const FastLocals& _closure)
: _s(_s), _sp_base(_s->_sp), co(co), _module(_module), _locals(std::move(_locals)), _closure(_closure) { }
Frame(ValueStack* _s, PyObject** _sp_base, const CodeObject* co, PyObject* _module, FastLocals&& _locals, const FastLocals& _closure)
: _s(_s), _sp_base(_sp_base), co(co), _module(_module), _locals(std::move(_locals)), _closure(_closure) { }
Frame(ValueStack* _s, const CodeObject* co, PyObject* _module, const FastLocals& _locals, const FastLocals& _closure)
: _s(_s), _sp_base(_s->_sp), co(co), _module(_module), _locals(_locals), _closure(_closure) { }
Frame(ValueStack* _s, PyObject** _sp_base, const CodeObject* co, PyObject* _module, const FastLocals& _locals, const FastLocals& _closure)
: _s(_s), _sp_base(_sp_base), co(co), _module(_module), _locals(_locals), _closure(_closure) { }
Frame(ValueStack* _s, const CodeObject_& co, PyObject* _module)
: _s(_s), _sp_base(_s->_sp), co(co.get()), _module(_module), _locals(), _closure() { }
Frame(ValueStack* _s, PyObject** _sp_base, const CodeObject_& co, PyObject* _module)
: _s(_s), _sp_base(_sp_base), co(co.get()), _module(_module), _locals(), _closure() { }
Frame(const Frame& other) = delete;
Frame& operator=(const Frame& other) = delete;

View File

@ -43,32 +43,32 @@ struct FileIO {
}
static void _register(VM* vm, PyObject* mod, PyObject* type){
vm->bind_static_method<2>(type, "__new__", [](VM* vm, Args& args){
vm->bind_static_method<2>(type, "__new__", [](VM* vm, ArgsView args){
return VAR_T(FileIO,
vm, CAST(Str, args[0]), CAST(Str, args[1])
);
});
vm->bind_method<0>(type, "read", [](VM* vm, Args& args){
vm->bind_method<0>(type, "read", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
std::string buffer;
io._fs >> buffer;
return VAR(buffer);
});
vm->bind_method<1>(type, "write", [](VM* vm, Args& args){
vm->bind_method<1>(type, "write", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
io._fs << CAST(Str&, args[1]);
return vm->None;
});
vm->bind_method<0>(type, "close", [](VM* vm, Args& args){
vm->bind_method<0>(type, "close", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
io._fs.close();
return vm->None;
});
vm->bind_method<0>(type, "__exit__", [](VM* vm, Args& args){
vm->bind_method<0>(type, "__exit__", [](VM* vm, ArgsView args){
FileIO& io = CAST(FileIO&, args[0]);
io._fs.close();
return vm->None;
@ -81,7 +81,7 @@ struct FileIO {
inline void add_module_io(VM* vm){
PyObject* mod = vm->new_module("io");
PyObject* type = FileIO::register_class(vm, mod);
vm->bind_builtin_func<2>("open", [type](VM* vm, const Args& args){
vm->bind_builtin_func<2>("open", [type](VM* vm, ArgsView args){
return vm->call(type, args);
});
}
@ -89,17 +89,17 @@ inline void add_module_io(VM* vm){
inline void add_module_os(VM* vm){
PyObject* mod = vm->new_module("os");
// Working directory is shared by all VMs!!
vm->bind_func<0>(mod, "getcwd", [](VM* vm, const Args& args){
vm->bind_func<0>(mod, "getcwd", [](VM* vm, ArgsView args){
return VAR(std::filesystem::current_path().string());
});
vm->bind_func<1>(mod, "chdir", [](VM* vm, const Args& args){
vm->bind_func<1>(mod, "chdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).c_str());
std::filesystem::current_path(path);
return vm->None;
});
vm->bind_func<1>(mod, "listdir", [](VM* vm, const Args& args){
vm->bind_func<1>(mod, "listdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).c_str());
std::filesystem::directory_iterator di;
try{
@ -115,28 +115,28 @@ inline void add_module_os(VM* vm){
return VAR(ret);
});
vm->bind_func<1>(mod, "remove", [](VM* vm, const Args& args){
vm->bind_func<1>(mod, "remove", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).c_str());
bool ok = std::filesystem::remove(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func<1>(mod, "mkdir", [](VM* vm, const Args& args){
vm->bind_func<1>(mod, "mkdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).c_str());
bool ok = std::filesystem::create_directory(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func<1>(mod, "rmdir", [](VM* vm, const Args& args){
vm->bind_func<1>(mod, "rmdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).c_str());
bool ok = std::filesystem::remove(path);
if(!ok) vm->IOError("operation failed");
return vm->None;
});
vm->bind_func<-1>(mod, "path_join", [](VM* vm, const Args& args){
vm->bind_func<-1>(mod, "path_join", [](VM* vm, ArgsView args){
std::filesystem::path path;
for(int i=0; i<args.size(); i++){
path /= CAST(Str&, args[i]).c_str();
@ -144,7 +144,7 @@ inline void add_module_os(VM* vm){
return VAR(path.string());
});
vm->bind_func<1>(mod, "path_exists", [](VM* vm, const Args& args){
vm->bind_func<1>(mod, "path_exists", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).c_str());
bool exists = std::filesystem::exists(path);
return VAR(exists);

View File

@ -7,7 +7,7 @@
int main(int argc, char** argv){
pkpy::VM* vm = pkpy_new_vm(true);
vm->bind_builtin_func<0>("input", [](pkpy::VM* vm, pkpy::Args& args){
vm->bind_builtin_func<0>("input", [](pkpy::VM* vm, pkpy::ArgsView args){
return VAR(pkpy::getline());
});
if(argc == 1){

View File

@ -11,7 +11,7 @@ struct Frame;
struct Function;
class VM;
typedef std::function<PyObject*(VM*, Args&)> NativeFuncRaw;
typedef std::function<PyObject*(VM*, ArgsView)> NativeFuncRaw;
typedef shared_ptr<CodeObject> CodeObject_;
struct NativeFunc {
@ -20,7 +20,7 @@ struct NativeFunc {
bool method;
NativeFunc(NativeFuncRaw f, int argc, bool method) : f(f), argc(argc), method(method) {}
PyObject* operator()(VM* vm, Args& args) const;
PyObject* operator()(VM* vm, ArgsView args) const;
};
struct FuncDecl {
@ -167,6 +167,13 @@ inline bool is_type(PyObject* obj, Type type) {
}
}
inline bool is_non_tagged_type(PyObject* obj, Type type) {
#if DEBUG_EXTRA_CHECK
if(obj == nullptr) throw std::runtime_error("is_non_tagged_type() called with nullptr");
#endif
return !is_tagged(obj) && obj->type == type;
}
#define PY_CLASS(T, mod, name) \
static Type _type(VM* vm) { \
static const StrName __x0(#mod); \

View File

@ -77,9 +77,6 @@ OPCODE(LOOP_BREAK)
OPCODE(GOTO)
/**************************/
OPCODE(CALL)
OPCODE(CALL_UNPACK)
OPCODE(CALL_KWARGS)
OPCODE(CALL_KWARGS_UNPACK)
OPCODE(RETURN_VALUE)
OPCODE(YIELD_VALUE)
/**************************/

View File

@ -25,7 +25,7 @@ inline CodeObject_ VM::compile(Str source, Str filename, CompileMode mode, bool
}
#define BIND_NUM_ARITH_OPT(name, op) \
_vm->_bind_methods<1>({"int","float"}, #name, [](VM* vm, Args& args){ \
_vm->_bind_methods<1>({"int","float"}, #name, [](VM* vm, ArgsView args){ \
if(is_both_int(args[0], args[1])){ \
return VAR(_CAST(i64, args[0]) op _CAST(i64, args[1])); \
}else{ \
@ -34,7 +34,7 @@ inline CodeObject_ VM::compile(Str source, Str filename, CompileMode mode, bool
});
#define BIND_NUM_LOGICAL_OPT(name, op, is_eq) \
_vm->_bind_methods<1>({"int","float"}, #name, [](VM* vm, Args& args){ \
_vm->_bind_methods<1>({"int","float"}, #name, [](VM* vm, ArgsView args){ \
if(is_both_int(args[0], args[1])) \
return VAR(_CAST(i64, args[0]) op _CAST(i64, args[1])); \
if(!is_both_int_or_float(args[0], args[1])){ \
@ -60,12 +60,12 @@ inline void init_builtins(VM* _vm) {
#undef BIND_NUM_ARITH_OPT
#undef BIND_NUM_LOGICAL_OPT
_vm->bind_builtin_func<1>("__sys_stdout_write", [](VM* vm, Args& args) {
_vm->bind_builtin_func<1>("__sys_stdout_write", [](VM* vm, ArgsView args) {
(*vm->_stdout) << CAST(Str&, args[0]);
return vm->None;
});
_vm->bind_builtin_func<2>("super", [](VM* vm, Args& args) {
_vm->bind_builtin_func<2>("super", [](VM* vm, ArgsView args) {
vm->check_type(args[0], vm->tp_type);
Type type = OBJ_GET(Type, args[0]);
if(!vm->isinstance(args[1], type)){
@ -77,39 +77,39 @@ inline void init_builtins(VM* _vm) {
return vm->heap.gcnew(vm->tp_super, Super(args[1], base));
});
_vm->bind_builtin_func<2>("isinstance", [](VM* vm, Args& args) {
_vm->bind_builtin_func<2>("isinstance", [](VM* vm, ArgsView args) {
vm->check_type(args[1], vm->tp_type);
Type type = OBJ_GET(Type, args[1]);
return VAR(vm->isinstance(args[0], type));
});
_vm->bind_builtin_func<1>("id", [](VM* vm, Args& args) {
_vm->bind_builtin_func<1>("id", [](VM* vm, ArgsView args) {
PyObject* obj = args[0];
if(is_tagged(obj)) return VAR((i64)0);
return VAR(BITS(obj));
});
_vm->bind_builtin_func<2>("divmod", [](VM* vm, Args& args) {
_vm->bind_builtin_func<2>("divmod", [](VM* vm, ArgsView args) {
i64 lhs = CAST(i64, args[0]);
i64 rhs = CAST(i64, args[1]);
if(rhs == 0) vm->ZeroDivisionError();
return VAR(Tuple({VAR(lhs/rhs), VAR(lhs%rhs)}));
});
_vm->bind_builtin_func<1>("eval", [](VM* vm, Args& args) {
_vm->bind_builtin_func<1>("eval", [](VM* vm, ArgsView args) {
CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<eval>", EVAL_MODE, true);
FrameId frame = vm->top_frame();
return vm->_exec(code.get(), frame->_module, frame->_locals, nullptr);
});
_vm->bind_builtin_func<1>("exec", [](VM* vm, Args& args) {
_vm->bind_builtin_func<1>("exec", [](VM* vm, ArgsView args) {
CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<exec>", EXEC_MODE, true);
FrameId frame = vm->top_frame();
vm->_exec(code.get(), frame->_module, frame->_locals, nullptr);
return vm->None;
});
_vm->bind_builtin_func<-1>("exit", [](VM* vm, Args& args) {
_vm->bind_builtin_func<-1>("exit", [](VM* vm, ArgsView args) {
if(args.size() == 0) std::exit(0);
else if(args.size() == 1) std::exit(CAST(int, args[0]));
else vm->TypeError("exit() takes at most 1 argument");
@ -117,51 +117,55 @@ inline void init_builtins(VM* _vm) {
});
_vm->bind_builtin_func<1>("repr", CPP_LAMBDA(vm->asRepr(args[0])));
_vm->bind_builtin_func<1>("len", CPP_LAMBDA(vm->fast_call(__len__, Args{args[0]})));
_vm->bind_builtin_func<1>("len", [](VM* vm, ArgsView args){
PyObject* self;
PyObject* len_f = vm->get_unbound_method(args[0], __len__, &self);
return vm->call_method(self, len_f);
});
_vm->bind_builtin_func<1>("hash", [](VM* vm, Args& args){
_vm->bind_builtin_func<1>("hash", [](VM* vm, ArgsView args){
i64 value = vm->hash(args[0]);
if(((value << 2) >> 2) != value) value >>= 2;
return VAR(value);
});
_vm->bind_builtin_func<1>("chr", [](VM* vm, Args& args) {
_vm->bind_builtin_func<1>("chr", [](VM* vm, ArgsView args) {
i64 i = CAST(i64, args[0]);
if (i < 0 || i > 128) vm->ValueError("chr() arg not in range(128)");
return VAR(std::string(1, (char)i));
});
_vm->bind_builtin_func<1>("ord", [](VM* vm, Args& args) {
_vm->bind_builtin_func<1>("ord", [](VM* vm, ArgsView args) {
const Str& s = CAST(Str&, args[0]);
if (s.length()!=1) vm->TypeError("ord() expected an ASCII character");
return VAR((i64)(s[0]));
});
_vm->bind_builtin_func<2>("hasattr", [](VM* vm, Args& args) {
_vm->bind_builtin_func<2>("hasattr", [](VM* vm, ArgsView args) {
return VAR(vm->getattr(args[0], CAST(Str&, args[1]), false) != nullptr);
});
_vm->bind_builtin_func<3>("setattr", [](VM* vm, Args& args) {
_vm->bind_builtin_func<3>("setattr", [](VM* vm, ArgsView args) {
vm->setattr(args[0], CAST(Str&, args[1]), args[2]);
return vm->None;
});
_vm->bind_builtin_func<2>("getattr", [](VM* vm, Args& args) {
_vm->bind_builtin_func<2>("getattr", [](VM* vm, ArgsView args) {
const Str& name = CAST(Str&, args[1]);
return vm->getattr(args[0], name);
});
_vm->bind_builtin_func<1>("hex", [](VM* vm, Args& args) {
_vm->bind_builtin_func<1>("hex", [](VM* vm, ArgsView args) {
std::stringstream ss;
ss << std::hex << CAST(i64, args[0]);
return VAR("0x" + ss.str());
});
_vm->bind_builtin_func<1>("iter", [](VM* vm, Args& args) {
_vm->bind_builtin_func<1>("iter", [](VM* vm, ArgsView args) {
return vm->asIter(args[0]);
});
_vm->bind_builtin_func<1>("dir", [](VM* vm, Args& args) {
_vm->bind_builtin_func<1>("dir", [](VM* vm, ArgsView args) {
std::set<StrName> names;
if(args[0]->is_attr_valid()){
std::vector<StrName> keys = args[0]->attr().keys();
@ -175,7 +179,7 @@ inline void init_builtins(VM* _vm) {
return VAR(std::move(ret));
});
_vm->bind_method<0>("object", "__repr__", [](VM* vm, Args& args) {
_vm->bind_method<0>("object", "__repr__", [](VM* vm, ArgsView args) {
PyObject* self = args[0];
if(is_tagged(self)) self = nullptr;
std::stringstream ss;
@ -187,7 +191,7 @@ inline void init_builtins(VM* _vm) {
_vm->bind_method<1>("object", "__ne__", CPP_LAMBDA(VAR(args[0] != args[1])));
_vm->bind_static_method<1>("type", "__new__", CPP_LAMBDA(vm->_t(args[0])));
_vm->bind_static_method<-1>("range", "__new__", [](VM* vm, Args& args) {
_vm->bind_static_method<-1>("range", "__new__", [](VM* vm, ArgsView args) {
Range r;
switch (args.size()) {
case 1: r.stop = CAST(i64, args[0]); break;
@ -205,13 +209,13 @@ inline void init_builtins(VM* _vm) {
_vm->bind_method<0>("NoneType", "__repr__", CPP_LAMBDA(VAR("None")));
_vm->bind_method<0>("NoneType", "__json__", CPP_LAMBDA(VAR("null")));
_vm->_bind_methods<1>({"int", "float"}, "__truediv__", [](VM* vm, Args& args) {
_vm->_bind_methods<1>({"int", "float"}, "__truediv__", [](VM* vm, ArgsView args) {
f64 rhs = vm->num_to_float(args[1]);
if (rhs == 0) vm->ZeroDivisionError();
return VAR(vm->num_to_float(args[0]) / rhs);
});
_vm->_bind_methods<1>({"int", "float"}, "__pow__", [](VM* vm, Args& args) {
_vm->_bind_methods<1>({"int", "float"}, "__pow__", [](VM* vm, ArgsView args) {
if(is_both_int(args[0], args[1])){
i64 lhs = _CAST(i64, args[0]);
i64 rhs = _CAST(i64, args[1]);
@ -231,7 +235,7 @@ inline void init_builtins(VM* _vm) {
});
/************ PyInt ************/
_vm->bind_static_method<1>("int", "__new__", [](VM* vm, Args& args) {
_vm->bind_static_method<1>("int", "__new__", [](VM* vm, ArgsView args) {
if (is_type(args[0], vm->tp_int)) return args[0];
if (is_type(args[0], vm->tp_float)) return VAR((i64)CAST(f64, args[0]));
if (is_type(args[0], vm->tp_bool)) return VAR(_CAST(bool, args[0]) ? 1 : 0);
@ -250,13 +254,13 @@ inline void init_builtins(VM* _vm) {
return vm->None;
});
_vm->bind_method<1>("int", "__floordiv__", [](VM* vm, Args& args) {
_vm->bind_method<1>("int", "__floordiv__", [](VM* vm, ArgsView args) {
i64 rhs = CAST(i64, args[1]);
if(rhs == 0) vm->ZeroDivisionError();
return VAR(CAST(i64, args[0]) / rhs);
});
_vm->bind_method<1>("int", "__mod__", [](VM* vm, Args& args) {
_vm->bind_method<1>("int", "__mod__", [](VM* vm, ArgsView args) {
i64 rhs = CAST(i64, args[1]);
if(rhs == 0) vm->ZeroDivisionError();
return VAR(CAST(i64, args[0]) % rhs);
@ -277,7 +281,7 @@ inline void init_builtins(VM* _vm) {
#undef INT_BITWISE_OP
/************ PyFloat ************/
_vm->bind_static_method<1>("float", "__new__", [](VM* vm, Args& args) {
_vm->bind_static_method<1>("float", "__new__", [](VM* vm, ArgsView args) {
if (is_type(args[0], vm->tp_int)) return VAR((f64)CAST(i64, args[0]));
if (is_type(args[0], vm->tp_float)) return args[0];
if (is_type(args[0], vm->tp_bool)) return VAR(_CAST(bool, args[0]) ? 1.0 : 0.0);
@ -296,7 +300,7 @@ inline void init_builtins(VM* _vm) {
return vm->None;
});
_vm->bind_method<0>("float", "__repr__", [](VM* vm, Args& args) {
_vm->bind_method<0>("float", "__repr__", [](VM* vm, ArgsView args) {
f64 val = CAST(f64, args[0]);
if(std::isinf(val) || std::isnan(val)) return VAR(std::to_string(val));
std::stringstream ss;
@ -306,7 +310,7 @@ inline void init_builtins(VM* _vm) {
return VAR(s);
});
_vm->bind_method<0>("float", "__json__", [](VM* vm, Args& args) {
_vm->bind_method<0>("float", "__json__", [](VM* vm, ArgsView args) {
f64 val = CAST(f64, args[0]);
if(std::isinf(val) || std::isnan(val)) vm->ValueError("cannot jsonify 'nan' or 'inf'");
return VAR(std::to_string(val));
@ -315,18 +319,18 @@ inline void init_builtins(VM* _vm) {
/************ PyString ************/
_vm->bind_static_method<1>("str", "__new__", CPP_LAMBDA(vm->asStr(args[0])));
_vm->bind_method<1>("str", "__add__", [](VM* vm, Args& args) {
_vm->bind_method<1>("str", "__add__", [](VM* vm, ArgsView args) {
const Str& lhs = CAST(Str&, args[0]);
const Str& rhs = CAST(Str&, args[1]);
return VAR(lhs + rhs);
});
_vm->bind_method<0>("str", "__len__", [](VM* vm, Args& args) {
_vm->bind_method<0>("str", "__len__", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]);
return VAR(self.u8_length());
});
_vm->bind_method<1>("str", "__contains__", [](VM* vm, Args& args) {
_vm->bind_method<1>("str", "__contains__", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]);
const Str& other = CAST(Str&, args[1]);
return VAR(self.index(other) != -1);
@ -335,29 +339,29 @@ inline void init_builtins(VM* _vm) {
_vm->bind_method<0>("str", "__str__", CPP_LAMBDA(args[0]));
_vm->bind_method<0>("str", "__iter__", CPP_LAMBDA(vm->PyIter(StringIter(vm, args[0]))));
_vm->bind_method<0>("str", "__repr__", [](VM* vm, Args& args) {
_vm->bind_method<0>("str", "__repr__", [](VM* vm, ArgsView args) {
const Str& _self = CAST(Str&, args[0]);
return VAR(_self.escape());
});
_vm->bind_method<0>("str", "__json__", [](VM* vm, Args& args) {
_vm->bind_method<0>("str", "__json__", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]);
return VAR(self.escape(false));
});
_vm->bind_method<1>("str", "__eq__", [](VM* vm, Args& args) {
_vm->bind_method<1>("str", "__eq__", [](VM* vm, ArgsView args) {
if(is_type(args[0], vm->tp_str) && is_type(args[1], vm->tp_str))
return VAR(CAST(Str&, args[0]) == CAST(Str&, args[1]));
return VAR(args[0] == args[1]);
});
_vm->bind_method<1>("str", "__ne__", [](VM* vm, Args& args) {
_vm->bind_method<1>("str", "__ne__", [](VM* vm, ArgsView args) {
if(is_type(args[0], vm->tp_str) && is_type(args[1], vm->tp_str))
return VAR(CAST(Str&, args[0]) != CAST(Str&, args[1]));
return VAR(args[0] != args[1]);
});
_vm->bind_method<1>("str", "__getitem__", [](VM* vm, Args& args) {
_vm->bind_method<1>("str", "__getitem__", [](VM* vm, ArgsView args) {
const Str& self (CAST(Str&, args[0]));
if(is_type(args[1], vm->tp_slice)){
@ -371,32 +375,32 @@ inline void init_builtins(VM* _vm) {
return VAR(self.u8_getitem(index));
});
_vm->bind_method<1>("str", "__gt__", [](VM* vm, Args& args) {
_vm->bind_method<1>("str", "__gt__", [](VM* vm, ArgsView args) {
const Str& self (CAST(Str&, args[0]));
const Str& obj (CAST(Str&, args[1]));
return VAR(self > obj);
});
_vm->bind_method<1>("str", "__lt__", [](VM* vm, Args& args) {
_vm->bind_method<1>("str", "__lt__", [](VM* vm, ArgsView args) {
const Str& self (CAST(Str&, args[0]));
const Str& obj (CAST(Str&, args[1]));
return VAR(self < obj);
});
_vm->bind_method<2>("str", "replace", [](VM* vm, Args& args) {
_vm->bind_method<2>("str", "replace", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]);
const Str& old = CAST(Str&, args[1]);
const Str& new_ = CAST(Str&, args[2]);
return VAR(self.replace(old, new_));
});
_vm->bind_method<1>("str", "startswith", [](VM* vm, Args& args) {
_vm->bind_method<1>("str", "startswith", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]);
const Str& prefix = CAST(Str&, args[1]);
return VAR(self.index(prefix) == 0);
});
_vm->bind_method<1>("str", "endswith", [](VM* vm, Args& args) {
_vm->bind_method<1>("str", "endswith", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]);
const Str& suffix = CAST(Str&, args[1]);
int offset = self.length() - suffix.length();
@ -405,7 +409,7 @@ inline void init_builtins(VM* _vm) {
return VAR(ok);
});
_vm->bind_method<1>("str", "join", [](VM* vm, Args& args) {
_vm->bind_method<1>("str", "join", [](VM* vm, ArgsView args) {
const Str& self = CAST(Str&, args[0]);
FastStrStream ss;
PyObject* obj = vm->asList(args[1]);
@ -418,13 +422,13 @@ inline void init_builtins(VM* _vm) {
});
/************ PyList ************/
_vm->bind_method<1>("list", "append", [](VM* vm, Args& args) {
_vm->bind_method<1>("list", "append", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]);
self.push_back(args[1]);
return vm->None;
});
_vm->bind_method<1>("list", "extend", [](VM* vm, Args& args) {
_vm->bind_method<1>("list", "extend", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]);
PyObject* obj = vm->asList(args[1]);
const List& list = CAST(List&, obj);
@ -432,13 +436,13 @@ inline void init_builtins(VM* _vm) {
return vm->None;
});
_vm->bind_method<0>("list", "reverse", [](VM* vm, Args& args) {
_vm->bind_method<0>("list", "reverse", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]);
std::reverse(self.begin(), self.end());
return vm->None;
});
_vm->bind_method<1>("list", "__mul__", [](VM* vm, Args& args) {
_vm->bind_method<1>("list", "__mul__", [](VM* vm, ArgsView args) {
const List& self = CAST(List&, args[0]);
int n = CAST(int, args[1]);
List result;
@ -447,7 +451,7 @@ inline void init_builtins(VM* _vm) {
return VAR(std::move(result));
});
_vm->bind_method<2>("list", "insert", [](VM* vm, Args& args) {
_vm->bind_method<2>("list", "insert", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]);
int index = CAST(int, args[1]);
if(index < 0) index += self.size();
@ -457,14 +461,14 @@ inline void init_builtins(VM* _vm) {
return vm->None;
});
_vm->bind_method<0>("list", "clear", [](VM* vm, Args& args) {
_vm->bind_method<0>("list", "clear", [](VM* vm, ArgsView args) {
CAST(List&, args[0]).clear();
return vm->None;
});
_vm->bind_method<0>("list", "copy", CPP_LAMBDA(VAR(CAST(List, args[0]))));
_vm->bind_method<1>("list", "__add__", [](VM* vm, Args& args) {
_vm->bind_method<1>("list", "__add__", [](VM* vm, ArgsView args) {
const List& self = CAST(List&, args[0]);
const List& other = CAST(List&, args[1]);
List new_list(self); // copy construct
@ -472,16 +476,16 @@ inline void init_builtins(VM* _vm) {
return VAR(std::move(new_list));
});
_vm->bind_method<0>("list", "__len__", [](VM* vm, Args& args) {
_vm->bind_method<0>("list", "__len__", [](VM* vm, ArgsView args) {
const List& self = CAST(List&, args[0]);
return VAR(self.size());
});
_vm->bind_method<0>("list", "__iter__", [](VM* vm, Args& args) {
_vm->bind_method<0>("list", "__iter__", [](VM* vm, ArgsView args) {
return vm->PyIter(ArrayIter<List>(vm, args[0]));
});
_vm->bind_method<1>("list", "__getitem__", [](VM* vm, Args& args) {
_vm->bind_method<1>("list", "__getitem__", [](VM* vm, ArgsView args) {
const List& self = CAST(List&, args[0]);
if(is_type(args[1], vm->tp_slice)){
@ -497,7 +501,7 @@ inline void init_builtins(VM* _vm) {
return self[index];
});
_vm->bind_method<2>("list", "__setitem__", [](VM* vm, Args& args) {
_vm->bind_method<2>("list", "__setitem__", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]);
int index = CAST(int, args[1]);
index = vm->normalized_index(index, self.size());
@ -505,7 +509,7 @@ inline void init_builtins(VM* _vm) {
return vm->None;
});
_vm->bind_method<1>("list", "__delitem__", [](VM* vm, Args& args) {
_vm->bind_method<1>("list", "__delitem__", [](VM* vm, ArgsView args) {
List& self = CAST(List&, args[0]);
int index = CAST(int, args[1]);
index = vm->normalized_index(index, self.size());
@ -514,16 +518,16 @@ inline void init_builtins(VM* _vm) {
});
/************ PyTuple ************/
_vm->bind_static_method<1>("tuple", "__new__", [](VM* vm, Args& args) {
_vm->bind_static_method<1>("tuple", "__new__", [](VM* vm, ArgsView args) {
List list = CAST(List, vm->asList(args[0]));
return VAR(Tuple(std::move(list)));
});
_vm->bind_method<0>("tuple", "__iter__", [](VM* vm, Args& args) {
_vm->bind_method<0>("tuple", "__iter__", [](VM* vm, ArgsView args) {
return vm->PyIter(ArrayIter<Args>(vm, args[0]));
});
_vm->bind_method<1>("tuple", "__getitem__", [](VM* vm, Args& args) {
_vm->bind_method<1>("tuple", "__getitem__", [](VM* vm, ArgsView args) {
const Tuple& self = CAST(Tuple&, args[0]);
if(is_type(args[1], vm->tp_slice)){
@ -539,7 +543,7 @@ inline void init_builtins(VM* _vm) {
return self[index];
});
_vm->bind_method<0>("tuple", "__len__", [](VM* vm, Args& args) {
_vm->bind_method<0>("tuple", "__len__", [](VM* vm, ArgsView args) {
const Tuple& self = CAST(Tuple&, args[0]);
return VAR(self.size());
});
@ -547,17 +551,17 @@ inline void init_builtins(VM* _vm) {
/************ PyBool ************/
_vm->bind_static_method<1>("bool", "__new__", CPP_LAMBDA(VAR(vm->asBool(args[0]))));
_vm->bind_method<0>("bool", "__repr__", [](VM* vm, Args& args) {
_vm->bind_method<0>("bool", "__repr__", [](VM* vm, ArgsView args) {
bool val = CAST(bool, args[0]);
return VAR(val ? "True" : "False");
});
_vm->bind_method<0>("bool", "__json__", [](VM* vm, Args& args) {
_vm->bind_method<0>("bool", "__json__", [](VM* vm, ArgsView args) {
bool val = CAST(bool, args[0]);
return VAR(val ? "true" : "false");
});
_vm->bind_method<1>("bool", "__xor__", [](VM* vm, Args& args) {
_vm->bind_method<1>("bool", "__xor__", [](VM* vm, ArgsView args) {
bool self = CAST(bool, args[0]);
bool other = CAST(bool, args[1]);
return VAR(self ^ other);
@ -579,7 +583,7 @@ inline void init_builtins(VM* _vm) {
inline void add_module_time(VM* vm){
PyObject* mod = vm->new_module("time");
vm->bind_func<0>(mod, "time", [](VM* vm, Args& args) {
vm->bind_func<0>(mod, "time", [](VM* vm, ArgsView args) {
auto now = std::chrono::high_resolution_clock::now();
return VAR(std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count() / 1000000.0);
});
@ -588,28 +592,23 @@ inline void add_module_time(VM* vm){
inline void add_module_sys(VM* vm){
PyObject* mod = vm->new_module("sys");
vm->setattr(mod, "version", VAR(PK_VERSION));
vm->bind_func<0>(mod, "getrecursionlimit", CPP_LAMBDA(VAR(vm->recursionlimit)));
vm->bind_func<1>(mod, "setrecursionlimit", [](VM* vm, Args& args) {
vm->recursionlimit = CAST(int, args[0]);
return vm->None;
});
}
inline void add_module_json(VM* vm){
PyObject* mod = vm->new_module("json");
vm->bind_func<1>(mod, "loads", [](VM* vm, Args& args) {
vm->bind_func<1>(mod, "loads", [](VM* vm, ArgsView args) {
const Str& expr = CAST(Str&, args[0]);
CodeObject_ code = vm->compile(expr, "<json>", JSON_MODE);
return vm->_exec(code, vm->top_frame()->_module);
});
vm->bind_func<1>(mod, "dumps", CPP_LAMBDA(vm->fast_call(__json__, Args{args[0]})));
vm->bind_func<1>(mod, "dumps", CPP_LAMBDA(vm->call_method(args[0], __json__)));
}
inline void add_module_math(VM* vm){
PyObject* mod = vm->new_module("math");
vm->setattr(mod, "pi", VAR(3.1415926535897932384));
vm->setattr(mod, "e" , VAR(2.7182818284590452354));
mod->attr().set("pi", VAR(3.1415926535897932384));
mod->attr().set("e" , VAR(2.7182818284590452354));
vm->bind_func<1>(mod, "log", CPP_LAMBDA(VAR(std::log(vm->num_to_float(args[0])))));
vm->bind_func<1>(mod, "log10", CPP_LAMBDA(VAR(std::log10(vm->num_to_float(args[0])))));
@ -627,7 +626,7 @@ inline void add_module_math(VM* vm){
inline void add_module_dis(VM* vm){
PyObject* mod = vm->new_module("dis");
vm->bind_func<1>(mod, "dis", [](VM* vm, Args& args) {
vm->bind_func<1>(mod, "dis", [](VM* vm, ArgsView args) {
PyObject* f = args[0];
if(is_type(f, vm->tp_bound_method)) f = CAST(BoundMethod, args[0]).method;
CodeObject_ code = CAST(Function&, f).decl->code;
@ -649,12 +648,12 @@ struct ReMatch {
vm->bind_method<0>(type, "start", CPP_LAMBDA(VAR(CAST(ReMatch&, args[0]).start)));
vm->bind_method<0>(type, "end", CPP_LAMBDA(VAR(CAST(ReMatch&, args[0]).end)));
vm->bind_method<0>(type, "span", [](VM* vm, Args& args) {
vm->bind_method<0>(type, "span", [](VM* vm, ArgsView args) {
auto& self = CAST(ReMatch&, args[0]);
return VAR(Tuple({VAR(self.start), VAR(self.end)}));
});
vm->bind_method<1>(type, "group", [](VM* vm, Args& args) {
vm->bind_method<1>(type, "group", [](VM* vm, ArgsView args) {
auto& self = CAST(ReMatch&, args[0]);
int index = CAST(int, args[1]);
index = vm->normalized_index(index, self.m.size());
@ -679,19 +678,19 @@ inline void add_module_re(VM* vm){
PyObject* mod = vm->new_module("re");
ReMatch::register_class(vm, mod);
vm->bind_func<2>(mod, "match", [](VM* vm, Args& args) {
vm->bind_func<2>(mod, "match", [](VM* vm, ArgsView args) {
const Str& pattern = CAST(Str&, args[0]);
const Str& string = CAST(Str&, args[1]);
return _regex_search(pattern, string, true, vm);
});
vm->bind_func<2>(mod, "search", [](VM* vm, Args& args) {
vm->bind_func<2>(mod, "search", [](VM* vm, ArgsView args) {
const Str& pattern = CAST(Str&, args[0]);
const Str& string = CAST(Str&, args[1]);
return _regex_search(pattern, string, false, vm);
});
vm->bind_func<3>(mod, "sub", [](VM* vm, Args& args) {
vm->bind_func<3>(mod, "sub", [](VM* vm, ArgsView args) {
const Str& pattern = CAST(Str&, args[0]);
const Str& repl = CAST(Str&, args[1]);
const Str& string = CAST(Str&, args[2]);
@ -699,7 +698,7 @@ inline void add_module_re(VM* vm){
return VAR(std::regex_replace(string.str(), re, repl.str()));
});
vm->bind_func<2>(mod, "split", [](VM* vm, Args& args) {
vm->bind_func<2>(mod, "split", [](VM* vm, ArgsView args) {
const Str& pattern = CAST(Str&, args[0]);
const Str& string = CAST(Str&, args[1]);
std::regex re(pattern.begin(), pattern.end());
@ -789,11 +788,11 @@ inline void VM::post_init(){
// property is defined in builtins.py so we need to add it after builtins is loaded
_t(tp_object)->attr().set(__class__, property(CPP_LAMBDA(vm->_t(args[0]))));
_t(tp_type)->attr().set(__base__, property([](VM* vm, Args& args){
_t(tp_type)->attr().set(__base__, property([](VM* vm, ArgsView args){
const PyTypeInfo& info = vm->_all_types[OBJ_GET(Type, args[0])];
return info.base.index == -1 ? vm->None : vm->_all_types[info.base].obj;
}));
_t(tp_type)->attr().set(__name__, property([](VM* vm, Args& args){
_t(tp_type)->attr().set(__name__, property([](VM* vm, ArgsView args){
const PyTypeInfo& info = vm->_all_types[OBJ_GET(Type, args[0])];
return VAR(info.name);
}));
@ -964,7 +963,7 @@ extern "C" {
ss << f_header;
for(int i=0; i<args.size(); i++){
ss << ' ';
pkpy::PyObject* x = vm->fast_call(pkpy::__json__, pkpy::Args{args[i]});
pkpy::PyObject* x = vm->call_method(args[i], pkpy::__json__);
ss << pkpy::CAST(pkpy::Str&, x);
}
char* packet = strdup(ss.str().c_str());

View File

@ -63,24 +63,9 @@ public:
PyObject** begin() const { return _args; }
PyObject** end() const { return _args + _size; }
void extend_self(PyObject* self){
PyObject** old_args = _args;
int old_size = _size;
_alloc(old_size+1);
_args[0] = self;
for(int i=0; i<old_size; i++) _args[i+1] = old_args[i];
if(old_args!=nullptr) pool64.dealloc(old_args);
}
~Tuple(){ if(_args!=nullptr) pool64.dealloc(_args); }
};
using Args = Tuple;
inline const Args& no_arg() {
static const Args _zero(0);
return _zero;
}
// a lightweight view for function args, it does not own the memory
struct ArgsView{
PyObject** _begin;
@ -109,4 +94,6 @@ struct ArgsView{
}
};
using Args = ArgsView;
} // namespace pkpy

336
src/vm.h
View File

@ -8,9 +8,23 @@
#include "memory.h"
#include "obj.h"
#include "str.h"
#include "tuplelist.h"
#include <tuple>
namespace pkpy{
/* Stack manipulation macros */
// https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123
#define TOP() (s_data.top())
#define SECOND() (s_data.second())
#define THIRD() (s_data.third())
#define PEEK(n) (s_data.peek(n))
#define STACK_SHRINK(n) (s_data.shrink(n))
#define PUSH(v) (s_data.push(v))
#define POP() (s_data.pop())
#define POPX() (s_data.popx())
#define STACK_VIEW(n) (s_data.view(n))
Str _read_file_cwd(const Str& name, bool* ok);
#define DEF_NATIVE_2(ctype, ptype) \
@ -68,9 +82,10 @@ public:
NameDict _modules; // loaded modules
std::map<StrName, Str> _lazy_modules; // lazy loaded modules
PyObject* _py_null;
PyObject* _py_begin_call;
PyObject* _py_op_call;
PyObject* _py_op_yield;
PyObject* _py_null;
PyObject* None;
PyObject* True;
PyObject* False;
@ -82,7 +97,6 @@ public:
std::stringstream _stderr_buffer;
std::ostream* _stdout;
std::ostream* _stderr;
int recursionlimit = 1000;
// for quick access
Type tp_object, tp_type, tp_int, tp_float, tp_bool, tp_str;
@ -111,7 +125,7 @@ public:
PyObject* asStr(PyObject* obj){
PyObject* self;
PyObject* f = get_unbound_method(obj, __str__, &self, false);
if(self != _py_null) return call(f, Args{self});
if(self != _py_null) return call_method(self, f);
return asRepr(obj);
}
@ -119,14 +133,14 @@ public:
if(is_type(obj, tp_iterator)) return obj;
PyObject* self;
PyObject* iter_f = get_unbound_method(obj, __iter__, &self, false);
if(self != _py_null) return call(iter_f, Args{self});
if(self != _py_null) return call_method(self, iter_f);
TypeError(OBJ_NAME(_t(obj)).escape() + " object is not iterable");
return nullptr;
}
PyObject* asList(PyObject* iterable){
if(is_type(iterable, tp_list)) return iterable;
return call(_t(tp_list), Args{iterable});
PyObject* asList(PyObject* it){
if(is_non_tagged_type(it, tp_list)) return it;
return call_(_t(tp_list), it);
}
PyObject* find_name_in_mro(PyObject* cls, StrName name){
@ -153,17 +167,18 @@ public:
return false;
}
PyObject* fast_call(StrName name, Args&& args){
PyObject* val = find_name_in_mro(_t(args[0]), name);
if(val != nullptr) return call(val, std::move(args));
AttributeError(args[0], name);
return nullptr;
}
template<typename ArgT>
std::enable_if_t<std::is_same_v<std::decay_t<ArgT>, Args>, PyObject*>
call(PyObject* callable, ArgT&& args){
return call(callable, std::forward<ArgT>(args), no_arg(), false);
PyObject* fast_call_method(PyObject* obj, StrName name, int ARGC){
PyObject* callable = find_name_in_mro(_t(obj), name);
if(callable == nullptr) AttributeError(obj, name);
// [a, b]
// [......., a, b]
// [unbound, a, b]
// ^^^^^^^
s_data._sp++;
PyObject** t = s_data._sp;
for(; t>s_data._sp-ARGC; t--) *t = t[-1];
*t = obj;
return _vectorcall(ARGC-1);
}
PyObject* exec(Str source, Str filename, CompileMode mode, PyObject* _module=nullptr){
@ -185,34 +200,54 @@ public:
}
#endif
callstack.clear();
s_data.clear();
return nullptr;
}
template<typename ...Args>
void _push_new_frame(Args&&... args){
if(callstack.size() > recursionlimit){
_error("RecursionError", "maximum recursion depth exceeded");
}
callstack.emplace(&s_data, std::forward<Args>(args)...);
}
void _push_new_frame(Frame&& frame){
if(callstack.size() > recursionlimit){
_error("RecursionError", "maximum recursion depth exceeded");
}
callstack.emplace(std::move(frame));
}
template<typename ...Args>
PyObject* _exec(Args&&... args){
_push_new_frame(std::forward<Args>(args)...);
callstack.emplace(&s_data, s_data._sp, std::forward<Args>(args)...);
return _run_top_frame();
}
void _push_varargs(int n, ...){
va_list args;
va_start(args, n);
for(int i=0; i<n; i++){
PyObject* obj = va_arg(args, PyObject*);
PUSH(obj);
}
va_end(args);
}
template<typename... Args>
PyObject* call_(PyObject* callable, Args&&... args){
PUSH(callable);
PUSH(_py_null);
int ARGC = sizeof...(args);
_push_varargs(ARGC, args...);
return _vectorcall(ARGC);
}
template<typename... Args>
PyObject* call_method(PyObject* self, PyObject* callable, Args&&... args){
PUSH(callable);
PUSH(self);
int ARGC = sizeof...(args);
_push_varargs(ARGC, args...);
return _vectorcall(ARGC);
}
template<typename... Args>
PyObject* call_method(PyObject* self, StrName name, Args&&... args){
PyObject* callable = get_unbound_method(self, name, &self);
return call_method(self, callable, args...);
}
PyObject* property(NativeFuncRaw fget){
PyObject* p = builtins->attr("property");
PyObject* method = heap.gcnew(tp_native_function, NativeFunc(fget, 1, false));
return call(p, Args{method});
return call_(p, method);
}
PyObject* new_type_object(PyObject* mod, StrName name, Type base){
@ -297,6 +332,7 @@ public:
else throw UnhandledException();
}
void RecursionError() { _error("RecursionError", "maximum recursion depth exceeded"); }
void IOError(const Str& msg) { _error("IOError", msg); }
void NotImplementedError(){ _error("NotImplementedError", ""); }
void TypeError(const Str& msg){ _error("TypeError", msg); }
@ -334,6 +370,8 @@ public:
_lazy_modules.clear();
}
PyObject* _vectorcall(int ARGC, int KWARGC=0, bool op_call=false);
CodeObject_ compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope=false);
PyObject* num_negated(PyObject* obj);
f64 num_to_float(PyObject* obj);
@ -343,13 +381,10 @@ public:
PyObject* new_module(StrName name);
Str disassemble(CodeObject_ co);
void init_builtin_types();
PyObject* call(PyObject* callable, Args args, const Args& kwargs, bool opCall);
PyObject* _py_call(PyObject* callable, ArgsView args, ArgsView kwargs);
void unpack_args(Args& args);
PyObject* _py_call(PyObject** sp_base, PyObject* callable, ArgsView args, ArgsView kwargs);
PyObject* getattr(PyObject* obj, StrName name, bool throw_err=true);
PyObject* get_unbound_method(PyObject* obj, StrName name, PyObject** self, bool throw_err=true, bool fallback=false);
template<typename T>
void setattr(PyObject* obj, StrName name, T&& value);
void setattr(PyObject* obj, StrName name, PyObject* value);
template<int ARGC>
void bind_method(PyObject*, Str, NativeFuncRaw);
template<int ARGC>
@ -359,7 +394,7 @@ public:
void post_init();
};
inline PyObject* NativeFunc::operator()(VM* vm, Args& args) const{
inline PyObject* NativeFunc::operator()(VM* vm, ArgsView args) const{
int args_size = args.size() - (int)method; // remove self
if(argc != -1 && args_size != argc) {
vm->TypeError(fmt("expected ", argc, " arguments, but got ", args_size));
@ -512,23 +547,25 @@ inline f64 VM::num_to_float(PyObject* obj){
}
inline bool VM::asBool(PyObject* obj){
if(is_type(obj, tp_bool)) return obj == True;
if(is_non_tagged_type(obj, tp_bool)) return obj == True;
if(obj == None) return false;
if(is_type(obj, tp_int)) return CAST(i64, obj) != 0;
if(is_type(obj, tp_float)) return CAST(f64, obj) != 0.0;
if(is_int(obj)) return CAST(i64, obj) != 0;
if(is_float(obj)) return CAST(f64, obj) != 0.0;
PyObject* self;
PyObject* len_f = get_unbound_method(obj, __len__, &self, false);
if(self != _py_null){
PyObject* ret = call(len_f, Args{self});
PUSH(len_f);
PUSH(self);
PyObject* ret = _vectorcall(0);
return CAST(i64, ret) > 0;
}
return true;
}
inline i64 VM::hash(PyObject* obj){
if (is_type(obj, tp_str)) return CAST(Str&, obj).hash();
if (is_non_tagged_type(obj, tp_str)) return CAST(Str&, obj).hash();
if (is_int(obj)) return CAST(i64, obj);
if (is_type(obj, tp_tuple)) {
if (is_non_tagged_type(obj, tp_tuple)) {
i64 x = 1000003;
const Tuple& items = CAST(Tuple&, obj);
for (int i=0; i<items.size(); i++) {
@ -538,8 +575,8 @@ inline i64 VM::hash(PyObject* obj){
}
return x;
}
if (is_type(obj, tp_type)) return BITS(obj);
if (is_type(obj, tp_bool)) return _CAST(bool, obj) ? 1 : 0;
if (is_non_tagged_type(obj, tp_type)) return BITS(obj);
if (is_non_tagged_type(obj, tp_bool)) return _CAST(bool, obj) ? 1 : 0;
if (is_float(obj)){
f64 val = CAST(f64, obj);
return (i64)std::hash<f64>()(val);
@ -550,7 +587,7 @@ inline i64 VM::hash(PyObject* obj){
inline PyObject* VM::asRepr(PyObject* obj){
// TODO: fastcall does not take care of super() proxy!
return fast_call(__repr__, Args{obj});
return fast_call_method(obj, __repr__, 0);
}
inline PyObject* VM::new_module(StrName name) {
@ -657,9 +694,12 @@ inline void VM::init_builtin_types(){
this->Ellipsis = heap._new<Dummy>(_new_type_object("ellipsis"), {});
this->True = heap._new<Dummy>(tp_bool, {});
this->False = heap._new<Dummy>(tp_bool, {});
this->_py_null = heap._new<Dummy>(_new_type_object("_py_null"), {});
this->_py_op_call = heap._new<Dummy>(_new_type_object("_py_op_call"), {});
this->_py_op_yield = heap._new<Dummy>(_new_type_object("_py_op_yield"), {});
Type _internal_type = _new_type_object("_internal");
this->_py_null = heap._new<Dummy>(_internal_type, {});
this->_py_begin_call = heap._new<Dummy>(_internal_type, {});
this->_py_op_call = heap._new<Dummy>(_internal_type, {});
this->_py_op_yield = heap._new<Dummy>(_internal_type, {});
this->builtins = new_module("builtins");
this->_main = new_module("__main__");
@ -682,8 +722,102 @@ inline void VM::init_builtin_types(){
for(auto [k, v]: _modules.items()) v->attr()._try_perfect_rehash();
}
inline PyObject* VM::_py_call(PyObject* callable, ArgsView args, ArgsView kwargs){
// callable is a `function` object
inline PyObject* VM::_vectorcall(int ARGC, int KWARGC, bool op_call){
bool is_varargs = ARGC == 0xFFFF;
PyObject** p0;
PyObject** p1 = s_data._sp - KWARGC*2;
if(is_varargs){
p0 = p1 - 1;
while(*p0 != _py_begin_call) p0--;
// [BEGIN_CALL, callable, <self>, args..., kwargs...]
// ^p0 ^p1 ^_sp
ARGC = p1 - (p0 + 3);
}else{
p0 = p1 - ARGC - 2 - (int)is_varargs;
// [callable, <self>, args..., kwargs...]
// ^p0 ^p1 ^_sp
}
PyObject* callable = p1[-(ARGC + 2)];
bool method_call = p1[-(ARGC + 1)] != _py_null;
ArgsView args(p1 - ARGC - int(method_call), p1);
// handle boundmethod, do a patch
if(is_non_tagged_type(callable, tp_bound_method)){
if(method_call) FATAL_ERROR();
auto& bm = CAST(BoundMethod&, callable);
callable = bm.method; // get unbound method
p1[-(ARGC + 2)] = bm.method;
p1[-(ARGC + 1)] = bm.obj;
// [unbound, self, args..., kwargs...]
}
if(is_non_tagged_type(callable, tp_native_function)){
const auto& f = OBJ_GET(NativeFunc, callable);
if(KWARGC != 0) TypeError("native_function does not accept keyword arguments");
PyObject* ret = f(this, args);
s_data.reset(p0);
return ret;
}
ArgsView kwargs(p1, s_data._sp);
if(is_non_tagged_type(callable, tp_function)){
// ret is nullptr or a generator
PyObject* ret = _py_call(p0, callable, args, kwargs);
// stack resetting is handled by _py_call
if(ret != nullptr) return ret;
if(op_call) return _py_op_call;
return _run_top_frame();
}
if(is_non_tagged_type(callable, tp_type)){
if(method_call) FATAL_ERROR();
// [type, NULL, args..., kwargs...]
// TODO: derived __new__ ?
PyObject* new_f = callable->attr().try_get(__new__);
PyObject* obj;
if(new_f != nullptr){
PUSH(new_f);
s_data.dup_top_n(1 + ARGC + KWARGC*2);
obj = _vectorcall(ARGC, KWARGC, false);
if(!isinstance(obj, OBJ_GET(Type, callable))) return obj;
}else{
obj = heap.gcnew<DummyInstance>(OBJ_GET(Type, callable), {});
}
PyObject* self;
callable = get_unbound_method(obj, __init__, &self, false);
if (self != _py_null) {
// replace `NULL` with `self`
p1[-(ARGC + 2)] = callable;
p1[-(ARGC + 1)] = self;
// [init_f, self, args..., kwargs...]
_vectorcall(ARGC, KWARGC, false);
// We just discard the return value of `__init__`
// in cpython it raises a TypeError if the return value is not None
}else{
// manually reset the stack
s_data.reset(p0);
}
return obj;
}
// handle `__call__` overload
PyObject* self;
PyObject* call_f = get_unbound_method(callable, __call__, &self, false);
if(self != _py_null){
p1[-(ARGC + 2)] = call_f;
p1[-(ARGC + 1)] = self;
// [call_f, self, args..., kwargs...]
return _vectorcall(ARGC, KWARGC, false);
}
TypeError(OBJ_NAME(_t(callable)).escape() + " object is not callable");
return nullptr;
}
inline PyObject* VM::_py_call(PyObject** sp_base, PyObject* callable, ArgsView args, ArgsView kwargs){
// callable must be a `function` object
const Function& fn = CAST(Function&, callable);
const CodeObject* co = fn.decl->code.get();
FastLocals locals(co);
@ -723,85 +857,17 @@ inline PyObject* VM::_py_call(PyObject* callable, ArgsView args, ArgsView kwargs
for(int i=0; i<kwargs.size(); i+=2){
StrName key = CAST(int, kwargs[i]);
// 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, "()"));
}
bool ok = locals._try_set(key, kwargs[i+1]);
if(!ok) TypeError(fmt(key.escape(), " is an invalid keyword argument for ", co->name, "()"));
}
PyObject* _module = fn._module != nullptr ? fn._module : top_frame()->_module;
if(co->is_generator){
return PyIter(Generator(this, Frame(&s_data, co, _module, std::move(locals), fn._closure)));
}
_push_new_frame(co, _module, std::move(locals), fn._closure);
if(co->is_generator) return PyIter(Generator(this, Frame(
&s_data, sp_base, co, _module, std::move(locals), fn._closure
)));
callstack.emplace(&s_data, sp_base, co, _module, std::move(locals), fn._closure);
return nullptr;
}
// TODO: callable/args here may be garbage collected accidentally
inline PyObject* VM::call(PyObject* callable, Args args, const Args& kwargs, bool opCall){
if(is_type(callable, tp_bound_method)){
auto& bm = CAST(BoundMethod&, callable);
callable = bm.method; // get unbound method
args.extend_self(bm.obj);
}
if(is_type(callable, tp_native_function)){
const auto& f = OBJ_GET(NativeFunc, callable);
if(kwargs.size() != 0) TypeError("native_function does not accept keyword arguments");
return f(this, args);
} else if(is_type(callable, tp_function)){
// ret is nullptr or a generator
PyObject* ret = _py_call(callable, args, kwargs);
if(ret != nullptr) return ret;
if(opCall) return _py_op_call;
return _run_top_frame();
}
if(is_type(callable, tp_type)){
// TODO: derived __new__ ?
PyObject* new_f = callable->attr().try_get(__new__);
PyObject* obj;
if(new_f != nullptr){
// should not use std::move here, since we will reuse args in possible __init__
obj = call(new_f, args, kwargs, false);
if(!isinstance(obj, OBJ_GET(Type, callable))) return obj;
}else{
obj = heap.gcnew<DummyInstance>(OBJ_GET(Type, callable), {});
}
PyObject* self;
PyObject* init_f = get_unbound_method(obj, __init__, &self, false);
if (self != _py_null) {
args.extend_self(self);
call(init_f, std::move(args), kwargs, false);
}
return obj;
}
PyObject* self;
PyObject* call_f = get_unbound_method(callable, __call__, &self, false);
if(self != _py_null){
args.extend_self(self);
return call(call_f, std::move(args), kwargs, false);
}
TypeError(OBJ_NAME(_t(callable)).escape() + " object is not callable");
return None;
}
inline void VM::unpack_args(Args& args){
List unpacked;
for(int i=0; i<args.size(); i++){
if(is_type(args[i], tp_star_wrapper)){
auto& star = _CAST(StarWrapper&, args[i]);
List& list = CAST(List&, asList(star.obj));
unpacked.extend(list);
}else{
unpacked.push_back(args[i]);
}
}
args = Args(std::move(unpacked));
}
// https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
inline PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err){
PyObject* objtype = _t(obj);
@ -815,7 +881,7 @@ inline PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err){
if(cls_var != nullptr){
// handle descriptor
PyObject* descr_get = _t(cls_var)->attr().try_get(__get__);
if(descr_get != nullptr) return call(descr_get, Args{cls_var, obj});
if(descr_get != nullptr) return call_method(cls_var, descr_get, obj);
}
// handle instance __dict__
if(!is_tagged(obj) && obj->is_attr_valid()){
@ -850,7 +916,7 @@ inline PyObject* VM::get_unbound_method(PyObject* obj, StrName name, PyObject**
if(cls_var != nullptr){
// handle descriptor
PyObject* descr_get = _t(cls_var)->attr().try_get(__get__);
if(descr_get != nullptr) return call(descr_get, Args{cls_var, obj});
if(descr_get != nullptr) return call_method(cls_var, descr_get, obj);
}
// handle instance __dict__
if(!is_tagged(obj) && obj->is_attr_valid()){
@ -869,9 +935,7 @@ inline PyObject* VM::get_unbound_method(PyObject* obj, StrName name, PyObject**
return nullptr;
}
template<typename T>
inline void VM::setattr(PyObject* obj, StrName name, T&& value){
static_assert(std::is_same_v<std::decay_t<T>, PyObject*>);
inline void VM::setattr(PyObject* obj, StrName name, PyObject* value){
PyObject* objtype = _t(obj);
// handle super() proxy
if(is_type(obj, tp_super)){
@ -886,7 +950,7 @@ inline void VM::setattr(PyObject* obj, StrName name, T&& value){
if(cls_var_t->attr().contains(__get__)){
PyObject* descr_set = cls_var_t->attr().try_get(__set__);
if(descr_set != nullptr){
call(descr_set, Args{cls_var, obj, std::forward<T>(value)});
call_method(cls_var, descr_set, obj, value);
}else{
TypeError(fmt("readonly attribute: ", name.escape()));
}
@ -895,7 +959,7 @@ inline void VM::setattr(PyObject* obj, StrName name, T&& value){
}
// handle instance __dict__
if(is_tagged(obj) || !obj->is_attr_valid()) TypeError("cannot set attribute");
obj->attr().set(name, std::forward<T>(value));
obj->attr().set(name, value);
}
template<int ARGC>