mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 03:20:18 +00:00
Compare commits
10 Commits
026171e753
...
23ffa73f4c
Author | SHA1 | Date | |
---|---|---|---|
|
23ffa73f4c | ||
f4d676548f | |||
|
3e9a6256ad | ||
273ce0c186 | |||
|
e1e3e208cb | ||
|
f06f7e21c9 | ||
|
2a7067cd03 | ||
2c5f46f096 | |||
8aa52ba64a | |||
c4d14847e8 |
@ -61,13 +61,32 @@ endif()
|
|||||||
# PK_IS_MAIN determines whether the project is being used from root
|
# PK_IS_MAIN determines whether the project is being used from root
|
||||||
# or if it is added as a dependency (through add_subdirectory for example).
|
# or if it is added as a dependency (through add_subdirectory for example).
|
||||||
if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
|
if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
set(PK_IS_MAIN TRUE)
|
set(PK_IS_MAIN TRUE)
|
||||||
option(PK_BUILD_SHARED_LIB "Build shared library" OFF)
|
option(PK_BUILD_SHARED_LIB "Build shared library" OFF)
|
||||||
option(PK_BUILD_STATIC_LIB "Build static library" OFF)
|
option(PK_BUILD_STATIC_LIB "Build static library" OFF)
|
||||||
|
|
||||||
|
# @szdytom favored testing
|
||||||
|
# disabled by default because @blueloveTH doesn't like it :C
|
||||||
|
option(BUILD_TESTING "Build the testing tree." OFF)
|
||||||
|
if (BUILD_TESTING)
|
||||||
|
option(BUILD_TESTING_SANITIZE "Build the source with sanitizers" OFF)
|
||||||
|
if (BUILD_TESTING_SANITIZE)
|
||||||
|
if (MSVC)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /fno-omit-frame-pointer")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /fno-omit-frame-pointer")
|
||||||
|
else()
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,leak,undefined")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,leak,undefined")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(tests/)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
set(PK_IS_MAIN FALSE)
|
set(PK_IS_MAIN FALSE)
|
||||||
option(PK_BUILD_SHARED_LIB "Build shared library" OFF)
|
option(PK_BUILD_SHARED_LIB "Build shared library" OFF)
|
||||||
option(PK_BUILD_STATIC_LIB "Build static library" ON)
|
option(PK_BUILD_STATIC_LIB "Build static library" ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(PK_BUILD_SHARED_LIB)
|
if(PK_BUILD_SHARED_LIB)
|
||||||
@ -77,12 +96,15 @@ elseif(PK_BUILD_STATIC_LIB)
|
|||||||
else()
|
else()
|
||||||
set(PROJECT_EXE_NAME main)
|
set(PROJECT_EXE_NAME main)
|
||||||
add_executable(${PROJECT_EXE_NAME} src2/main.cpp)
|
add_executable(${PROJECT_EXE_NAME} src2/main.cpp)
|
||||||
# shared linked main
|
if (BUILD_TESTING_SANITIZE)
|
||||||
add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC})
|
# static linked main, for sanitizing purpose
|
||||||
target_link_libraries(${PROJECT_EXE_NAME} ${PROJECT_NAME} ${CMAKE_DL_LIBS})
|
add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC})
|
||||||
# static linked main
|
target_link_libraries(${PROJECT_EXE_NAME} ${PROJECT_NAME})
|
||||||
# add_library(${PROJECT_NAME} STATIC ${POCKETPY_SRC})
|
else()
|
||||||
# target_link_libraries(${PROJECT_EXE_NAME} ${PROJECT_NAME})
|
# shared linked main, used by default, for CI and others
|
||||||
|
add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC})
|
||||||
|
target_link_libraries(${PROJECT_EXE_NAME} ${PROJECT_NAME} ${CMAKE_DL_LIBS})
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(PK_USE_CJSON)
|
if(PK_USE_CJSON)
|
||||||
|
@ -9,7 +9,7 @@ order: 0
|
|||||||
Sometimes you need to use the following code to prevent the gc from collecting objects.
|
Sometimes you need to use the following code to prevent the gc from collecting objects.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
auto _lock = vm->heap.gc_scope_lock();
|
auto _lock = vm->gc_scope_lock();
|
||||||
```
|
```
|
||||||
|
|
||||||
The scope lock is required if you create a PyVar and then try to run python-level bytecodes.
|
The scope lock is required if you create a PyVar and then try to run python-level bytecodes.
|
||||||
@ -34,7 +34,7 @@ The scope lock prevents this from happening.
|
|||||||
void some_func(VM* vm){
|
void some_func(VM* vm){
|
||||||
PyVar obj = VAR(List(5));
|
PyVar obj = VAR(List(5));
|
||||||
// safe
|
// safe
|
||||||
auto _lock = vm->heap.gc_scope_lock();
|
auto _lock = vm->gc_scope_lock();
|
||||||
PyVar iter = vm->py_iter(obj);
|
PyVar iter = vm->py_iter(obj);
|
||||||
PyVar next = vm->py_next(iter);
|
PyVar next = vm->py_next(iter);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ PyObject* VM::bind_field(PyObject* obj, const char* name, F T::*field) {
|
|||||||
};
|
};
|
||||||
_1 = new_object<NativeFunc>(tp_native_func, fset, 2, field);
|
_1 = new_object<NativeFunc>(tp_native_func, fset, 2, field);
|
||||||
}
|
}
|
||||||
PyObject* prop = heap.gcnew<Property>(tp_property, _0, _1);
|
PyObject* prop = new_object<Property>(tp_property, _0, _1).get();
|
||||||
obj->attr().set(StrName(name_sv), prop);
|
obj->attr().set(StrName(name_sv), prop);
|
||||||
return prop;
|
return prop;
|
||||||
}
|
}
|
||||||
|
40
include/pocketpy/interpreter/gc.h
Normal file
40
include/pocketpy/interpreter/gc.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include "pocketpy/objects/object.h"
|
||||||
|
#include "pocketpy/objects/public.h"
|
||||||
|
#include "pocketpy/common/config.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct pk_ManagedHeap{
|
||||||
|
c11_vector no_gc;
|
||||||
|
c11_vector gen;
|
||||||
|
|
||||||
|
int gc_threshold;
|
||||||
|
int gc_counter;
|
||||||
|
int gc_lock_counter;
|
||||||
|
pkpy_VM* vm;
|
||||||
|
|
||||||
|
void (*_gc_on_delete)(pkpy_VM*, PyObject*);
|
||||||
|
void (*_gc_marker_ex)(pkpy_VM*);
|
||||||
|
} pk_ManagedHeap;
|
||||||
|
|
||||||
|
void pk_ManagedHeap__ctor(pk_ManagedHeap* self, pkpy_VM* vm);
|
||||||
|
void pk_ManagedHeap__dtor(pk_ManagedHeap* self);
|
||||||
|
|
||||||
|
void pk_ManagedHeap__push_lock(pk_ManagedHeap* self);
|
||||||
|
void pk_ManagedHeap__pop_lock(pk_ManagedHeap* self);
|
||||||
|
|
||||||
|
void pk_ManagedHeap__collect_if_needed(pk_ManagedHeap* self);
|
||||||
|
int pk_ManagedHeap__collect(pk_ManagedHeap* self);
|
||||||
|
int pk_ManagedHeap__sweep(pk_ManagedHeap* self);
|
||||||
|
|
||||||
|
PyObject* pk_ManagedHeap__new(pk_ManagedHeap* self, pkpy_Type type, int size, bool gc);
|
||||||
|
|
||||||
|
// external implementation
|
||||||
|
void pk_ManagedHeap__mark(pk_ManagedHeap* self);
|
||||||
|
void pk_ManagedHeap__delete_obj(pk_ManagedHeap* self, PyObject* obj);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -1,92 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pocketpy/common/config.h"
|
|
||||||
#include "pocketpy/common/vector.hpp"
|
|
||||||
#include "pocketpy/common/utils.h"
|
|
||||||
#include "pocketpy/objects/object.hpp"
|
|
||||||
#include "pocketpy/objects/namedict.hpp"
|
|
||||||
|
|
||||||
namespace pkpy {
|
|
||||||
struct ManagedHeap {
|
|
||||||
vector<PyObject*> _no_gc;
|
|
||||||
vector<PyObject*> gen;
|
|
||||||
VM* vm;
|
|
||||||
void (*_gc_on_delete)(VM*, PyObject*) = nullptr;
|
|
||||||
void (*_gc_marker_ex)(VM*) = nullptr;
|
|
||||||
|
|
||||||
ManagedHeap(VM* vm) : vm(vm) {}
|
|
||||||
|
|
||||||
int gc_threshold = PK_GC_MIN_THRESHOLD;
|
|
||||||
int gc_counter = 0;
|
|
||||||
|
|
||||||
/********************/
|
|
||||||
int _gc_lock_counter = 0;
|
|
||||||
|
|
||||||
struct ScopeLock {
|
|
||||||
PK_ALWAYS_PASS_BY_POINTER(ScopeLock)
|
|
||||||
|
|
||||||
ManagedHeap* heap;
|
|
||||||
|
|
||||||
ScopeLock(ManagedHeap* heap) : heap(heap) { heap->_gc_lock_counter++; }
|
|
||||||
|
|
||||||
~ScopeLock() { heap->_gc_lock_counter--; }
|
|
||||||
};
|
|
||||||
|
|
||||||
ScopeLock gc_scope_lock() { return ScopeLock(this); }
|
|
||||||
|
|
||||||
/********************/
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
PyObject* _basic_new(Type type, Args&&... args) {
|
|
||||||
using __T = std::decay_t<T>;
|
|
||||||
static_assert(!is_sso_v<__T>, "gcnew cannot be used with SSO types");
|
|
||||||
// https://github.com/pocketpy/pocketpy/issues/94#issuecomment-1594784476
|
|
||||||
PyObject* p;
|
|
||||||
if constexpr(py_sizeof<__T> <= kPoolObjectBlockSize){
|
|
||||||
p = new (PoolObject_alloc()) PyObject(type, false);
|
|
||||||
}else{
|
|
||||||
p = new (std::malloc(py_sizeof<__T>)) PyObject(type, true);
|
|
||||||
}
|
|
||||||
new (p->_value_ptr()) T(std::forward<Args>(args)...);
|
|
||||||
|
|
||||||
// backdoor for important builtin types
|
|
||||||
if constexpr(std::is_same_v<__T, DummyInstance>) {
|
|
||||||
p->_attr = new NameDict();
|
|
||||||
} else if constexpr(std::is_same_v<__T, Type>) {
|
|
||||||
p->_attr = new NameDict();
|
|
||||||
} else if constexpr(std::is_same_v<__T, DummyModule>) {
|
|
||||||
p->_attr = new NameDict();
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
PyObject* gcnew(Type type, Args&&... args) {
|
|
||||||
PyObject* p = _basic_new<T>(type, std::forward<Args>(args)...);
|
|
||||||
gen.push_back(p);
|
|
||||||
gc_counter++;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
PyObject* _new(Type type, Args&&... args) {
|
|
||||||
PyObject* p = _basic_new<T>(type, std::forward<Args>(args)...);
|
|
||||||
_no_gc.push_back(p);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _delete(PyObject*);
|
|
||||||
|
|
||||||
#if PK_DEBUG_GC_STATS
|
|
||||||
inline static std::map<Type, int> deleted;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int sweep();
|
|
||||||
void _auto_collect();
|
|
||||||
|
|
||||||
bool _should_auto_collect() const { return gc_counter >= gc_threshold; }
|
|
||||||
|
|
||||||
int collect();
|
|
||||||
void mark();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pkpy
|
|
12
include/pocketpy/interpreter/vm.h
Normal file
12
include/pocketpy/interpreter/vm.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "pocketpy/objects/object.h"
|
||||||
|
|
||||||
|
typedef struct pkpy_VM{
|
||||||
|
PyVar True;
|
||||||
|
PyVar False;
|
||||||
|
PyVar None;
|
||||||
|
PyVar NotImplemented;
|
||||||
|
PyVar Ellipsis;
|
||||||
|
} pkpy_VM;
|
||||||
|
|
||||||
|
void pkpy_VM__ctor(pkpy_VM* self);
|
||||||
|
void pkpy_VM__dtor(pkpy_VM* self);
|
@ -4,7 +4,7 @@
|
|||||||
#include "pocketpy/objects/dict.hpp"
|
#include "pocketpy/objects/dict.hpp"
|
||||||
#include "pocketpy/objects/error.hpp"
|
#include "pocketpy/objects/error.hpp"
|
||||||
#include "pocketpy/objects/builtins.hpp"
|
#include "pocketpy/objects/builtins.hpp"
|
||||||
#include "pocketpy/interpreter/gc.hpp"
|
#include "pocketpy/interpreter/gc.h"
|
||||||
#include "pocketpy/interpreter/frame.hpp"
|
#include "pocketpy/interpreter/frame.hpp"
|
||||||
#include "pocketpy/interpreter/profiler.hpp"
|
#include "pocketpy/interpreter/profiler.hpp"
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ class VM {
|
|||||||
VM* vm; // self reference to simplify code
|
VM* vm; // self reference to simplify code
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ManagedHeap heap;
|
pk_ManagedHeap heap;
|
||||||
ValueStack s_data;
|
ValueStack s_data;
|
||||||
CallStack callstack;
|
CallStack callstack;
|
||||||
vector<PyTypeInfo> _all_types;
|
vector<PyTypeInfo> _all_types;
|
||||||
@ -446,7 +446,31 @@ public:
|
|||||||
template<typename T, typename ...Args>
|
template<typename T, typename ...Args>
|
||||||
PyVar new_object(Type type, Args&&... args){
|
PyVar new_object(Type type, Args&&... args){
|
||||||
static_assert(!is_sso_v<T>);
|
static_assert(!is_sso_v<T>);
|
||||||
return heap.gcnew<T>(type, std::forward<Args>(args)...);
|
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||||
|
PyObject* p = (PyObject*)pk_ManagedHeap__new(&heap, type, py_sizeof<T>, true);
|
||||||
|
new (p->_value_ptr()) T(std::forward<Args>(args)...);
|
||||||
|
// backdoor for important builtin types
|
||||||
|
if constexpr(std::is_same_v<T, DummyInstance>
|
||||||
|
|| std::is_same_v<T, Type>
|
||||||
|
|| std::is_same_v<T, DummyModule>) {
|
||||||
|
p->_attr = new NameDict();
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename ...Args>
|
||||||
|
PyVar new_object_no_gc(Type type, Args&&... args){
|
||||||
|
static_assert(!is_sso_v<T>);
|
||||||
|
static_assert(std::is_same_v<T, std::decay_t<T>>);
|
||||||
|
PyObject* p = (PyObject*)pk_ManagedHeap__new(&heap, type, py_sizeof<T>, false);
|
||||||
|
new (p->_value_ptr()) T(std::forward<Args>(args)...);
|
||||||
|
// backdoor for important builtin types
|
||||||
|
if constexpr(std::is_same_v<T, DummyInstance>
|
||||||
|
|| std::is_same_v<T, Type>
|
||||||
|
|| std::is_same_v<T, DummyModule>) {
|
||||||
|
p->_attr = new NameDict();
|
||||||
|
}
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -456,7 +480,14 @@ public:
|
|||||||
if(it == nullptr) PK_FATAL_ERROR("T not found in cxx_typeid_map\n")
|
if(it == nullptr) PK_FATAL_ERROR("T not found in cxx_typeid_map\n")
|
||||||
return *it;
|
return *it;
|
||||||
}
|
}
|
||||||
|
/********** old heap op **********/
|
||||||
|
struct HeapScopeLock {
|
||||||
|
PK_ALWAYS_PASS_BY_POINTER(HeapScopeLock)
|
||||||
|
pk_ManagedHeap* heap;
|
||||||
|
HeapScopeLock(pk_ManagedHeap* heap) : heap(heap) { pk_ManagedHeap__push_lock(heap);}
|
||||||
|
~HeapScopeLock() { pk_ManagedHeap__pop_lock(heap); }
|
||||||
|
};
|
||||||
|
HeapScopeLock gc_scope_lock(){ return {&heap}; }
|
||||||
/********** private **********/
|
/********** private **********/
|
||||||
virtual ~VM();
|
virtual ~VM();
|
||||||
|
|
||||||
@ -569,13 +600,13 @@ PyVar py_var(VM* vm, __T&& value) {
|
|||||||
if constexpr(is_sso_v<T>)
|
if constexpr(is_sso_v<T>)
|
||||||
return PyVar(const_type, value);
|
return PyVar(const_type, value);
|
||||||
else
|
else
|
||||||
return vm->heap.gcnew<T>(const_type, std::forward<__T>(value));
|
return vm->new_object<T>(const_type, std::forward<__T>(value));
|
||||||
} else {
|
} else {
|
||||||
Type type = vm->_find_type_in_cxx_typeid_map<T>();
|
Type type = vm->_find_type_in_cxx_typeid_map<T>();
|
||||||
if constexpr(is_sso_v<T>)
|
if constexpr(is_sso_v<T>)
|
||||||
return PyVar(type, value);
|
return PyVar(type, value);
|
||||||
else
|
else
|
||||||
return vm->heap.gcnew<T>(type, std::forward<__T>(value));
|
return vm->new_object<T>(type, std::forward<__T>(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "stdint.h"
|
#include "stdint.h"
|
||||||
|
#include "stdbool.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -15,10 +16,24 @@ struct pkpy_G {
|
|||||||
pkpy_VM* vm;
|
pkpy_VM* vm;
|
||||||
} extern pkpy_g;
|
} extern pkpy_g;
|
||||||
|
|
||||||
|
void py_initialize();
|
||||||
|
void py_switch_vm(const char* name);
|
||||||
|
void py_finalize();
|
||||||
|
|
||||||
bool py_eq(const PyVar*, const PyVar*);
|
bool py_eq(const PyVar*, const PyVar*);
|
||||||
bool py_le(const PyVar*, const PyVar*);
|
bool py_le(const PyVar*, const PyVar*);
|
||||||
int64_t py_hash(const PyVar*);
|
int64_t py_hash(const PyVar*);
|
||||||
|
|
||||||
|
|
||||||
|
/* py_var */
|
||||||
|
void py_newint(PyVar*, int64_t);
|
||||||
|
void py_newfloat(PyVar*, double);
|
||||||
|
void py_newbool(PyVar*, bool);
|
||||||
|
void py_newstr(PyVar*, const char*);
|
||||||
|
void py_newstr2(PyVar*, const char*, int);
|
||||||
|
void py_newbytes(PyVar*, const uint8_t*, int);
|
||||||
|
void py_newnone(PyVar*);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -101,6 +101,7 @@ Error* Compiler::pop_context() noexcept{
|
|||||||
|
|
||||||
assert(func->type != FuncType::UNSET);
|
assert(func->type != FuncType::UNSET);
|
||||||
}
|
}
|
||||||
|
contexts.back().s_clean();
|
||||||
contexts.pop_back();
|
contexts.pop_back();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ void VM::__op_unpack_sequence(uint16_t arg) {
|
|||||||
ValueError(_S("expected ", (int)arg, " values to unpack, got ", (int)tuple.size()));
|
ValueError(_S("expected ", (int)arg, " values to unpack, got ", (int)tuple.size()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto _lock = heap.gc_scope_lock(); // lock the gc via RAII!!
|
auto _lock = gc_scope_lock(); // lock the gc via RAII!!
|
||||||
_0 = py_iter(_0);
|
_0 = py_iter(_0);
|
||||||
const PyTypeInfo* ti = _tp_info(_0);
|
const PyTypeInfo* ti = _tp_info(_0);
|
||||||
for(int i = 0; i < arg; i++) {
|
for(int i = 0; i < arg; i++) {
|
||||||
@ -802,7 +802,7 @@ PyVar VM::__run_top_frame() {
|
|||||||
DISPATCH()
|
DISPATCH()
|
||||||
case OP_REPR: TOP() = VAR(py_repr(TOP())); DISPATCH()
|
case OP_REPR: TOP() = VAR(py_repr(TOP())); DISPATCH()
|
||||||
case OP_CALL: {
|
case OP_CALL: {
|
||||||
if(heap._should_auto_collect()) heap._auto_collect();
|
pk_ManagedHeap__collect_if_needed(&heap);
|
||||||
PyVar _0 = vectorcall(byte.arg & 0xFF, // ARGC
|
PyVar _0 = vectorcall(byte.arg & 0xFF, // ARGC
|
||||||
(byte.arg >> 8) & 0xFF, // KWARGC
|
(byte.arg >> 8) & 0xFF, // KWARGC
|
||||||
true);
|
true);
|
||||||
@ -814,7 +814,7 @@ PyVar VM::__run_top_frame() {
|
|||||||
}
|
}
|
||||||
DISPATCH()
|
DISPATCH()
|
||||||
case OP_CALL_TP: {
|
case OP_CALL_TP: {
|
||||||
if(heap._should_auto_collect()) heap._auto_collect();
|
pk_ManagedHeap__collect_if_needed(&heap);
|
||||||
PyVar _0;
|
PyVar _0;
|
||||||
PyVar _1;
|
PyVar _1;
|
||||||
PyVar _2;
|
PyVar _2;
|
||||||
@ -1000,7 +1000,7 @@ PyVar VM::__run_top_frame() {
|
|||||||
}
|
}
|
||||||
DISPATCH()
|
DISPATCH()
|
||||||
case OP_UNPACK_EX: {
|
case OP_UNPACK_EX: {
|
||||||
auto _lock = heap.gc_scope_lock(); // lock the gc via RAII!!
|
auto _lock = gc_scope_lock(); // lock the gc via RAII!!
|
||||||
PyVar _0 = py_iter(POPX());
|
PyVar _0 = py_iter(POPX());
|
||||||
const PyTypeInfo* _ti = _tp_info(_0);
|
const PyTypeInfo* _ti = _tp_info(_0);
|
||||||
PyVar _1;
|
PyVar _1;
|
||||||
|
109
src/interpreter/gc.c
Normal file
109
src/interpreter/gc.c
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include "pocketpy/interpreter/gc.h"
|
||||||
|
#include "pocketpy/common/memorypool.h"
|
||||||
|
|
||||||
|
void pk_ManagedHeap__ctor(pk_ManagedHeap *self, pkpy_VM *vm){
|
||||||
|
c11_vector__ctor(&self->no_gc, sizeof(PyObject*));
|
||||||
|
c11_vector__ctor(&self->gen, sizeof(PyObject*));
|
||||||
|
|
||||||
|
self->gc_threshold = PK_GC_MIN_THRESHOLD;
|
||||||
|
self->gc_counter = 0;
|
||||||
|
self->gc_lock_counter = 0;
|
||||||
|
self->vm = vm;
|
||||||
|
|
||||||
|
self->_gc_on_delete = NULL;
|
||||||
|
self->_gc_marker_ex = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pk_ManagedHeap__dtor(pk_ManagedHeap *self){
|
||||||
|
for(int i = 0; i < self->gen.count; i++){
|
||||||
|
PyObject* obj = c11__getitem(PyObject*, &self->gen, i);
|
||||||
|
pk_ManagedHeap__delete_obj(self, obj);
|
||||||
|
}
|
||||||
|
for(int i = 0; i < self->no_gc.count; i++){
|
||||||
|
PyObject* obj = c11__getitem(PyObject*, &self->no_gc, i);
|
||||||
|
pk_ManagedHeap__delete_obj(self, obj);
|
||||||
|
}
|
||||||
|
c11_vector__dtor(&self->no_gc);
|
||||||
|
c11_vector__dtor(&self->gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pk_ManagedHeap__push_lock(pk_ManagedHeap *self){
|
||||||
|
self->gc_lock_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pk_ManagedHeap__pop_lock(pk_ManagedHeap *self){
|
||||||
|
self->gc_lock_counter--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pk_ManagedHeap__collect_if_needed(pk_ManagedHeap *self){
|
||||||
|
if(self->gc_counter < self->gc_threshold) return;
|
||||||
|
if(self->gc_lock_counter > 0) return;
|
||||||
|
self->gc_counter = 0;
|
||||||
|
pk_ManagedHeap__collect(self);
|
||||||
|
self->gc_threshold = self->gen.count * 2;
|
||||||
|
if(self->gc_threshold < PK_GC_MIN_THRESHOLD){
|
||||||
|
self->gc_threshold = PK_GC_MIN_THRESHOLD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int pk_ManagedHeap__collect(pk_ManagedHeap *self){
|
||||||
|
assert(self->gc_lock_counter == 0);
|
||||||
|
pk_ManagedHeap__mark(self);
|
||||||
|
int freed = pk_ManagedHeap__sweep(self);
|
||||||
|
return freed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pk_ManagedHeap__sweep(pk_ManagedHeap *self){
|
||||||
|
c11_vector alive;
|
||||||
|
c11_vector__ctor(&alive, sizeof(PyObject*));
|
||||||
|
c11_vector__reserve(&alive, self->gen.count / 2);
|
||||||
|
|
||||||
|
for(int i = 0; i < self->gen.count; i++){
|
||||||
|
PyObject* obj = c11__getitem(PyObject*, &self->gen, i);
|
||||||
|
if(obj->gc_marked) {
|
||||||
|
obj->gc_marked = false;
|
||||||
|
c11_vector__push(PyObject*, &alive, obj);
|
||||||
|
} else {
|
||||||
|
if(self->_gc_on_delete){
|
||||||
|
self->_gc_on_delete(self->vm, obj);
|
||||||
|
}
|
||||||
|
pk_ManagedHeap__delete_obj(self, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear _no_gc marked flag
|
||||||
|
for(int i=0; i<self->no_gc.count; i++){
|
||||||
|
PyObject* obj = c11__getitem(PyObject*, &self->no_gc, i);
|
||||||
|
obj->gc_marked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int freed = self->gen.count - alive.count;
|
||||||
|
|
||||||
|
// destroy old gen
|
||||||
|
c11_vector__dtor(&self->gen);
|
||||||
|
// move alive to gen
|
||||||
|
self->gen = alive;
|
||||||
|
|
||||||
|
PoolObject_shrink_to_fit();
|
||||||
|
return freed;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* pk_ManagedHeap__new(pk_ManagedHeap *self, pkpy_Type type, int size, bool gc){
|
||||||
|
PyObject* obj;
|
||||||
|
// TODO: can we use compile time check?
|
||||||
|
if(size <= kPoolObjectBlockSize){
|
||||||
|
obj = PoolObject_alloc();
|
||||||
|
PyObject__ctor(obj, type, false);
|
||||||
|
}else{
|
||||||
|
obj = malloc(size);
|
||||||
|
PyObject__ctor(obj, type, true);
|
||||||
|
}
|
||||||
|
// TODO: can we use compile time check?
|
||||||
|
if(gc){
|
||||||
|
c11_vector__push(PyObject*, &self->gen, obj);
|
||||||
|
self->gc_counter++;
|
||||||
|
}else{
|
||||||
|
c11_vector__push(PyObject*, &self->no_gc, obj);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
@ -1,56 +0,0 @@
|
|||||||
#include "pocketpy/interpreter/gc.hpp"
|
|
||||||
|
|
||||||
namespace pkpy {
|
|
||||||
|
|
||||||
int ManagedHeap::sweep() {
|
|
||||||
vector<PyObject*> alive;
|
|
||||||
alive.reserve(gen.size() / 2);
|
|
||||||
for(PyObject* obj: gen) {
|
|
||||||
if(obj->gc_marked) {
|
|
||||||
obj->gc_marked = false;
|
|
||||||
alive.push_back(obj);
|
|
||||||
} else {
|
|
||||||
#if PK_DEBUG_GC_STATS
|
|
||||||
deleted[obj->type] += 1;
|
|
||||||
#endif
|
|
||||||
if(_gc_on_delete) _gc_on_delete(vm, obj);
|
|
||||||
_delete(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear _no_gc marked flag
|
|
||||||
for(PyObject* obj: _no_gc)
|
|
||||||
obj->gc_marked = false;
|
|
||||||
|
|
||||||
int freed = gen.size() - alive.size();
|
|
||||||
|
|
||||||
#if PK_DEBUG_GC_STATS
|
|
||||||
for(auto& [type, count]: deleted) {
|
|
||||||
std::cout << "GC: " << _type_name(vm, type).sv() << "=" << count << std::endl;
|
|
||||||
}
|
|
||||||
std::cout << "GC: " << alive.size() << "/" << gen.size() << " (" << freed << " freed)" << std::endl;
|
|
||||||
deleted.clear();
|
|
||||||
#endif
|
|
||||||
gen.clear();
|
|
||||||
gen.swap(alive);
|
|
||||||
PoolObject_shrink_to_fit();
|
|
||||||
return freed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ManagedHeap::_auto_collect() {
|
|
||||||
#if !PK_DEBUG_NO_AUTO_GC
|
|
||||||
if(_gc_lock_counter > 0) return;
|
|
||||||
gc_counter = 0;
|
|
||||||
collect();
|
|
||||||
gc_threshold = gen.size() * 2;
|
|
||||||
if(gc_threshold < PK_GC_MIN_THRESHOLD) gc_threshold = PK_GC_MIN_THRESHOLD;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int ManagedHeap::collect() {
|
|
||||||
assert(_gc_lock_counter == 0);
|
|
||||||
mark();
|
|
||||||
int freed = sweep();
|
|
||||||
return freed;
|
|
||||||
}
|
|
||||||
} // namespace pkpy
|
|
40
src/interpreter/vm.c
Normal file
40
src/interpreter/vm.c
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// #include "pocketpy/interpreter/vm.h"
|
||||||
|
// #include "pocketpy/objects/base.h"
|
||||||
|
|
||||||
|
// void pkpy_VM__ctor(pkpy_VM* self){
|
||||||
|
// self->True = (PyVar){
|
||||||
|
// .type=tp_bool,
|
||||||
|
// .is_ptr=true,
|
||||||
|
// .extra=1,
|
||||||
|
// ._obj=pkpy_VM__gcnew(self, tp_bool)
|
||||||
|
// };
|
||||||
|
|
||||||
|
// self->False = (PyVar){
|
||||||
|
// .type=tp_bool,
|
||||||
|
// .is_ptr=true,
|
||||||
|
// .extra=0,
|
||||||
|
// ._obj=pkpy_VM__gcnew(self, tp_bool)
|
||||||
|
// };
|
||||||
|
|
||||||
|
// self->None = (PyVar){
|
||||||
|
// .type=tp_none_type,
|
||||||
|
// .is_ptr=true,
|
||||||
|
// ._obj=pkpy_VM__gcnew(self, tp_none_type)
|
||||||
|
// };
|
||||||
|
|
||||||
|
// self->NotImplemented = (PyVar){
|
||||||
|
// .type=tp_not_implemented_type,
|
||||||
|
// .is_ptr=true,
|
||||||
|
// ._obj=pkpy_VM__gcnew(self, tp_not_implemented_type)
|
||||||
|
// };
|
||||||
|
|
||||||
|
// self->Ellipsis = (PyVar){
|
||||||
|
// .type=tp_ellipsis,
|
||||||
|
// .is_ptr=true,
|
||||||
|
// ._obj=pkpy_VM__gcnew(self, tp_ellipsis)
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void pkpy_VM__dtor(pkpy_VM* self){
|
||||||
|
|
||||||
|
// }
|
@ -1,6 +1,7 @@
|
|||||||
#include "pocketpy/interpreter/vm.hpp"
|
#include "pocketpy/interpreter/vm.hpp"
|
||||||
#include "pocketpy/common/memorypool.h"
|
#include "pocketpy/common/memorypool.h"
|
||||||
#include "pocketpy/objects/base.h"
|
#include "pocketpy/objects/base.h"
|
||||||
|
#include "pocketpy/objects/public.h"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -77,16 +78,17 @@ struct JsonSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Str serialize() {
|
Str serialize() {
|
||||||
auto _lock = vm->heap.gc_scope_lock();
|
auto _lock = vm->gc_scope_lock();
|
||||||
write_object(root);
|
write_object(root);
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
VM::VM(bool enable_os) : heap(this), enable_os(enable_os) {
|
VM::VM(bool enable_os) : enable_os(enable_os) {
|
||||||
|
pkpy_g.vm = (pkpy_VM*)this; // setup the current VM
|
||||||
Pools_initialize();
|
Pools_initialize();
|
||||||
pkpy_StrName__initialize();
|
pkpy_StrName__initialize();
|
||||||
pkpy_g.vm = (pkpy_VM*)this; // setup the current VM
|
pk_ManagedHeap__ctor(&heap, (pkpy_VM*)this);
|
||||||
|
|
||||||
static ::PyObject __true_obj = {tp_bool, false, false, NULL};
|
static ::PyObject __true_obj = {tp_bool, false, false, NULL};
|
||||||
static ::PyObject __false_obj = {tp_bool, false, false, NULL};
|
static ::PyObject __false_obj = {tp_bool, false, false, NULL};
|
||||||
@ -226,7 +228,7 @@ PyVar VM::exec(std::string_view source) { return exec(source, "main.py", EXEC_MO
|
|||||||
PyVar VM::eval(std::string_view source) { return exec(source, "<eval>", EVAL_MODE); }
|
PyVar VM::eval(std::string_view source) { return exec(source, "<eval>", EVAL_MODE); }
|
||||||
|
|
||||||
PyObject* VM::new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled, PyTypeInfo::Vt vt) {
|
PyObject* VM::new_type_object(PyObject* mod, StrName name, Type base, bool subclass_enabled, PyTypeInfo::Vt vt) {
|
||||||
PyObject* obj = heap._new<Type>(tp_type, Type(_all_types.size()));
|
PyObject* obj = new_object_no_gc<Type>(tp_type, Type(_all_types.size())).get();
|
||||||
const PyTypeInfo& base_info = _all_types[base];
|
const PyTypeInfo& base_info = _all_types[base];
|
||||||
if(!base_info.subclass_enabled) {
|
if(!base_info.subclass_enabled) {
|
||||||
Str error = _S("type ", base_info.name.escape(), " is not `subclass_enabled`");
|
Str error = _S("type ", base_info.name.escape(), " is not `subclass_enabled`");
|
||||||
@ -302,7 +304,7 @@ bool VM::py_callable(PyVar obj) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PyVar VM::__minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key) {
|
PyVar VM::__minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key) {
|
||||||
auto _lock = heap.gc_scope_lock();
|
auto _lock = gc_scope_lock();
|
||||||
const Tuple& args_tuple = PK_OBJ_GET(Tuple, args); // from *args, it must be a tuple
|
const Tuple& args_tuple = PK_OBJ_GET(Tuple, args); // from *args, it must be a tuple
|
||||||
if(is_none(key) && args_tuple.size() == 2) {
|
if(is_none(key) && args_tuple.size() == 2) {
|
||||||
// fast path
|
// fast path
|
||||||
@ -328,7 +330,7 @@ PyVar VM::__minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key) {
|
|||||||
if((this->*op)(view[i], res)) res = view[i];
|
if((this->*op)(view[i], res)) res = view[i];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto _lock = heap.gc_scope_lock();
|
auto _lock = gc_scope_lock();
|
||||||
for(int i = 1; i < view.size(); i++) {
|
for(int i = 1; i < view.size(); i++) {
|
||||||
PyVar a = call(key, view[i]);
|
PyVar a = call(key, view[i]);
|
||||||
PyVar b = call(key, res);
|
PyVar b = call(key, res);
|
||||||
@ -418,11 +420,8 @@ PyObject* VM::py_import(Str path, bool throw_err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VM::~VM() {
|
VM::~VM() {
|
||||||
// clear managed heap
|
// destroy all objects
|
||||||
for(PyObject* obj: heap.gen)
|
pk_ManagedHeap__dtor(&heap);
|
||||||
heap._delete(obj);
|
|
||||||
for(PyObject* obj: heap._no_gc)
|
|
||||||
heap._delete(obj);
|
|
||||||
// clear everything
|
// clear everything
|
||||||
callstack.clear();
|
callstack.clear();
|
||||||
s_data.clear();
|
s_data.clear();
|
||||||
@ -472,7 +471,7 @@ void VM::__stack_gc_mark(PyVar* begin, PyVar* end) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List VM::py_list(PyVar it) {
|
List VM::py_list(PyVar it) {
|
||||||
auto _lock = heap.gc_scope_lock();
|
auto _lock = gc_scope_lock();
|
||||||
it = py_iter(it);
|
it = py_iter(it);
|
||||||
List list;
|
List list;
|
||||||
const PyTypeInfo* info = _tp_info(it);
|
const PyTypeInfo* info = _tp_info(it);
|
||||||
@ -566,7 +565,7 @@ PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar local
|
|||||||
return vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
|
return vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto _lock = heap.gc_scope_lock(); // for safety
|
auto _lock = gc_scope_lock(); // for safety
|
||||||
|
|
||||||
PyObject* globals_obj = nullptr;
|
PyObject* globals_obj = nullptr;
|
||||||
Dict* globals_dict = nullptr;
|
Dict* globals_dict = nullptr;
|
||||||
@ -583,7 +582,6 @@ PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar local
|
|||||||
check_compatible_type(globals, VM::tp_dict);
|
check_compatible_type(globals, VM::tp_dict);
|
||||||
// make a temporary object and copy globals into it
|
// make a temporary object and copy globals into it
|
||||||
globals_obj = new_object<DummyInstance>(VM::tp_object).get();
|
globals_obj = new_object<DummyInstance>(VM::tp_object).get();
|
||||||
globals_obj->_attr = new NameDict();
|
|
||||||
globals_dict = &PK_OBJ_GET(Dict, globals);
|
globals_dict = &PK_OBJ_GET(Dict, globals);
|
||||||
globals_dict->apply([&](PyVar k, PyVar v) {
|
globals_dict->apply([&](PyVar k, PyVar v) {
|
||||||
globals_obj->attr().set(CAST(Str&, k), v);
|
globals_obj->attr().set(CAST(Str&, k), v);
|
||||||
@ -603,7 +601,7 @@ PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar local
|
|||||||
locals_closure->set(CAST(Str&, k), v);
|
locals_closure->set(CAST(Str&, k), v);
|
||||||
});
|
});
|
||||||
PyObject* _callable =
|
PyObject* _callable =
|
||||||
heap.gcnew<Function>(tp_function, __dynamic_func_decl, globals_obj, nullptr, locals_closure);
|
new_object<Function>(tp_function, __dynamic_func_decl, globals_obj, nullptr, locals_closure).get();
|
||||||
retval = vm->_exec(code.get(), globals_obj, _callable, vm->s_data._sp);
|
retval = vm->_exec(code.get(), globals_obj, _callable, vm->s_data._sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,7 +721,7 @@ PyVar VM::__format_object(PyVar obj, Str spec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PyObject* VM::new_module(Str name, Str package) {
|
PyObject* VM::new_module(Str name, Str package) {
|
||||||
PyObject* obj = heap._new<DummyModule>(tp_module);
|
PyObject* obj = new_object_no_gc<DummyModule>(tp_module).get();
|
||||||
obj->attr().set(__name__, VAR(name));
|
obj->attr().set(__name__, VAR(name));
|
||||||
obj->attr().set(__package__, VAR(package));
|
obj->attr().set(__package__, VAR(package));
|
||||||
// convert to fullname
|
// convert to fullname
|
||||||
@ -873,8 +871,8 @@ void VM::__log_s_data(const char* title) {
|
|||||||
|
|
||||||
void VM::__init_builtin_types() {
|
void VM::__init_builtin_types() {
|
||||||
_all_types.emplace_back(nullptr, Type(), nullptr, "", false); // 0 is not used
|
_all_types.emplace_back(nullptr, Type(), nullptr, "", false); // 0 is not used
|
||||||
_all_types.emplace_back(heap._new<Type>(tp_type, tp_object), Type(), nullptr, "object", true);
|
_all_types.emplace_back(new_object_no_gc<Type>(tp_type, tp_object).get(), Type(), nullptr, "object", true);
|
||||||
_all_types.emplace_back(heap._new<Type>(tp_type, tp_type), tp_object, nullptr, "type", false);
|
_all_types.emplace_back(new_object_no_gc<Type>(tp_type, tp_type).get(), tp_object, nullptr, "type", false);
|
||||||
|
|
||||||
auto validate = [](Type type, PyObject* ret) {
|
auto validate = [](Type type, PyObject* ret) {
|
||||||
Type ret_t = ret->as<Type>();
|
Type ret_t = ret->as<Type>();
|
||||||
@ -946,7 +944,7 @@ void VM::__init_builtin_types() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VM::__unpack_as_list(ArgsView args, List& list) {
|
void VM::__unpack_as_list(ArgsView args, List& list) {
|
||||||
auto _lock = heap.gc_scope_lock();
|
auto _lock = gc_scope_lock();
|
||||||
for(PyVar obj: args) {
|
for(PyVar obj: args) {
|
||||||
if(is_type(obj, tp_star_wrapper)) {
|
if(is_type(obj, tp_star_wrapper)) {
|
||||||
const StarWrapper& w = _CAST(StarWrapper&, obj);
|
const StarWrapper& w = _CAST(StarWrapper&, obj);
|
||||||
@ -966,7 +964,7 @@ void VM::__unpack_as_list(ArgsView args, List& list) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VM::__unpack_as_dict(ArgsView args, Dict& dict) {
|
void VM::__unpack_as_dict(ArgsView args, Dict& dict) {
|
||||||
auto _lock = heap.gc_scope_lock();
|
auto _lock = gc_scope_lock();
|
||||||
for(PyVar obj: args) {
|
for(PyVar obj: args) {
|
||||||
if(is_type(obj, tp_star_wrapper)) {
|
if(is_type(obj, tp_star_wrapper)) {
|
||||||
const StarWrapper& w = _CAST(StarWrapper&, obj);
|
const StarWrapper& w = _CAST(StarWrapper&, obj);
|
||||||
@ -1360,11 +1358,11 @@ void VM::setattr(PyVar obj, StrName name, PyVar value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PyObject* VM::bind_func(PyObject* obj, StrName name, int argc, NativeFuncC fn, any userdata, BindType bt) {
|
PyObject* VM::bind_func(PyObject* obj, StrName name, int argc, NativeFuncC fn, any userdata, BindType bt) {
|
||||||
PyObject* nf = heap.gcnew<NativeFunc>(tp_native_func, fn, argc, std::move(userdata));
|
PyObject* nf = new_object<NativeFunc>(tp_native_func, fn, argc, std::move(userdata)).get();
|
||||||
switch(bt) {
|
switch(bt) {
|
||||||
case BindType::DEFAULT: break;
|
case BindType::DEFAULT: break;
|
||||||
case BindType::STATICMETHOD: nf = heap.gcnew<StaticMethod>(tp_staticmethod, nf); break;
|
case BindType::STATICMETHOD: nf = new_object<StaticMethod>(tp_staticmethod, nf).get(); break;
|
||||||
case BindType::CLASSMETHOD: nf = heap.gcnew<ClassMethod>(tp_classmethod, nf); break;
|
case BindType::CLASSMETHOD: nf = new_object<ClassMethod>(tp_classmethod, nf).get(); break;
|
||||||
}
|
}
|
||||||
if(obj != nullptr) obj->attr().set(name, nf);
|
if(obj != nullptr) obj->attr().set(name, nf);
|
||||||
return nf;
|
return nf;
|
||||||
@ -1384,11 +1382,11 @@ PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Native
|
|||||||
|
|
||||||
FuncDecl_ decl = co->func_decls[0];
|
FuncDecl_ decl = co->func_decls[0];
|
||||||
decl->docstring = docstring;
|
decl->docstring = docstring;
|
||||||
PyObject* f_obj = heap.gcnew<NativeFunc>(tp_native_func, fn, decl, std::move(userdata));
|
PyObject* f_obj = new_object<NativeFunc>(tp_native_func, fn, decl, std::move(userdata)).get();
|
||||||
|
|
||||||
switch(bt) {
|
switch(bt) {
|
||||||
case BindType::STATICMETHOD: f_obj = heap.gcnew<StaticMethod>(tp_staticmethod, f_obj); break;
|
case BindType::STATICMETHOD: f_obj = new_object<StaticMethod>(tp_staticmethod, f_obj).get(); break;
|
||||||
case BindType::CLASSMETHOD: f_obj = heap.gcnew<ClassMethod>(tp_classmethod, f_obj); break;
|
case BindType::CLASSMETHOD: f_obj = new_object<ClassMethod>(tp_classmethod, f_obj).get(); break;
|
||||||
case BindType::DEFAULT: break;
|
case BindType::DEFAULT: break;
|
||||||
}
|
}
|
||||||
if(obj != nullptr) obj->attr().set(decl->code->name, f_obj);
|
if(obj != nullptr) obj->attr().set(decl->code->name, f_obj);
|
||||||
@ -1403,7 +1401,7 @@ PyObject* VM::bind_property(PyObject* obj, const char* name, NativeFuncC fget, N
|
|||||||
PyVar _0 = new_object<NativeFunc>(tp_native_func, fget, 1);
|
PyVar _0 = new_object<NativeFunc>(tp_native_func, fget, 1);
|
||||||
PyVar _1 = vm->None;
|
PyVar _1 = vm->None;
|
||||||
if(fset != nullptr) _1 = new_object<NativeFunc>(tp_native_func, fset, 2);
|
if(fset != nullptr) _1 = new_object<NativeFunc>(tp_native_func, fset, 2);
|
||||||
PyObject* prop = heap.gcnew<Property>(tp_property, _0, _1);
|
PyObject* prop = new_object<Property>(tp_property, _0, _1).get();
|
||||||
obj->attr().set(StrName(name_sv), prop);
|
obj->attr().set(StrName(name_sv), prop);
|
||||||
return prop;
|
return prop;
|
||||||
}
|
}
|
||||||
@ -1877,27 +1875,35 @@ void Frame::_gc_mark(VM* vm) const {
|
|||||||
vm->obj_gc_mark(_callable);
|
vm->obj_gc_mark(_callable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ManagedHeap::mark() {
|
extern "C"{
|
||||||
for(PyObject* obj: _no_gc)
|
void pk_ManagedHeap__mark(pk_ManagedHeap* self){
|
||||||
vm->__obj_gc_mark(obj);
|
VM* vm = (VM*)self->vm;
|
||||||
vm->callstack.apply([this](Frame& frame) {
|
for(int i=0; i<self->no_gc.count; i++){
|
||||||
frame._gc_mark(vm);
|
PyObject* obj = c11__getitem(PyObject*, &self->no_gc, i);
|
||||||
});
|
vm->__obj_gc_mark(obj);
|
||||||
vm->obj_gc_mark(vm->__last_exception);
|
}
|
||||||
vm->obj_gc_mark(vm->__curr_class);
|
vm->callstack.apply([vm](Frame& frame) {
|
||||||
vm->obj_gc_mark(vm->__c.error);
|
frame._gc_mark(vm);
|
||||||
vm->__stack_gc_mark(vm->s_data.begin(), vm->s_data.end());
|
});
|
||||||
if(_gc_marker_ex) _gc_marker_ex(vm);
|
vm->obj_gc_mark(vm->__last_exception);
|
||||||
}
|
vm->obj_gc_mark(vm->__curr_class);
|
||||||
|
vm->obj_gc_mark(vm->__c.error);
|
||||||
|
vm->__stack_gc_mark(vm->s_data.begin(), vm->s_data.end());
|
||||||
|
if(self->_gc_marker_ex) self->_gc_marker_ex((pkpy_VM*)vm);
|
||||||
|
}
|
||||||
|
|
||||||
void ManagedHeap::_delete(PyObject* obj) {
|
void pk_ManagedHeap__delete_obj(pk_ManagedHeap* self, ::PyObject* __obj){
|
||||||
const PyTypeInfo* ti = vm->_tp_info(obj->type);
|
PyObject* obj = (PyObject*)__obj;
|
||||||
if(ti->vt._dtor) ti->vt._dtor(obj->_value_ptr());
|
const PyTypeInfo* ti = ((VM*)(self->vm))->_tp_info(obj->type);
|
||||||
delete obj->_attr; // delete __dict__ if exists
|
if(ti->vt._dtor) ti->vt._dtor(obj->_value_ptr());
|
||||||
if(obj->gc_is_large){
|
if (obj->_attr)
|
||||||
std::free(obj);
|
c11_vector__dtor(obj->_attr);
|
||||||
}else{
|
delete obj->_attr; // delete __dict__ if exists
|
||||||
PoolObject_dealloc(obj);
|
if(obj->gc_is_large){
|
||||||
|
std::free(obj);
|
||||||
|
}else{
|
||||||
|
PoolObject_dealloc(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ void add_module_io(VM* vm) {
|
|||||||
|
|
||||||
void add_module_os(VM* vm) {
|
void add_module_os(VM* vm) {
|
||||||
PyObject* mod = vm->new_module("os");
|
PyObject* mod = vm->new_module("os");
|
||||||
PyObject* path_obj = vm->heap.gcnew<DummyInstance>(VM::tp_object);
|
PyObject* path_obj = vm->new_object<DummyInstance>(VM::tp_object).get();
|
||||||
mod->attr().set("path", path_obj);
|
mod->attr().set("path", path_obj);
|
||||||
|
|
||||||
// Working directory is shared by all VMs!!
|
// Working directory is shared by all VMs!!
|
||||||
|
@ -78,8 +78,8 @@ void add_module_sys(VM* vm) {
|
|||||||
vm->setattr(mod, "version", VAR(PK_VERSION));
|
vm->setattr(mod, "version", VAR(PK_VERSION));
|
||||||
vm->setattr(mod, "platform", VAR(kPlatformStrings[PK_SYS_PLATFORM]));
|
vm->setattr(mod, "platform", VAR(kPlatformStrings[PK_SYS_PLATFORM]));
|
||||||
|
|
||||||
PyObject* stdout_ = vm->heap.gcnew<DummyInstance>(vm->tp_object);
|
PyObject* stdout_ = vm->new_object<DummyInstance>(vm->tp_object).get();
|
||||||
PyObject* stderr_ = vm->heap.gcnew<DummyInstance>(vm->tp_object);
|
PyObject* stderr_ = vm->new_object<DummyInstance>(vm->tp_object).get();
|
||||||
vm->setattr(mod, "stdout", stdout_);
|
vm->setattr(mod, "stdout", stdout_);
|
||||||
vm->setattr(mod, "stderr", stderr_);
|
vm->setattr(mod, "stderr", stderr_);
|
||||||
|
|
||||||
@ -240,7 +240,7 @@ void add_module_dis(VM* vm) {
|
|||||||
|
|
||||||
void add_module_gc(VM* vm) {
|
void add_module_gc(VM* vm) {
|
||||||
PyObject* mod = vm->new_module("gc");
|
PyObject* mod = vm->new_module("gc");
|
||||||
vm->bind_func(mod, "collect", 0, PK_LAMBDA(VAR(vm->heap.collect())));
|
vm->bind_func(mod, "collect", 0, PK_LAMBDA(VAR(pk_ManagedHeap__collect(&vm->heap))));
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_module_enum(VM* vm) {
|
void add_module_enum(VM* vm) {
|
||||||
|
@ -58,8 +58,9 @@ pkpy_Dict pkpy_Dict__copy(const pkpy_Dict* self) {
|
|||||||
|
|
||||||
static int pkpy_Dict__htget(const pkpy_Dict* self, int h) {
|
static int pkpy_Dict__htget(const pkpy_Dict* self, int h) {
|
||||||
#if PK_DICT_COMPACT_MODE
|
#if PK_DICT_COMPACT_MODE
|
||||||
const int *p = (const int*)(((const char*)self->_hashtable) + h * pkpy_Dict__idx_size(self));
|
const int loc = pkpy_Dict__idx_size(self) * h;
|
||||||
return (*p) & pkpy_Dict__idx_null(self);
|
const int *p = (const int*)(((const char*)self->_hashtable) + (loc & (~3)));
|
||||||
|
return (*p >> ((loc & 3) * 8)) & pkpy_Dict__idx_null(self);
|
||||||
#else
|
#else
|
||||||
return ((const int*)self->_hashtable)[h];
|
return ((const int*)self->_hashtable)[h];
|
||||||
#endif
|
#endif
|
||||||
@ -67,8 +68,10 @@ static int pkpy_Dict__htget(const pkpy_Dict* self, int h) {
|
|||||||
|
|
||||||
static void pkpy_Dict__htset(pkpy_Dict* self, int h, int v) {
|
static void pkpy_Dict__htset(pkpy_Dict* self, int h, int v) {
|
||||||
#if PK_DICT_COMPACT_MODE
|
#if PK_DICT_COMPACT_MODE
|
||||||
int *p = (int*)(((char*)self->_hashtable) + h * pkpy_Dict__idx_size(self));
|
const int loc = pkpy_Dict__idx_size(self) * h;
|
||||||
*p = v | (*p & ~pkpy_Dict__idx_null(self));
|
int *p = (int*)(((char*)self->_hashtable) + (loc & (~3)));
|
||||||
|
const int shift = (loc & 3) * 8;
|
||||||
|
*p = (v << shift) | (*p & ~(pkpy_Dict__idx_null(self) << shift));
|
||||||
#else
|
#else
|
||||||
((int*)self->_hashtable)[h] = v;
|
((int*)self->_hashtable)[h] = v;
|
||||||
#endif
|
#endif
|
||||||
|
@ -94,7 +94,7 @@ void __init_builtins(VM* _vm) {
|
|||||||
return vm->None;
|
return vm->None;
|
||||||
});
|
});
|
||||||
|
|
||||||
_vm->bind_func(_vm->builtins, "super", -1, [](VM* vm, ArgsView args) {
|
_vm->bind_func(_vm->builtins, "super", -1, [](VM* vm, ArgsView args)->PyVar {
|
||||||
PyObject* class_arg = nullptr;
|
PyObject* class_arg = nullptr;
|
||||||
PyVar self_arg = nullptr;
|
PyVar self_arg = nullptr;
|
||||||
if(args.size() == 2) {
|
if(args.size() == 2) {
|
||||||
@ -122,13 +122,13 @@ void __init_builtins(VM* _vm) {
|
|||||||
return vm->new_object<Super>(vm->tp_super, self_arg, vm->_all_types[type].base);
|
return vm->new_object<Super>(vm->tp_super, self_arg, vm->_all_types[type].base);
|
||||||
});
|
});
|
||||||
|
|
||||||
_vm->bind_func(_vm->builtins, "staticmethod", 1, [](VM* vm, ArgsView args) {
|
_vm->bind_func(_vm->builtins, "staticmethod", 1, [](VM* vm, ArgsView args)->PyVar {
|
||||||
PyVar func = args[0];
|
PyVar func = args[0];
|
||||||
vm->check_type(func, vm->tp_function);
|
vm->check_type(func, vm->tp_function);
|
||||||
return vm->new_object<StaticMethod>(vm->tp_staticmethod, args[0]);
|
return vm->new_object<StaticMethod>(vm->tp_staticmethod, args[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
_vm->bind_func(_vm->builtins, "classmethod", 1, [](VM* vm, ArgsView args) {
|
_vm->bind_func(_vm->builtins, "classmethod", 1, [](VM* vm, ArgsView args)->PyVar {
|
||||||
PyVar func = args[0];
|
PyVar func = args[0];
|
||||||
vm->check_type(func, vm->tp_function);
|
vm->check_type(func, vm->tp_function);
|
||||||
return vm->new_object<ClassMethod>(vm->tp_classmethod, args[0]);
|
return vm->new_object<ClassMethod>(vm->tp_classmethod, args[0]);
|
||||||
@ -380,7 +380,7 @@ void __init_builtins(VM* _vm) {
|
|||||||
return _0._obj == _1._obj ? vm->True : vm->False;
|
return _0._obj == _1._obj ? vm->True : vm->False;
|
||||||
});
|
});
|
||||||
|
|
||||||
_vm->__cached_object_new = _vm->bind_func(VM::tp_object, __new__, 1, [](VM* vm, ArgsView args) {
|
_vm->__cached_object_new = _vm->bind_func(VM::tp_object, __new__, 1, [](VM* vm, ArgsView args) ->PyVar {
|
||||||
vm->check_type(args[0], vm->tp_type);
|
vm->check_type(args[0], vm->tp_type);
|
||||||
Type t = PK_OBJ_GET(Type, args[0]);
|
Type t = PK_OBJ_GET(Type, args[0]);
|
||||||
return vm->new_object<DummyInstance>(t);
|
return vm->new_object<DummyInstance>(t);
|
||||||
@ -734,7 +734,7 @@ void __init_builtins(VM* _vm) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
_vm->bind_func(VM::tp_str, "join", 2, [](VM* vm, ArgsView args) {
|
_vm->bind_func(VM::tp_str, "join", 2, [](VM* vm, ArgsView args) {
|
||||||
auto _lock = vm->heap.gc_scope_lock();
|
auto _lock = vm->gc_scope_lock();
|
||||||
const Str& self = _CAST(Str&, args[0]);
|
const Str& self = _CAST(Str&, args[0]);
|
||||||
SStream ss;
|
SStream ss;
|
||||||
PyVar it = vm->py_iter(args[1]); // strong ref
|
PyVar it = vm->py_iter(args[1]); // strong ref
|
||||||
@ -964,7 +964,7 @@ void __init_builtins(VM* _vm) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
_vm->bind_func(VM::tp_list, "extend", 2, [](VM* vm, ArgsView args) {
|
_vm->bind_func(VM::tp_list, "extend", 2, [](VM* vm, ArgsView args) {
|
||||||
auto _lock = vm->heap.gc_scope_lock();
|
auto _lock = vm->gc_scope_lock();
|
||||||
List& self = _CAST(List&, args[0]);
|
List& self = _CAST(List&, args[0]);
|
||||||
PyVar it = vm->py_iter(args[1]); // strong ref
|
PyVar it = vm->py_iter(args[1]); // strong ref
|
||||||
const PyTypeInfo* info = vm->_tp_info(args[1]);
|
const PyTypeInfo* info = vm->_tp_info(args[1]);
|
||||||
@ -1342,7 +1342,7 @@ void __init_builtins(VM* _vm) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// tp_dict
|
// tp_dict
|
||||||
_vm->bind_func(VM::tp_dict, __new__, -1, [](VM* vm, ArgsView args) {
|
_vm->bind_func(VM::tp_dict, __new__, -1, [](VM* vm, ArgsView args)->PyVar {
|
||||||
Type cls_t = PK_OBJ_GET(Type, args[0]);
|
Type cls_t = PK_OBJ_GET(Type, args[0]);
|
||||||
return vm->new_object<Dict>(cls_t);
|
return vm->new_object<Dict>(cls_t);
|
||||||
});
|
});
|
||||||
@ -1350,7 +1350,7 @@ void __init_builtins(VM* _vm) {
|
|||||||
_vm->bind_func(VM::tp_dict, __init__, -1, [](VM* vm, ArgsView args) {
|
_vm->bind_func(VM::tp_dict, __init__, -1, [](VM* vm, ArgsView args) {
|
||||||
if(args.size() == 1 + 0) return vm->None;
|
if(args.size() == 1 + 0) return vm->None;
|
||||||
if(args.size() == 1 + 1) {
|
if(args.size() == 1 + 1) {
|
||||||
auto _lock = vm->heap.gc_scope_lock();
|
auto _lock = vm->gc_scope_lock();
|
||||||
Dict& self = PK_OBJ_GET(Dict, args[0]);
|
Dict& self = PK_OBJ_GET(Dict, args[0]);
|
||||||
if(is_type(args[1], vm->tp_dict)) {
|
if(is_type(args[1], vm->tp_dict)) {
|
||||||
Dict& other = CAST(Dict&, args[1]);
|
Dict& other = CAST(Dict&, args[1]);
|
||||||
@ -1539,7 +1539,7 @@ void __init_builtins(VM* _vm) {
|
|||||||
_vm->bind_func(VM::tp_exception, __new__, -1, [](VM* vm, ArgsView args) -> PyVar {
|
_vm->bind_func(VM::tp_exception, __new__, -1, [](VM* vm, ArgsView args) -> PyVar {
|
||||||
Type cls = PK_OBJ_GET(Type, args[0]);
|
Type cls = PK_OBJ_GET(Type, args[0]);
|
||||||
StrName cls_name = _type_name(vm, cls);
|
StrName cls_name = _type_name(vm, cls);
|
||||||
PyObject* e_obj = vm->heap.gcnew<Exception>(cls, cls_name.index);
|
PyObject* e_obj = vm->new_object<Exception>(cls, cls_name.index).get();
|
||||||
e_obj->_attr = new NameDict();
|
e_obj->_attr = new NameDict();
|
||||||
e_obj->as<Exception>().self = e_obj;
|
e_obj->as<Exception>().self = e_obj;
|
||||||
return e_obj;
|
return e_obj;
|
||||||
|
@ -428,7 +428,7 @@ bool pkpy_unpack_sequence(pkpy_vm* vm_handle, int n) {
|
|||||||
VM* vm = (VM*)vm_handle;
|
VM* vm = (VM*)vm_handle;
|
||||||
PK_ASSERT_NO_ERROR()
|
PK_ASSERT_NO_ERROR()
|
||||||
PK_ASSERT_N_EXTRA_ELEMENTS(1)
|
PK_ASSERT_N_EXTRA_ELEMENTS(1)
|
||||||
auto _lock = vm->heap.gc_scope_lock();
|
auto _lock = vm->gc_scope_lock();
|
||||||
PK_PROTECTED(
|
PK_PROTECTED(
|
||||||
PyVar _0 = vm->py_iter(vm->s_data.popx());
|
PyVar _0 = vm->py_iter(vm->s_data.popx());
|
||||||
for(int i=0; i<n; i++){
|
for(int i=0; i<n; i++){
|
||||||
|
36
src/public.c
Normal file
36
src/public.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include "pocketpy/objects/public.h"
|
||||||
|
#include "pocketpy/objects/object.h"
|
||||||
|
#include "pocketpy/interpreter/vm.h"
|
||||||
|
|
||||||
|
void py_initialize(){
|
||||||
|
// initialize the global VM
|
||||||
|
}
|
||||||
|
|
||||||
|
void py_newint(PyVar* self, int64_t val){
|
||||||
|
self->type = tp_int;
|
||||||
|
self->is_ptr = false;
|
||||||
|
self->_i64 = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void py_newfloat(PyVar* self, double val){
|
||||||
|
self->type = tp_float;
|
||||||
|
self->is_ptr = false;
|
||||||
|
self->_f64 = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void py_newbool(PyVar* self, bool val){
|
||||||
|
// return a global singleton
|
||||||
|
}
|
||||||
|
|
||||||
|
void py_newnone(PyVar* self){
|
||||||
|
// return a heap object
|
||||||
|
}
|
||||||
|
|
||||||
|
void py_newstr(PyVar* self, const char* val){
|
||||||
|
// return a heap object
|
||||||
|
}
|
||||||
|
|
||||||
|
void py_newstr2(PyVar*, const char*, int);
|
||||||
|
|
||||||
|
void py_newbytes(PyVar*, const uint8_t*, int);
|
||||||
|
|
22
tests/CMakeLists.txt
Normal file
22
tests/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# @szdytom favored testing, set BUILD_TESTING to enable it
|
||||||
|
# You can use scripts/run_tests.py as an alternative
|
||||||
|
# Note: the CI uses scripts/run_tests.py to run the tests
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
function(pkpy_add_test pyfile)
|
||||||
|
get_filename_component(test_name ${pyfile} NAME_WE)
|
||||||
|
add_test(
|
||||||
|
NAME ${test_name}
|
||||||
|
COMMAND $<TARGET_FILE:main> ${pyfile}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/..
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
message("Testing enabled")
|
||||||
|
|
||||||
|
file(GLOB PK_PYTHON_TESTCASES_FILES RELATIVE ${CMAKE_CURRENT_LIST_DIR}/.. "*.py")
|
||||||
|
|
||||||
|
foreach(pyfile ${PK_PYTHON_TESTCASES_FILES})
|
||||||
|
pkpy_add_test(${pyfile})
|
||||||
|
endforeach()
|
Loading…
x
Reference in New Issue
Block a user