blueloveTH f3ac21ccc2 ...
2023-07-02 01:39:24 +08:00

880 lines
30 KiB
C++

#pragma once
#include "codeobject.h"
#include "common.h"
#include "frame.h"
#include "error.h"
#include "gc.h"
#include "memory.h"
#include "obj.h"
#include "str.h"
#include "tuplelist.h"
#include "dict.h"
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))
#define DEF_NATIVE_2(ctype, ptype) \
template<> inline ctype py_cast<ctype>(VM* vm, PyObject* obj) { \
vm->check_non_tagged_type(obj, vm->ptype); \
return PK_OBJ_GET(ctype, obj); \
} \
template<> inline ctype _py_cast<ctype>(VM* vm, PyObject* obj) { \
PK_UNUSED(vm); \
return PK_OBJ_GET(ctype, obj); \
} \
template<> inline ctype& py_cast<ctype&>(VM* vm, PyObject* obj) { \
vm->check_non_tagged_type(obj, vm->ptype); \
return PK_OBJ_GET(ctype, obj); \
} \
template<> inline ctype& _py_cast<ctype&>(VM* vm, PyObject* obj) { \
PK_UNUSED(vm); \
return PK_OBJ_GET(ctype, obj); \
} \
inline PyObject* py_var(VM* vm, const ctype& value) { return vm->heap.gcnew(vm->ptype, value);} \
inline PyObject* py_var(VM* vm, ctype&& value) { return vm->heap.gcnew(vm->ptype, std::move(value));}
typedef PyObject* (*BinaryFuncC)(VM*, PyObject*, PyObject*);
struct PyTypeInfo{
PyObject* obj;
Type base;
Str name;
bool subclass_enabled;
// cached special methods
// unary operators
PyObject* (*m__repr__)(VM* vm, PyObject*) = nullptr;
PyObject* (*m__str__)(VM* vm, PyObject*) = nullptr;
i64 (*m__hash__)(VM* vm, PyObject*) = nullptr;
i64 (*m__len__)(VM* vm, PyObject*) = nullptr;
PyObject* (*m__iter__)(VM* vm, PyObject*) = nullptr;
PyObject* (*m__next__)(VM* vm, PyObject*) = nullptr;
PyObject* (*m__json__)(VM* vm, PyObject*) = nullptr;
PyObject* (*m__neg__)(VM* vm, PyObject*) = nullptr;
PyObject* (*m__bool__)(VM* vm, PyObject*) = nullptr;
BinaryFuncC m__eq__ = nullptr;
BinaryFuncC m__lt__ = nullptr;
BinaryFuncC m__le__ = nullptr;
BinaryFuncC m__gt__ = nullptr;
BinaryFuncC m__ge__ = nullptr;
BinaryFuncC m__contains__ = nullptr;
// binary operators
BinaryFuncC m__add__ = nullptr;
BinaryFuncC m__sub__ = nullptr;
BinaryFuncC m__mul__ = nullptr;
BinaryFuncC m__truediv__ = nullptr;
BinaryFuncC m__floordiv__ = nullptr;
BinaryFuncC m__mod__ = nullptr;
BinaryFuncC m__pow__ = nullptr;
BinaryFuncC m__matmul__ = nullptr;
BinaryFuncC m__lshift__ = nullptr;
BinaryFuncC m__rshift__ = nullptr;
BinaryFuncC m__and__ = nullptr;
BinaryFuncC m__or__ = nullptr;
BinaryFuncC m__xor__ = nullptr;
// indexer
PyObject* (*m__getitem__)(VM* vm, PyObject*, PyObject*) = nullptr;
void (*m__setitem__)(VM* vm, PyObject*, PyObject*, PyObject*) = nullptr;
void (*m__delitem__)(VM* vm, PyObject*, PyObject*) = nullptr;
};
struct FrameId{
std::vector<pkpy::Frame>* data;
int index;
FrameId(std::vector<pkpy::Frame>* data, int index) : data(data), index(index) {}
Frame* operator->() const { return &data->operator[](index); }
Frame* get() const { return &data->operator[](index); }
};
typedef void(*PrintFunc)(VM*, const Str&);
class VM {
VM* vm; // self reference for simplify code
public:
ManagedHeap heap;
ValueStack s_data;
stack< Frame > callstack;
std::vector<PyTypeInfo> _all_types;
NameDict _modules; // loaded modules
std::map<StrName, Str> _lazy_modules; // lazy loaded modules
PyObject* None;
PyObject* True;
PyObject* False;
PyObject* NotImplemented; // unused
PyObject* Ellipsis;
PyObject* builtins; // builtins module
PyObject* StopIteration;
PyObject* _main; // __main__ module
PyObject* _last_exception;
#if PK_ENABLE_CEVAL_CALLBACK
void (*_ceval_on_step)(VM*, Frame*, Bytecode bc) = nullptr;
#endif
PrintFunc _stdout;
PrintFunc _stderr;
Bytes (*_import_handler)(const Str& name);
// for quick access
Type tp_object, tp_type, tp_int, tp_float, tp_bool, tp_str;
Type tp_list, tp_tuple;
Type tp_function, tp_native_func, tp_bound_method;
Type tp_slice, tp_range, tp_module;
Type tp_super, tp_exception, tp_bytes, tp_mappingproxy;
Type tp_dict, tp_property, tp_star_wrapper;
PyObject* cached_object__new__;
const bool enable_os;
VM(bool enable_os=true) : heap(this), enable_os(enable_os) {
this->vm = this;
_stdout = [](VM* vm, const Str& s) {
PK_UNUSED(vm);
std::cout << s;
};
_stderr = [](VM* vm, const Str& s) {
PK_UNUSED(vm);
std::cerr << s;
};
callstack.reserve(8);
_main = nullptr;
_last_exception = nullptr;
_import_handler = [](const Str& name) {
PK_UNUSED(name);
return Bytes();
};
init_builtin_types();
}
FrameId top_frame() {
#if PK_DEBUG_EXTRA_CHECK
if(callstack.empty()) FATAL_ERROR();
#endif
return FrameId(&callstack.data(), callstack.size()-1);
}
PyObject* py_str(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__str__) return ti->m__str__(this, obj);
PyObject* self;
PyObject* f = get_unbound_method(obj, __str__, &self, false);
if(self != PY_NULL) return call_method(self, f);
return py_repr(obj);
}
PyObject* py_repr(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__repr__) return ti->m__repr__(this, obj);
return call_method(obj, __repr__);
}
PyObject* py_json(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__json__) return ti->m__json__(this, obj);
return call_method(obj, __json__);
}
PyObject* py_iter(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__iter__) return ti->m__iter__(this, obj);
PyObject* self;
PyObject* iter_f = get_unbound_method(obj, __iter__, &self, false);
if(self != PY_NULL) return call_method(self, iter_f);
TypeError(OBJ_NAME(_t(obj)).escape() + " object is not iterable");
return nullptr;
}
PyObject* find_name_in_mro(PyObject* cls, StrName name){
PyObject* val;
do{
val = cls->attr().try_get(name);
if(val != nullptr) return val;
Type base = _all_types[PK_OBJ_GET(Type, cls)].base;
if(base.index == -1) break;
cls = _all_types[base].obj;
}while(true);
return nullptr;
}
bool isinstance(PyObject* obj, Type cls_t){
Type obj_t = PK_OBJ_GET(Type, _t(obj));
do{
if(obj_t == cls_t) return true;
Type base = _all_types[obj_t].base;
if(base.index == -1) break;
obj_t = base;
}while(true);
return false;
}
PyObject* exec(Str source, Str filename, CompileMode mode, PyObject* _module=nullptr){
if(_module == nullptr) _module = _main;
try {
CodeObject_ code = compile(source, filename, mode);
#if PK_DEBUG_DIS_EXEC
if(_module == _main) std::cout << disassemble(code) << '\n';
#endif
return _exec(code, _module);
}catch (const Exception& e){
_stderr(this, e.summary() + "\n");
}
#if !PK_DEBUG_FULL_EXCEPTION
catch (const std::exception& e) {
Str msg = "An std::exception occurred! It could be a bug.\n";
msg = msg + e.what();
_stderr(this, msg + "\n");
}
#endif
callstack.clear();
s_data.clear();
return nullptr;
}
template<typename ...Args>
PyObject* _exec(Args&&... args){
callstack.emplace(&s_data, s_data._sp, std::forward<Args>(args)...);
return _run_top_frame();
}
void _pop_frame(){
Frame* frame = &callstack.top();
s_data.reset(frame->_sp_base);
callstack.pop();
}
void _push_varargs(){ }
void _push_varargs(PyObject* _0){ PUSH(_0); }
void _push_varargs(PyObject* _0, PyObject* _1){ PUSH(_0); PUSH(_1); }
void _push_varargs(PyObject* _0, PyObject* _1, PyObject* _2){ PUSH(_0); PUSH(_1); PUSH(_2); }
void _push_varargs(PyObject* _0, PyObject* _1, PyObject* _2, PyObject* _3){ PUSH(_0); PUSH(_1); PUSH(_2); PUSH(_3); }
template<typename... Args>
PyObject* call(PyObject* callable, Args&&... args){
PUSH(callable);
PUSH(PY_NULL);
_push_varargs(args...);
return vectorcall(sizeof...(args));
}
template<typename... Args>
PyObject* call_method(PyObject* self, PyObject* callable, Args&&... args){
PUSH(callable);
PUSH(self);
_push_varargs(args...);
return vectorcall(sizeof...(args));
}
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(NativeFuncC fget, NativeFuncC fset=nullptr){
PyObject* _0 = heap.gcnew(tp_native_func, NativeFunc(fget, 1, false));
PyObject* _1 = vm->None;
if(fset != nullptr) _1 = heap.gcnew(tp_native_func, NativeFunc(fset, 2, false));
return call(_t(tp_property), _0, _1);
}
PyObject* new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled=true){
PyObject* obj = heap._new<Type>(tp_type, _all_types.size());
const PyTypeInfo& base_info = _all_types[base];
if(!base_info.subclass_enabled){
TypeError(fmt("type ", base_info.name.escape(), " is not `subclass_enabled`"));
}
PyTypeInfo info{
obj,
base,
(mod!=nullptr && mod!=builtins) ? Str(OBJ_NAME(mod)+"."+name.sv()): name.sv(),
subclass_enabled,
};
if(mod != nullptr) mod->attr().set(name, obj);
_all_types.push_back(info);
return obj;
}
Type _new_type_object(StrName name, Type base=0) {
PyObject* obj = new_type_object(nullptr, name, base, false);
return PK_OBJ_GET(Type, obj);
}
PyObject* _find_type_object(const Str& type){
PyObject* obj = builtins->attr().try_get(type);
if(obj == nullptr){
for(auto& t: _all_types) if(t.name == type) return t.obj;
throw std::runtime_error(fmt("type not found: ", type));
}
check_non_tagged_type(obj, tp_type);
return obj;
}
Type _type(const Str& type){
PyObject* obj = _find_type_object(type);
return PK_OBJ_GET(Type, obj);
}
PyTypeInfo* _type_info(const Str& type){
PyObject* obj = builtins->attr().try_get(type);
if(obj == nullptr){
for(auto& t: _all_types) if(t.name == type) return &t;
FATAL_ERROR();
}
return &_all_types[PK_OBJ_GET(Type, obj)];
}
PyTypeInfo* _type_info(Type type){
return &_all_types[type];
}
const PyTypeInfo* _inst_type_info(PyObject* obj){
if(is_int(obj)) return &_all_types[tp_int];
if(is_float(obj)) return &_all_types[tp_float];
return &_all_types[obj->type];
}
#define BIND_UNARY_SPECIAL(name) \
void bind##name(Type type, PyObject* (*f)(VM*, PyObject*)){ \
_all_types[type].m##name = f; \
PyObject* nf = bind_method<0>(_t(type), #name, [](VM* vm, ArgsView args){ \
return lambda_get_userdata<PyObject*(*)(VM*, PyObject*)>(args.begin())(vm, args[0]);\
}); \
PK_OBJ_GET(NativeFunc, nf).set_userdata(f); \
}
BIND_UNARY_SPECIAL(__repr__)
BIND_UNARY_SPECIAL(__str__)
BIND_UNARY_SPECIAL(__iter__)
BIND_UNARY_SPECIAL(__next__)
BIND_UNARY_SPECIAL(__json__)
BIND_UNARY_SPECIAL(__neg__)
BIND_UNARY_SPECIAL(__bool__)
void bind__hash__(Type type, i64 (*f)(VM* vm, PyObject*));
void bind__len__(Type type, i64 (*f)(VM* vm, PyObject*));
#undef BIND_UNARY_SPECIAL
#define BIND_BINARY_SPECIAL(name) \
void bind##name(Type type, BinaryFuncC f){ \
PyObject* obj = _t(type); \
_all_types[type].m##name = f; \
PyObject* nf = bind_method<1>(obj, #name, [](VM* vm, ArgsView args){ \
return lambda_get_userdata<BinaryFuncC>(args.begin())(vm, args[0], args[1]); \
}); \
PK_OBJ_GET(NativeFunc, nf).set_userdata(f); \
}
BIND_BINARY_SPECIAL(__eq__)
BIND_BINARY_SPECIAL(__lt__)
BIND_BINARY_SPECIAL(__le__)
BIND_BINARY_SPECIAL(__gt__)
BIND_BINARY_SPECIAL(__ge__)
BIND_BINARY_SPECIAL(__contains__)
BIND_BINARY_SPECIAL(__add__)
BIND_BINARY_SPECIAL(__sub__)
BIND_BINARY_SPECIAL(__mul__)
BIND_BINARY_SPECIAL(__truediv__)
BIND_BINARY_SPECIAL(__floordiv__)
BIND_BINARY_SPECIAL(__mod__)
BIND_BINARY_SPECIAL(__pow__)
BIND_BINARY_SPECIAL(__matmul__)
BIND_BINARY_SPECIAL(__lshift__)
BIND_BINARY_SPECIAL(__rshift__)
BIND_BINARY_SPECIAL(__and__)
BIND_BINARY_SPECIAL(__or__)
BIND_BINARY_SPECIAL(__xor__)
#undef BIND_BINARY_SPECIAL
void bind__getitem__(Type type, PyObject* (*f)(VM*, PyObject*, PyObject*)){
PyObject* obj = _t(type);
_all_types[type].m__getitem__ = f;
PyObject* nf = bind_method<1>(obj, "__getitem__", [](VM* vm, ArgsView args){
return lambda_get_userdata<PyObject*(*)(VM*, PyObject*, PyObject*)>(args.begin())(vm, args[0], args[1]);
});
PK_OBJ_GET(NativeFunc, nf).set_userdata(f);
}
void bind__setitem__(Type type, void (*f)(VM*, PyObject*, PyObject*, PyObject*)){
PyObject* obj = _t(type);
_all_types[type].m__setitem__ = f;
PyObject* nf = bind_method<2>(obj, "__setitem__", [](VM* vm, ArgsView args){
lambda_get_userdata<void(*)(VM* vm, PyObject*, PyObject*, PyObject*)>(args.begin())(vm, args[0], args[1], args[2]);
return vm->None;
});
PK_OBJ_GET(NativeFunc, nf).set_userdata(f);
}
void bind__delitem__(Type type, void (*f)(VM*, PyObject*, PyObject*)){
PyObject* obj = _t(type);
_all_types[type].m__delitem__ = f;
PyObject* nf = bind_method<1>(obj, "__delitem__", [](VM* vm, ArgsView args){
lambda_get_userdata<void(*)(VM*, PyObject*, PyObject*)>(args.begin())(vm, args[0], args[1]);
return vm->None;
});
PK_OBJ_GET(NativeFunc, nf).set_userdata(f);
}
bool py_equals(PyObject* lhs, PyObject* rhs){
if(lhs == rhs) return true;
const PyTypeInfo* ti = _inst_type_info(lhs);
PyObject* res;
if(ti->m__eq__){
res = ti->m__eq__(this, lhs, rhs);
if(res != vm->NotImplemented) return res == vm->True;
}
res = call_method(lhs, __eq__, rhs);
if(res != vm->NotImplemented) return res == vm->True;
ti = _inst_type_info(rhs);
if(ti->m__eq__){
res = ti->m__eq__(this, rhs, lhs);
if(res != vm->NotImplemented) return res == vm->True;
}
res = call_method(rhs, __eq__, lhs);
if(res != vm->NotImplemented) return res == vm->True;
return false;
}
template<int ARGC>
PyObject* bind_func(Str type, Str name, NativeFuncC fn) {
return bind_func<ARGC>(_find_type_object(type), name, fn);
}
template<int ARGC>
PyObject* bind_method(Str type, Str name, NativeFuncC fn) {
return bind_method<ARGC>(_find_type_object(type), name, fn);
}
template<int ARGC, typename __T>
PyObject* bind_constructor(__T&& type, NativeFuncC fn) {
static_assert(ARGC==-1 || ARGC>=1);
return bind_func<ARGC>(std::forward<__T>(type), "__new__", fn);
}
template<typename T, typename __T>
PyObject* bind_default_constructor(__T&& type) {
return bind_constructor<1>(std::forward<__T>(type), [](VM* vm, ArgsView args){
Type t = PK_OBJ_GET(Type, args[0]);
return vm->heap.gcnew<T>(t, T());
});
}
template<typename T, typename __T>
PyObject* bind_notimplemented_constructor(__T&& type) {
return bind_constructor<-1>(std::forward<__T>(type), [](VM* vm, ArgsView args){
PK_UNUSED(args);
vm->NotImplementedError();
return vm->None;
});
}
template<int ARGC>
PyObject* bind_builtin_func(Str name, NativeFuncC fn) {
return bind_func<ARGC>(builtins, name, fn);
}
int normalized_index(int index, int size){
if(index < 0) index += size;
if(index < 0 || index >= size){
IndexError(std::to_string(index) + " not in [0, " + std::to_string(size) + ")");
}
return index;
}
PyObject* py_next(PyObject* obj){
const PyTypeInfo* ti = _inst_type_info(obj);
if(ti->m__next__) return ti->m__next__(this, obj);
return call_method(obj, __next__);
}
/***** Error Reporter *****/
void _error(StrName name, const Str& msg){
_error(Exception(name, msg));
}
void _raise(){
bool ok = top_frame()->jump_to_exception_handler();
if(ok) throw HandledException();
else throw UnhandledException();
}
void StackOverflowError() { _error("StackOverflowError", ""); }
void IOError(const Str& msg) { _error("IOError", msg); }
void NotImplementedError(){ _error("NotImplementedError", ""); }
void TypeError(const Str& msg){ _error("TypeError", msg); }
void IndexError(const Str& msg){ _error("IndexError", msg); }
void ValueError(const Str& msg){ _error("ValueError", msg); }
void NameError(StrName name){ _error("NameError", fmt("name ", name.escape() + " is not defined")); }
void KeyError(PyObject* obj){ _error("KeyError", PK_OBJ_GET(Str, py_repr(obj))); }
void BinaryOptError(const char* op) { TypeError(fmt("unsupported operand type(s) for ", op)); }
void AttributeError(PyObject* obj, StrName name){
// OBJ_NAME calls getattr, which may lead to a infinite recursion
_error("AttributeError", fmt("type ", OBJ_NAME(_t(obj)).escape(), " has no attribute ", name.escape()));
}
void AttributeError(Str msg){ _error("AttributeError", msg); }
void check_type(PyObject* obj, Type type){
if(is_type(obj, type)) return;
TypeError("expected " + OBJ_NAME(_t(type)).escape() + ", got " + OBJ_NAME(_t(obj)).escape());
}
void check_args_size(int size, int min_size, int max_size){
if(size >= min_size && size <= max_size) return;
TypeError(fmt("expected ", min_size, "-", max_size, " arguments, got ", size));
}
void check_non_tagged_type(PyObject* obj, Type type){
if(is_non_tagged_type(obj, type)) return;
TypeError("expected " + OBJ_NAME(_t(type)).escape() + ", got " + OBJ_NAME(_t(obj)).escape());
}
void check_int(PyObject* obj){
if(is_int(obj)) return;
check_type(obj, tp_int); // if failed, redirect to check_type to raise TypeError
}
void check_float(PyObject* obj){
if(is_float(obj)) return;
check_type(obj, tp_float); // if failed, redirect to check_type to raise TypeError
}
PyObject* _t(Type t){
return _all_types[t.index].obj;
}
PyObject* _t(PyObject* obj){
if(is_int(obj)) return _t(tp_int);
if(is_float(obj)) return _t(tp_float);
return _all_types[obj->type].obj;
}
struct ImportContext{
// 0: normal; 1: __init__.py; 2: relative
std::vector<std::pair<StrName, int>> pending;
struct Temp{
VM* vm;
StrName name;
Temp(VM* vm, StrName name, int type): vm(vm), name(name){
ImportContext* ctx = &vm->_import_context;
ctx->pending.emplace_back(name, type);
}
~Temp(){
ImportContext* ctx = &vm->_import_context;
ctx->pending.pop_back();
}
};
Temp temp(VM* vm, StrName name, int type){
return Temp(vm, name, type);
}
};
ImportContext _import_context;
PyObject* py_import(StrName name, bool relative=false){
Str filename;
int type;
if(relative){
ImportContext* ctx = &_import_context;
type = 2;
for(auto it=ctx->pending.rbegin(); it!=ctx->pending.rend(); ++it){
if(it->second == 2) continue;
if(it->second == 1){
filename = fmt(it->first, kPlatformSep, name, ".py");
name = fmt(it->first, '.', name).c_str();
break;
}
}
if(filename.length() == 0) _error("ImportError", "relative import outside of package");
}else{
type = 0;
filename = fmt(name, ".py");
}
for(auto& [k, v]: _import_context.pending){
if(k == name){
vm->_error("ImportError", fmt("circular import ", name.escape()));
}
}
PyObject* ext_mod = _modules.try_get(name);
if(ext_mod == nullptr){
Str source;
auto it = _lazy_modules.find(name);
if(it == _lazy_modules.end()){
Bytes b = _import_handler(filename);
if(!relative && !b){
filename = fmt(name, kPlatformSep, "__init__.py");
b = _import_handler(filename);
if(b) type = 1;
}
if(!b) _error("ImportError", fmt("module ", name.escape(), " not found"));
source = Str(b.str());
}else{
source = it->second;
_lazy_modules.erase(it);
}
auto _ = _import_context.temp(this, name, type);
CodeObject_ code = compile(source, filename, EXEC_MODE);
PyObject* new_mod = new_module(name);
_exec(code, new_mod);
new_mod->attr()._try_perfect_rehash();
return new_mod;
}else{
return ext_mod;
}
}
~VM() {
callstack.clear();
s_data.clear();
_all_types.clear();
_modules.clear();
_lazy_modules.clear();
}
#if PK_DEBUG_CEVAL_STEP
void _log_s_data(const char* title = nullptr);
#endif
void _unpack_as_list(ArgsView args, List& list);
void _unpack_as_dict(ArgsView args, Dict& dict);
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* py_negate(PyObject* obj);
f64 num_to_float(PyObject* obj);
bool py_bool(PyObject* obj);
i64 py_hash(PyObject* obj);
PyObject* py_list(PyObject*);
PyObject* new_module(StrName name);
Str disassemble(CodeObject_ co);
void init_builtin_types();
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);
void parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step);
PyObject* format(Str, PyObject*);
void setattr(PyObject* obj, StrName name, PyObject* value);
template<int ARGC>
PyObject* bind_method(PyObject*, Str, NativeFuncC);
template<int ARGC>
PyObject* bind_func(PyObject*, Str, NativeFuncC);
void _error(Exception);
PyObject* _run_top_frame();
void post_init();
PyObject* _py_generator(Frame&& frame, ArgsView buffer);
// new style binding api
PyObject* bind(PyObject*, const char*, const char*, NativeFuncC);
PyObject* bind(PyObject*, const char*, NativeFuncC);
void _prepare_py_call(PyObject**, ArgsView, ArgsView, const FuncDecl_&);
};
DEF_NATIVE_2(Str, tp_str)
DEF_NATIVE_2(List, tp_list)
DEF_NATIVE_2(Tuple, tp_tuple)
DEF_NATIVE_2(Function, tp_function)
DEF_NATIVE_2(NativeFunc, tp_native_func)
DEF_NATIVE_2(BoundMethod, tp_bound_method)
DEF_NATIVE_2(Range, tp_range)
DEF_NATIVE_2(Slice, tp_slice)
DEF_NATIVE_2(Exception, tp_exception)
DEF_NATIVE_2(Bytes, tp_bytes)
DEF_NATIVE_2(MappingProxy, tp_mappingproxy)
DEF_NATIVE_2(Dict, tp_dict)
DEF_NATIVE_2(Property, tp_property)
DEF_NATIVE_2(StarWrapper, tp_star_wrapper)
#undef DEF_NATIVE_2
#define PY_CAST_INT(T) \
template<> inline T py_cast<T>(VM* vm, PyObject* obj){ \
vm->check_int(obj); \
return (T)(PK_BITS(obj) >> 2); \
} \
template<> inline T _py_cast<T>(VM* vm, PyObject* obj){ \
PK_UNUSED(vm); \
return (T)(PK_BITS(obj) >> 2); \
}
PY_CAST_INT(char)
PY_CAST_INT(short)
PY_CAST_INT(int)
PY_CAST_INT(long)
PY_CAST_INT(long long)
PY_CAST_INT(unsigned char)
PY_CAST_INT(unsigned short)
PY_CAST_INT(unsigned int)
PY_CAST_INT(unsigned long)
PY_CAST_INT(unsigned long long)
template<> inline float py_cast<float>(VM* vm, PyObject* obj){
vm->check_float(obj);
i64 bits = PK_BITS(obj) & Number::c1;
return BitsCvt(bits)._float;
}
template<> inline float _py_cast<float>(VM* vm, PyObject* obj){
PK_UNUSED(vm);
i64 bits = PK_BITS(obj) & Number::c1;
return BitsCvt(bits)._float;
}
template<> inline double py_cast<double>(VM* vm, PyObject* obj){
vm->check_float(obj);
i64 bits = PK_BITS(obj) & Number::c1;
return BitsCvt(bits)._float;
}
template<> inline double _py_cast<double>(VM* vm, PyObject* obj){
PK_UNUSED(vm);
i64 bits = PK_BITS(obj) & Number::c1;
return BitsCvt(bits)._float;
}
#define PY_VAR_INT(T) \
inline PyObject* py_var(VM* vm, T _val){ \
i64 val = static_cast<i64>(_val); \
if(((val << 2) >> 2) != val){ \
vm->_error("OverflowError", std::to_string(val) + " is out of range"); \
} \
val = (val << 2) | 0b01; \
return reinterpret_cast<PyObject*>(val); \
}
PY_VAR_INT(char)
PY_VAR_INT(short)
PY_VAR_INT(int)
PY_VAR_INT(long)
PY_VAR_INT(long long)
PY_VAR_INT(unsigned char)
PY_VAR_INT(unsigned short)
PY_VAR_INT(unsigned int)
PY_VAR_INT(unsigned long)
PY_VAR_INT(unsigned long long)
#define PY_VAR_FLOAT(T) \
inline PyObject* py_var(VM* vm, T _val){ \
PK_UNUSED(vm); \
BitsCvt val(static_cast<f64>(_val)); \
i64 bits = val._int & Number::c1; \
i64 tail = val._int & Number::c2; \
if(tail == 0b10){ \
if(bits&0b100) bits += 0b100; \
}else if(tail == 0b11){ \
bits += 0b100; \
} \
bits |= 0b10; \
return reinterpret_cast<PyObject*>(bits); \
}
PY_VAR_FLOAT(float)
PY_VAR_FLOAT(double)
#undef PY_VAR_INT
#undef PY_VAR_FLOAT
inline PyObject* py_var(VM* vm, bool val){
return val ? vm->True : vm->False;
}
template<> inline bool py_cast<bool>(VM* vm, PyObject* obj){
if(obj == vm->True) return true;
if(obj == vm->False) return false;
vm->check_non_tagged_type(obj, vm->tp_bool);
return false;
}
template<> inline bool _py_cast<bool>(VM* vm, PyObject* obj){
return obj == vm->True;
}
template<> inline CString py_cast<CString>(VM* vm, PyObject* obj){
vm->check_non_tagged_type(obj, vm->tp_str);
return PK_OBJ_GET(Str, obj).c_str();
}
template<> inline CString _py_cast<CString>(VM* vm, PyObject* obj){
return PK_OBJ_GET(Str, obj).c_str();
}
inline PyObject* py_var(VM* vm, const char val[]){
return VAR(Str(val));
}
inline PyObject* py_var(VM* vm, std::string val){
return VAR(Str(std::move(val)));
}
inline PyObject* py_var(VM* vm, std::string_view val){
return VAR(Str(val));
}
inline PyObject* py_var(VM* vm, NoReturn val){
PK_UNUSED(val);
return vm->None;
}
inline PyObject* py_var(VM* vm, PyObject* val){
PK_UNUSED(vm);
return val;
}
template<int ARGC>
PyObject* VM::bind_method(PyObject* obj, Str name, NativeFuncC fn) {
check_non_tagged_type(obj, tp_type);
PyObject* nf = VAR(NativeFunc(fn, ARGC, true));
obj->attr().set(name, nf);
return nf;
}
template<int ARGC>
PyObject* VM::bind_func(PyObject* obj, Str name, NativeFuncC fn) {
PyObject* nf = VAR(NativeFunc(fn, ARGC, false));
obj->attr().set(name, nf);
return nf;
}
/***************************************************/
template<typename T>
PyObject* PyArrayGetItem(VM* vm, PyObject* obj, PyObject* index){
static_assert(std::is_same_v<T, List> || std::is_same_v<T, Tuple>);
const T& self = _CAST(T&, obj);
if(is_non_tagged_type(index, vm->tp_slice)){
const Slice& s = _CAST(Slice&, index);
int start, stop, step;
vm->parse_int_slice(s, self.size(), start, stop, step);
List new_list;
for(int i=start; step>0?i<stop:i>stop; i+=step) new_list.push_back(self[i]);
return VAR(T(std::move(new_list)));
}
int i = CAST(int, index);
i = vm->normalized_index(i, self.size());
return self[i];
}
} // namespace pkpy