From 51cf6d42e8dfccb6778f588c5efb60430e892577 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Mon, 17 Apr 2023 16:43:30 +0800 Subject: [PATCH] ... --- .github/workflows/main.yml | 236 +++++++++++++------------- benchmarks/recursive.py | 6 +- src/ceval.h | 147 +++++----------- src/cffi.h | 46 ++--- src/common.h | 4 +- src/frame.h | 42 +++-- src/io.h | 28 ++-- src/main.cpp | 2 +- src/obj.h | 11 +- src/opcodes.h | 3 - src/pocketpy.h | 165 +++++++++--------- src/tuplelist.h | 17 +- src/vm.h | 336 ++++++++++++++++++++++--------------- 13 files changed, 525 insertions(+), 518 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3f310555..b80c291b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 \ No newline at end of file +# 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 \ No newline at end of file diff --git a/benchmarks/recursive.py b/benchmarks/recursive.py index f0dd6334..9917aa9b 100644 --- a/benchmarks/recursive.py +++ b/benchmarks/recursive.py @@ -1,9 +1,5 @@ -import sys - -sys.setrecursionlimit(5000) - def f(n): - if n == 4000: + if n == 900: return -1 return f(n + 1) diff --git a/src/ceval.h b/src/ceval.h index 2f09176a..b14877ea 100644 --- a/src/ceval.h +++ b/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) diff --git a/src/cffi.h b/src/cffi.h index 521ecb0b..08e86b88 100644 --- a/src/cffi.h +++ b/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 - std::enable_if_t, PyObject*> call(VM* vm, Args& args, std::index_sequence) { + std::enable_if_t, PyObject*> call(VM* vm, ArgsView args, std::index_sequence) { func(py_cast(vm, args[Is])...); return vm->None; } template - std::enable_if_t, PyObject*> call(VM* vm, Args& args, std::index_sequence) { + std::enable_if_t, PyObject*> call(VM* vm, ArgsView args, std::index_sequence) { __Ret ret = func(py_cast(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 - std::enable_if_t, PyObject*> call(VM* vm, Args& args, std::index_sequence) { + std::enable_if_t, PyObject*> call(VM* vm, ArgsView args, std::index_sequence) { T& self = py_cast(vm, args[0]); (self.*func)(py_cast(vm, args[Is+1])...); return vm->None; } template - std::enable_if_t, PyObject*> call(VM* vm, Args& args, std::index_sequence) { + std::enable_if_t, PyObject*> call(VM* vm, ArgsView args, std::index_sequence) { T& self = py_cast(vm, args[0]); __Ret ret = (self.*func)(py_cast(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(), (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]); diff --git a/src/common.h b/src/common.h index 488e00b7..61f9a581 100644 --- a/src/common.h +++ b/src/common.h @@ -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()!"); diff --git a/src/frame.h b/src/frame.h index 79e36b7c..e45e0f46 100644 --- a/src/frame.h +++ b/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& 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; diff --git a/src/io.h b/src/io.h index a5fbb614..63814418 100644 --- a/src/io.h +++ b/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; ibind_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); diff --git a/src/main.cpp b/src/main.cpp index ebda9c09..f8599109 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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){ diff --git a/src/obj.h b/src/obj.h index 519ccd76..5b822e9b 100644 --- a/src/obj.h +++ b/src/obj.h @@ -11,7 +11,7 @@ struct Frame; struct Function; class VM; -typedef std::function NativeFuncRaw; +typedef std::function NativeFuncRaw; typedef shared_ptr 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); \ diff --git a/src/opcodes.h b/src/opcodes.h index 4c19bdcb..2b253fb9 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -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) /**************************/ diff --git a/src/pocketpy.h b/src/pocketpy.h index b9d170d5..ff5264db 100644 --- a/src/pocketpy.h +++ b/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_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_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 names; if(args[0]->is_attr_valid()){ std::vector 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(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(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(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_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; ifast_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()); diff --git a/src/tuplelist.h b/src/tuplelist.h index 0c8e47f7..53b56c6a 100644 --- a/src/tuplelist.h +++ b/src/tuplelist.h @@ -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 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 _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 - std::enable_if_t, Args>, PyObject*> - call(PyObject* callable, ArgT&& args){ - return call(callable, std::forward(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 - void _push_new_frame(Args&&... args){ - if(callstack.size() > recursionlimit){ - _error("RecursionError", "maximum recursion depth exceeded"); - } - callstack.emplace(&s_data, std::forward(args)...); - } - - void _push_new_frame(Frame&& frame){ - if(callstack.size() > recursionlimit){ - _error("RecursionError", "maximum recursion depth exceeded"); - } - callstack.emplace(std::move(frame)); - } - template PyObject* _exec(Args&&... args){ - _push_new_frame(std::forward(args)...); + callstack.emplace(&s_data, s_data._sp, std::forward(args)...); return _run_top_frame(); } + void _push_varargs(int n, ...){ + va_list args; + va_start(args, n); + for(int i=0; i + PyObject* call_(PyObject* callable, Args&&... args){ + PUSH(callable); + PUSH(_py_null); + int ARGC = sizeof...(args); + _push_varargs(ARGC, args...); + return _vectorcall(ARGC); + } + + template + 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 + 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 - void setattr(PyObject* obj, StrName name, T&& value); + void setattr(PyObject* obj, StrName name, PyObject* value); template void bind_method(PyObject*, Str, NativeFuncRaw); template @@ -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()(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(_new_type_object("ellipsis"), {}); this->True = heap._new(tp_bool, {}); this->False = heap._new(tp_bool, {}); - this->_py_null = heap._new(_new_type_object("_py_null"), {}); - this->_py_op_call = heap._new(_new_type_object("_py_op_call"), {}); - this->_py_op_yield = heap._new(_new_type_object("_py_op_yield"), {}); + + Type _internal_type = _new_type_object("_internal"); + this->_py_null = heap._new(_internal_type, {}); + this->_py_begin_call = heap._new(_internal_type, {}); + this->_py_op_call = heap._new(_internal_type, {}); + this->_py_op_yield = heap._new(_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, , args..., kwargs...] + // ^p0 ^p1 ^_sp + ARGC = p1 - (p0 + 3); + }else{ + p0 = p1 - ARGC - 2 - (int)is_varargs; + // [callable, , 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(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; iname, "()")); - } + 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(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; iattr().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 -inline void VM::setattr(PyObject* obj, StrName name, T&& value){ - static_assert(std::is_same_v, 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(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(value)); + obj->attr().set(name, value); } template