mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
...
This commit is contained in:
parent
adc2f2cc3f
commit
51cf6d42e8
236
.github/workflows/main.yml
vendored
236
.github/workflows/main.yml
vendored
@ -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
|
@ -1,9 +1,5 @@
|
||||
import sys
|
||||
|
||||
sys.setrecursionlimit(5000)
|
||||
|
||||
def f(n):
|
||||
if n == 4000:
|
||||
if n == 900:
|
||||
return -1
|
||||
return f(n + 1)
|
||||
|
||||
|
147
src/ceval.h
147
src/ceval.h
@ -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)
|
||||
|
46
src/cffi.h
46
src/cffi.h
@ -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]);
|
||||
|
@ -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()!");
|
||||
|
42
src/frame.h
42
src/frame.h
@ -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;
|
||||
|
28
src/io.h
28
src/io.h
@ -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);
|
||||
|
@ -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){
|
||||
|
11
src/obj.h
11
src/obj.h
@ -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); \
|
||||
|
@ -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)
|
||||
/**************************/
|
||||
|
165
src/pocketpy.h
165
src/pocketpy.h
@ -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());
|
||||
|
@ -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
336
src/vm.h
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user