Merge branch 'pocketpy:main' into gsoc-2025-debugger

This commit is contained in:
lightovernight 2025-08-07 22:32:48 +08:00 committed by GitHub
commit 7251257e44
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
94 changed files with 4040 additions and 1513 deletions

View File

@ -36,7 +36,7 @@ jobs:
shell: bash
run: |
mkdir -p output/x86_64
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON -DPK_BUILD_MODULE_CUTE_PNG=ON
cp main.exe output/x86_64
cp pocketpy.dll output/x86_64
- uses: actions/upload-artifact@v4
@ -76,7 +76,7 @@ jobs:
run: |
python scripts/check_pragma_once.py include
mkdir -p output/x86_64
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON -DPK_BUILD_MODULE_CUTE_PNG=ON
python scripts/run_tests.py
cp main output/x86_64
cp libpocketpy.so output/x86_64
@ -96,7 +96,7 @@ jobs:
submodules: recursive
- name: Compile and Test
run: |
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON
python cmake_build.py Release -DPK_BUILD_MODULE_LZ4=ON -DPK_BUILD_MODULE_CUTE_PNG=ON
python scripts/run_tests.py
- name: Benchmark
run: python scripts/run_tests.py benchmark
@ -162,7 +162,7 @@ jobs:
uses: jirutka/setup-alpine@v1
with:
arch: ${{ matrix.arch }}
packages: gcc g++ make cmake libc-dev linux-headers python3
packages: gcc g++ make cmake libc-dev linux-headers python3 git
- name: Build and Test
run: |
echo "Building for architecture: ${{ matrix.arch }}"

1
.gitignore vendored
View File

@ -40,3 +40,4 @@ docs/C-API/functions.md
cmake-build-*
tmp/

View File

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.10)
project(cute_png)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_library(${PROJECT_NAME} STATIC src/cute_png.c)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/include
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
#include "pocketpy.h"
#define CUTE_PNG_IMPLEMENTATION
#include "cute_png.h"
static bool cute_png_loads(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
PY_CHECK_ARG_TYPE(0, tp_bytes);
int size;
unsigned char* data = py_tobytes(argv, &size);
cp_image_t image = cp_load_png_mem(data, size);
if(image.pix == NULL) return ValueError("cute_png: %s", cp_error_reason);
py_newarray2d(py_retval(), image.w, image.h);
for(int y = 0; y < image.h; y++) {
for(int x = 0; x < image.w; x++) {
cp_pixel_t pixel = image.pix[y * image.w + x];
py_ObjectRef slot = py_array2d_getitem(py_retval(), x, y);
c11_color32 color;
color.r = pixel.r;
color.g = pixel.g;
color.b = pixel.b;
color.a = pixel.a;
py_newcolor32(slot, color);
}
}
return true;
}
static bool cute_png_dumps(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
PY_CHECK_ARG_TYPE(0, tp_array2d);
int width = py_array2d_getwidth(argv);
int height = py_array2d_getheight(argv);
cp_image_t image = cp_load_blank(width, height);
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
py_ObjectRef slot = py_array2d_getitem(argv, x, y);
if(!py_checktype(slot, tp_color32)) return false;
c11_color32 color = py_tocolor32(slot);
cp_pixel_t pixel = cp_make_pixel_a(color.r, color.g, color.b, color.a);
image.pix[y * width + x] = pixel;
}
}
cp_saved_png_t saved_image = cp_save_png_to_memory(&image);
assert(saved_image.data != NULL);
unsigned char* data = py_newbytes(py_retval(), saved_image.size);
memcpy(data, saved_image.data, saved_image.size);
return true;
}
void pk__add_module_cute_png() {
py_GlobalRef mod = py_newmodule("cute_png");
py_bindfunc(mod, "loads", cute_png_loads);
py_bindfunc(mod, "dumps", cute_png_dumps);
}

View File

@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.10)
project(libhv_bindings)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 14)

View File

@ -5,12 +5,12 @@ project(lz4)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_library(lz4 STATIC lz4/lib/lz4.c)
add_library(${PROJECT_NAME} STATIC lz4/lib/lz4.c)
target_include_directories(lz4 PRIVATE
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/lz4/lib
)
target_include_directories(lz4 INTERFACE
target_include_directories(${PROJECT_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)

View File

@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.10)
project(pocketpy)
if(PK_BUILD_SHARED_LIB OR NOT PK_BUILD_STATIC_MAIN)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
@ -13,8 +17,7 @@ endif()
if(MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8 /jumptablerdata")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /experimental:c11atomics")
add_compile_options(/wd4267 /wd4244 /wd4146)
add_compile_options(/wd4267 /wd4244 /wd4146 /experimental:c11atomics)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox")
@ -47,19 +50,57 @@ file(GLOB_RECURSE POCKETPY_SRC ${CMAKE_CURRENT_LIST_DIR}/src/*.c)
if(PK_ENABLE_OS)
add_definitions(-DPK_ENABLE_OS=1)
else()
add_definitions(-DPK_ENABLE_OS=0)
endif()
if(PK_ENABLE_THREADS)
add_definitions(-DPK_ENABLE_THREADS=1)
else()
add_definitions(-DPK_ENABLE_THREADS=0)
endif()
if(PK_ENABLE_DETERMINISM)
add_subdirectory(3rd/dmath/dmath)
add_definitions(-DPK_ENABLE_DETERMINISM=1)
else()
add_definitions(-DPK_ENABLE_DETERMINISM=0)
endif()
if(PK_ENABLE_WATCHDOG)
add_definitions(-DPK_ENABLE_WATCHDOG=1)
else()
add_definitions(-DPK_ENABLE_WATCHDOG=0)
endif()
if(PK_ENABLE_CUSTOM_SNAME)
add_definitions(-DPK_ENABLE_CUSTOM_SNAME=1)
else()
add_definitions(-DPK_ENABLE_CUSTOM_SNAME=0)
endif()
if(PK_ENABLE_MIMALLOC)
message(">> Fetching mimalloc")
include(FetchContent)
FetchContent_Declare(
mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
GIT_TAG v3.1.5
)
set(MI_OVERRIDE OFF CACHE BOOL "" FORCE)
set(MI_NO_USE_CXX ON CACHE BOOL "" FORCE)
set(MI_BUILD_SHARED OFF CACHE BOOL "" FORCE)
set(MI_BUILD_OBJECT OFF CACHE BOOL "" FORCE)
set(MI_BUILD_STATIC ON CACHE BOOL "" FORCE)
set(MI_BUILD_TESTS OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(mimalloc)
include_directories(${mimalloc_SOURCE_DIR}/include)
add_definitions(-DPK_ENABLE_MIMALLOC=1)
else()
add_definitions(-DPK_ENABLE_MIMALLOC=0)
endif()
@ -73,6 +114,12 @@ if(PK_BUILD_MODULE_LIBHV)
add_definitions(-DPK_BUILD_MODULE_LIBHV)
endif()
if(PK_BUILD_MODULE_CUTE_PNG)
add_subdirectory(3rd/cute_png)
add_definitions(-DPK_BUILD_MODULE_CUTE_PNG)
endif()
if(PK_BUILD_SHARED_LIB)
message(">> Building shared library")
add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC})
@ -104,8 +151,10 @@ if(PK_ENABLE_DETERMINISM)
endif()
endif()
find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} Threads::Threads)
if(PK_ENABLE_THREADS)
find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} Threads::Threads)
endif()
if(UNIX AND NOT APPLE)
if(NOT PK_ENABLE_DETERMINISM)
@ -133,4 +182,13 @@ endif()
if(PK_BUILD_MODULE_LIBHV)
target_link_libraries(${PROJECT_NAME} libhv_bindings)
endif()
if(PK_BUILD_MODULE_CUTE_PNG)
target_link_libraries(${PROJECT_NAME} cute_png)
endif()
if(PK_ENABLE_MIMALLOC)
target_link_libraries(${PROJECT_NAME} mimalloc-static)
endif()

View File

@ -6,15 +6,17 @@ else()
endif()
# system features
option(PK_ENABLE_OS "" OFF)
option(PK_ENABLE_OS "" ON)
option(PK_ENABLE_THREADS "" ON)
option(PK_ENABLE_DETERMINISM "" OFF)
option(PK_ENABLE_WATCHDOG "" OFF)
option(PK_ENABLE_CUSTOM_SNAME "" OFF)
option(PK_ENABLE_MIMALLOC "" OFF)
# modules
option(PK_BUILD_MODULE_LZ4 "" OFF)
option(PK_BUILD_MODULE_LIBHV "" OFF)
option(PK_BUILD_MODULE_CUTE_PNG "" OFF)
# 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).

View File

@ -31,6 +31,7 @@ Developers are able to write Python bindings via C-API or pybind11 compatible in
Please see https://pocketpy.dev for details and try the following resources.
+ [Live Python Demo](https://pocketpy.dev/static/web/): Run Python code in your browser
+ [Live C Examples](https://pocketpy.github.io/examples/): Explore C-APIs in your browser
+ [Godot Extension](https://github.com/pocketpy/godot-pocketpy): Use pocketpy in Godot Engine
## Supported Platforms
@ -186,6 +187,7 @@ And these are the results of the primes benchmark on Intel i5-12400F, WSL (Ubunt
| | Description |
|-----------------------------------------------------------------|--------------------------------------------------------------------------|
| [godot-pocketpy](https://github.com/pocketpy/godot-pocketpy) | Godot extension for using pocketpy in Godot Engine. |
| [TIC-80](https://github.com/nesbox/TIC-80) | TIC-80 is a fantasy computer for making, playing and sharing tiny games. |
| [py-js](https://github.com/shakfu/py-js) | Python3 externals for Max / MSP. |
| [crescent](https://github.com/chukobyte/crescent) | Crescent is a cross-platform 2D fighting and beat-em-up game engine. |

View File

@ -16,6 +16,7 @@ cmake \
-DPK_BUILD_SHARED_LIB=ON \
-DCMAKE_BUILD_TYPE=Release \
-DPK_BUILD_MODULE_LZ4=ON \
-DPK_BUILD_MODULE_LIBHV=ON
-DPK_BUILD_MODULE_LIBHV=ON \
-DPK_BUILD_CUTE_PNG=ON
cmake --build . --config Release

View File

@ -20,7 +20,7 @@ assert config in ['Debug', 'Release', 'RelWithDebInfo']
os.chdir("build")
code = os.system(f"cmake .. -DPK_ENABLE_OS=ON -DPK_ENABLE_DETERMINISM=ON -DCMAKE_BUILD_TYPE={config} {extra_flags}")
code = os.system(f"cmake .. -DPK_ENABLE_MIMALLOC=ON -DPK_ENABLE_DETERMINISM=ON -DCMAKE_BUILD_TYPE={config} {extra_flags}")
assert code == 0
code = os.system(f"cmake --build . --config {config} -j 4")
assert code == 0

View File

@ -4,4 +4,5 @@
-std=c11
-Iinclude/
-I3rd/lz4/
-I3rd/libhv/include/
-I3rd/libhv/include/
-I3rd/cute_headers/include/

View File

@ -12,6 +12,8 @@ Developers are able to write Python bindings via C-API or pybind11 compatible in
+ [Live Python Demo](https://pocketpy.dev/static/web/): Run Python code in your browser
+ [Live C Examples](https://pocketpy.github.io/examples/): Explore C-APIs in your browser
+ [Godot Extension](https://github.com/pocketpy/godot-pocketpy): Use pocketpy in Godot Engine
+ [Flutter Plugin](https://pub.dev/packages/pocketpy): Use pocketpy in Flutter apps
## What it looks like

14
docs/modules/cute_png.md Normal file
View File

@ -0,0 +1,14 @@
---
icon: package
label: cute_png
---
!!!
This module is optional. Set option `PK_BUILD_MODULE_CUTE_PNG` to `ON` in your `CMakeLists.txt` to enable it.
!!!
Wraps [cute_png.h](https://github.com/RandyGaul/cute_headers/blob/master/cute_png.h) for PNG image loading and saving.
#### Source code
:::code source="../../include/typings/cute_png.pyi" :::

View File

@ -1,6 +1,7 @@
#pragma once
#include "pocketpy/common/vector.h"
typedef struct c11_chunkedvector_chunk {
int length;
int capacity;

View File

@ -1,7 +1,10 @@
#pragma once
#include "pocketpy/config.h"
#include <stdint.h>
#if PK_ENABLE_OS
typedef void* c11_socket_handler;
enum c11_address_family { C11_AF_INET = 2 };
@ -23,3 +26,5 @@ int c11_socket_close(c11_socket_handler socket);
int c11_socket_set_block(c11_socket_handler socket, int flag);
c11_socket_handler c11_socket_invalid_socket_handler();
int c11_socket_get_last_error();
#endif // PK_ENABLE_OS

View File

@ -67,6 +67,7 @@ c11_string* c11_sv__replace2(c11_sv self, c11_sv old, c11_sv new_);
c11_vector /* T=c11_sv */ c11_sv__split(c11_sv self, char sep);
c11_vector /* T=c11_sv */ c11_sv__split2(c11_sv self, c11_sv sep);
c11_vector /* T=c11_sv */ c11_sv__splitwhitespace(c11_sv self);
// misc
int c11__unicode_index_to_byte(const char* data, int i);

View File

@ -1,5 +1,9 @@
#pragma once
#include "pocketpy/config.h"
#if PK_ENABLE_THREADS
#include <stdatomic.h>
#include <stdbool.h>
@ -17,3 +21,5 @@ typedef int c11_thrd_retval_t;
bool c11_thrd_create(c11_thrd_t* thrd, c11_thrd_retval_t (*func)(void*), void* arg);
void c11_thrd_yield();
#endif

View File

@ -1,6 +1,8 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define PK_REGION(name) 1

View File

@ -1,7 +1,5 @@
#pragma once
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
@ -23,6 +21,7 @@ void* c11_vector__emplace(c11_vector* self);
bool c11_vector__contains(const c11_vector* self, void* elem);
void* c11_vector__submit(c11_vector* self, int* length);
void c11_vector__swap(c11_vector* self, c11_vector* other);
int c11_vector__nextcap(c11_vector* self);
#define c11__getitem(T, self, index) (((T*)(self)->data)[index])
#define c11__setitem(T, self, index, value) ((T*)(self)->data)[index] = value;
@ -30,7 +29,9 @@ void c11_vector__swap(c11_vector* self, c11_vector* other);
#define c11_vector__push(T, self, elem) \
do { \
if((self)->length == (self)->capacity) c11_vector__reserve((self), (self)->capacity * 2); \
if((self)->length == (self)->capacity) { \
c11_vector__reserve((self), c11_vector__nextcap((self))); \
} \
((T*)(self)->data)[(self)->length] = (elem); \
(self)->length++; \
} while(0)
@ -42,15 +43,19 @@ void c11_vector__swap(c11_vector* self, c11_vector* other);
#define c11_vector__extend(T, self, p, size) \
do { \
int min_capacity = (self)->length + (size); \
if((self)->capacity < min_capacity) \
c11_vector__reserve((self), c11__max((self)->capacity * 2, min_capacity)); \
if((self)->capacity < min_capacity) { \
int nextcap = c11_vector__nextcap(self); \
c11_vector__reserve((self), c11__max(nextcap, min_capacity)); \
} \
memcpy((T*)(self)->data + (self)->length, (p), (size) * sizeof(T)); \
(self)->length += (size); \
} while(0)
#define c11_vector__insert(T, self, index, elem) \
do { \
if((self)->length == (self)->capacity) c11_vector__reserve((self), (self)->capacity * 2); \
if((self)->length == (self)->capacity) { \
c11_vector__reserve((self), c11_vector__nextcap(self)); \
} \
T* p = (T*)(self)->data + (index); \
memmove(p + 1, p, ((self)->length - (index)) * sizeof(T)); \
*p = (elem); \

View File

@ -1,17 +1,20 @@
#pragma once
// clang-format off
#define PK_VERSION "2.1.0"
#define PK_VERSION "2.1.1"
#define PK_VERSION_MAJOR 2
#define PK_VERSION_MINOR 1
#define PK_VERSION_PATCH 0
#define PK_VERSION_PATCH 1
/*************** feature settings ***************/
// Whether to compile os-related modules or not
#ifndef PK_ENABLE_OS // can be overridden by cmake
#define PK_ENABLE_OS 1
#endif
#ifndef PK_ENABLE_THREADS // can be overridden by cmake
#define PK_ENABLE_THREADS 1
#endif
#ifndef PK_ENABLE_DETERMINISM // must be enabled from cmake
#define PK_ENABLE_DETERMINISM 0
#endif
@ -24,24 +27,15 @@
#define PK_ENABLE_CUSTOM_SNAME 0
#endif
#ifndef PK_ENABLE_MIMALLOC // can be overridden by cmake
#define PK_ENABLE_MIMALLOC 0
#endif
// GC min threshold
#ifndef PK_GC_MIN_THRESHOLD // can be overridden by cmake
#define PK_GC_MIN_THRESHOLD 32768
#endif
// Memory allocation functions
#ifndef PK_MALLOC
#ifndef __cplusplus
#define PK_MALLOC(size) malloc(size)
#define PK_REALLOC(ptr, size) realloc(ptr, size)
#define PK_FREE(ptr) free(ptr)
#else
#define PK_MALLOC(size) std::malloc(size)
#define PK_REALLOC(ptr, size) std::realloc(ptr, size)
#define PK_FREE(ptr) std::free(ptr)
#endif
#endif
// This is the maximum size of the value stack in py_TValue units
// The actual size in bytes equals `sizeof(py_TValue) * PK_VM_STACK_SIZE`
#ifndef PK_VM_STACK_SIZE // can be overridden by cmake
@ -81,3 +75,31 @@
#define restrict
#endif
#endif
#if PK_ENABLE_THREADS
#define PK_THREAD_LOCAL _Thread_local
#else
#define PK_THREAD_LOCAL
#endif
// Memory allocation functions
#ifndef PK_MALLOC
#if PK_ENABLE_MIMALLOC
#include "mimalloc.h"
#define PK_MALLOC(size) mi_malloc(size)
#define PK_REALLOC(ptr, size) mi_realloc(ptr, size)
#define PK_FREE(ptr) mi_free(ptr)
#else
#ifndef __cplusplus
#include <stdlib.h>
#define PK_MALLOC(size) malloc(size)
#define PK_REALLOC(ptr, size) realloc(ptr, size)
#define PK_FREE(ptr) free(ptr)
#else
#include <cstdlib>
#define PK_MALLOC(size) std::malloc(size)
#define PK_REALLOC(ptr, size) std::realloc(ptr, size)
#define PK_FREE(ptr) std::free(ptr)
#endif
#endif
#endif

View File

@ -60,4 +60,16 @@
#define PK_DEPRECATED __attribute__((deprecated))
#else
#define PK_DEPRECATED
#endif
#endif
#ifdef NDEBUG
#if defined(__GNUC__)
#define PK_INLINE __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
#define PK_INLINE __forceinline
#else
#define PK_INLINE inline
#endif
#else
#define PK_INLINE
#endif

View File

@ -31,7 +31,7 @@ typedef struct c11_array2d_view {
c11_vec2i origin;
} c11_array2d_view;
c11_array2d* py_newarray2d(py_OutRef out, int n_cols, int n_rows);
c11_array2d* c11_newarray2d(py_OutRef out, int n_cols, int n_rows);
/* chunked_array2d */
#define SMALLMAP_T__HEADER

View File

@ -0,0 +1,11 @@
#pragma once
#include "pocketpy/pocketpy.h"
bool generator__next__(int argc, py_Ref argv);
bool array2d_like_iterator__next__(int argc, py_Ref argv);
bool list_iterator__next__(int argc, py_Ref argv);
bool tuple_iterator__next__(int argc, py_Ref argv);
bool dict_items__next__(int argc, py_Ref argv);
bool range_iterator__next__(int argc, py_Ref argv);
bool str_iterator__next__(int argc, py_Ref argv);

View File

@ -31,4 +31,10 @@ void pk__add_module_pkpy();
void pk__add_module_libhv();
#else
#define pk__add_module_libhv()
#endif
#endif
#ifdef PK_BUILD_MODULE_CUTE_PNG
void pk__add_module_cute_png();
#else
#define pk__add_module_cute_png()
#endif

View File

@ -5,14 +5,19 @@
#define kPoolArenaSize (120 * 1024)
#define kMultiPoolCount 5
#define kPoolMaxBlockSize (32*kMultiPoolCount)
#define kPoolMaxBlockSize (32 * kMultiPoolCount)
typedef struct PoolArena {
int block_size;
int block_count;
int unused_length;
int* unused;
char data[kPoolArenaSize];
union {
char data[kPoolArenaSize];
int64_t _align64;
};
int unused[];
} PoolArena;
typedef struct Pool {

View File

@ -14,7 +14,12 @@ typedef struct py_TypeInfo {
py_GlobalRef module;
bool is_python; // is it a python class? (not derived from c object)
bool is_sealed; // can it be subclassed?
bool is_final; // can it be subclassed?
bool (*getattribute)(py_Ref self, py_Name name) PY_RAISE PY_RETURN;
bool (*setattribute)(py_Ref self, py_Name name, py_Ref val) PY_RAISE PY_RETURN;
bool (*delattribute)(py_Ref self, py_Name name) PY_RAISE;
bool (*getunboundmethod)(py_Ref self, py_Name name) PY_RETURN;
py_TValue annotations;
py_Dtor dtor; // destructor for this type, NULL if no dtor
@ -30,7 +35,7 @@ py_Type pk_newtype(const char* name,
const py_GlobalRef module,
void (*dtor)(void*),
bool is_python,
bool is_sealed);
bool is_final);
py_Type pk_newtypewithmode(py_Name name,
@ -38,4 +43,4 @@ py_Type pk_newtypewithmode(py_Name name,
const py_GlobalRef module,
void (*dtor)(void*),
bool is_python,
bool is_sealed, enum py_CompileMode mode);
bool is_final, enum py_CompileMode mode);

View File

@ -3,26 +3,22 @@
#include "pocketpy/common/vector.h"
#include "pocketpy/objects/base.h"
#define PK_DICT_MAX_COLLISION 4
typedef struct {
uint64_t hash;
py_TValue key;
py_TValue val;
} DictEntry;
typedef struct {
int _[PK_DICT_MAX_COLLISION];
} DictIndex;
typedef struct {
int length;
uint32_t capacity;
DictIndex* indices;
uint32_t null_index_value;
bool index_is_short;
void* indices;
c11_vector /*T=DictEntry*/ entries;
} Dict;
typedef c11_vector List;
void c11_chunked_array2d__mark(void* ud, c11_vector* p_stack);
void function__gc_mark(void* ud, c11_vector* p_stack);
void function__gc_mark(void* ud, c11_vector* p_stack);

View File

@ -67,7 +67,7 @@ typedef struct VM {
NameDict compile_time_funcs;
py_StackRef curr_class;
py_StackRef curr_decl_based_function;
py_StackRef curr_decl_based_function; // this is for get current function without frame
TraceInfo trace_info;
WatchdogInfo watchdog_info;
LineProfiler line_profiler;
@ -92,7 +92,6 @@ bool pk__parse_int_slice(py_Ref slice,
bool pk__normalize_index(int* index, int length);
bool pk__object_new(int argc, py_Ref argv);
py_TypeInfo* pk_typeinfo(py_Type type);
bool pk_wrapper__self(int argc, py_Ref argv);
@ -113,7 +112,6 @@ const char* pk_opname(Opcode op);
int pk_arrayview(py_Ref self, py_TValue** p);
bool pk_wrapper__arrayequal(py_Type type, int argc, py_Ref argv);
bool pk_arrayiter(py_Ref val);
bool pk_arraycontains(py_Ref self, py_Ref val);
bool pk_loadmethod(py_StackRef self, py_Name name);
@ -139,7 +137,8 @@ py_Type pk_dict__register();
py_Type pk_dict_items__register();
py_Type pk_list__register();
py_Type pk_tuple__register();
py_Type pk_array_iterator__register();
py_Type pk_list_iterator__register();
py_Type pk_tuple_iterator__register();
py_Type pk_slice__register();
py_Type pk_function__register();
py_Type pk_nativefunc__register();

View File

@ -19,8 +19,10 @@ typedef struct py_TValue {
PyObject* _obj;
c11_vec2 _vec2;
c11_vec2i _vec2i;
c11_vec3 _vec3;
c11_vec3i _vec3i;
c11_color32 _color32;
void* _ptr;
char _chars[8];
char _chars[16];
};
} py_TValue;

View File

@ -0,0 +1,15 @@
#pragma once
#include "pocketpy/objects/base.h"
#include "pocketpy/common/vector.h"
typedef struct tuple_iterator {
py_TValue* p;
int length;
int index;
} tuple_iterator;
typedef struct list_iterator {
c11_vector* vec;
int index;
} list_iterator;

View File

@ -30,7 +30,14 @@ typedef void (*py_Dtor)(void*);
#ifdef PK_IS_PUBLIC_INCLUDE
typedef struct py_TValue {
int64_t _[2];
py_Type type;
bool is_ptr;
int extra;
union {
int64_t _i64;
char _chars[16];
};
} py_TValue;
#endif
@ -70,8 +77,10 @@ typedef void (*py_TraceFunc)(py_Frame* frame, enum py_TraceEvent);
/// A struct contains the callbacks of the VM.
typedef struct py_Callbacks {
/// Used by `__import__` to load source code of a module.
/// Used by `__import__` to load a source module.
char* (*importfile)(const char*);
/// Called before `importfile` to lazy-import a C module.
py_GlobalRef (*lazyimport)(const char*);
/// Used by `print` to output a string.
void (*print)(const char*);
/// Flush the output buffer of `print`.
@ -124,6 +133,13 @@ PK_API int py_gc_collect();
/// Setup the callbacks for the current VM.
PK_API py_Callbacks* py_callbacks();
/// Wrapper for `PK_MALLOC(size)`.
PK_API void* py_malloc(size_t size);
/// Wrapper for `PK_REALLOC(ptr, size)`.
PK_API void* py_realloc(void* ptr, size_t size);
/// Wrapper for `PK_FREE(ptr)`.
PK_API void py_free(void* ptr);
/// Begin the watchdog with `timeout` in milliseconds.
/// `PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature.
/// You need to call `py_watchdog_end()` later.
@ -201,6 +217,8 @@ PK_API py_GlobalRef py_NIL();
/// Create an `int` object.
PK_API void py_newint(py_OutRef, py_i64);
/// Create a trivial value object.
PK_API void py_newtrivial(py_OutRef out, py_Type type, void* data, int size);
/// Create a `float` object.
PK_API void py_newfloat(py_OutRef, py_f64);
/// Create a `bool` object.
@ -282,6 +300,8 @@ PK_API void* py_newobject(py_OutRef out, py_Type type, int slots, int udsize);
/// Convert an `int` object in python to `int64_t`.
PK_API py_i64 py_toint(py_Ref);
/// Get the address of the trivial value object.
PK_API void* py_totrivial(py_Ref);
/// Convert a `float` object in python to `double`.
PK_API py_f64 py_tofloat(py_Ref);
/// Cast a `int` or `float` object in python to `double`.
@ -350,6 +370,15 @@ PK_API py_GlobalRef py_tpobject(py_Type type);
PK_API const char* py_tpname(py_Type type);
/// Call a type to create a new instance.
PK_API bool py_tpcall(py_Type type, int argc, py_Ref argv) PY_RAISE PY_RETURN;
/// Disable the type for subclassing.
PK_API void py_tpsetfinal(py_Type type);
/// Set attribute hooks for the given type.
PK_API void py_tphookattributes(py_Type type,
bool (*getattribute)(py_Ref self, py_Name name) PY_RAISE PY_RETURN,
bool (*setattribute)(py_Ref self, py_Name name, py_Ref val)
PY_RAISE PY_RETURN,
bool (*delattribute)(py_Ref self, py_Name name) PY_RAISE,
bool (*getunboundmethod)(py_Ref self, py_Name name) PY_RETURN);
/// Check if the object is an instance of the given type exactly.
/// Raise `TypeError` if the check fails.
@ -389,6 +418,7 @@ PK_API void py_setglobal(py_Name name, py_Ref val);
PK_API py_ItemRef py_getbuiltin(py_Name name);
/// Get the last return value.
/// Please note that `py_retval()` cannot be used as input argument.
PK_API py_GlobalRef py_retval();
/// Get an item from the object's `__dict__`.
@ -715,19 +745,33 @@ PK_API bool
/// noexcept
PK_API int py_dict_len(py_Ref self);
/************* random module *************/
PK_API void py_newRandom(py_OutRef out);
PK_API void py_Random_seed(py_Ref self, py_i64 seed);
PK_API py_f64 py_Random_random(py_Ref self);
PK_API py_f64 py_Random_uniform(py_Ref self, py_f64 a, py_f64 b);
PK_API py_i64 py_Random_randint(py_Ref self, py_i64 a, py_i64 b);
/************* array2d module *************/
PK_API void py_newarray2d(py_OutRef out, int width, int height);
PK_API int py_array2d_getwidth(py_Ref self);
PK_API int py_array2d_getheight(py_Ref self);
PK_API py_ObjectRef py_array2d_getitem(py_Ref self, int x, int y);
PK_API void py_array2d_setitem(py_Ref self, int x, int y, py_Ref val);
/************* vmath module *************/
void py_newvec2(py_OutRef out, c11_vec2);
void py_newvec3(py_OutRef out, c11_vec3);
void py_newvec2i(py_OutRef out, c11_vec2i);
void py_newvec3i(py_OutRef out, c11_vec3i);
void py_newcolor32(py_OutRef out, c11_color32);
c11_mat3x3* py_newmat3x3(py_OutRef out);
c11_vec2 py_tovec2(py_Ref self);
c11_vec3 py_tovec3(py_Ref self);
c11_vec2i py_tovec2i(py_Ref self);
c11_vec3i py_tovec3i(py_Ref self);
c11_mat3x3* py_tomat3x3(py_Ref self);
c11_color32 py_tocolor32(py_Ref self);
PK_API void py_newvec2(py_OutRef out, c11_vec2);
PK_API void py_newvec3(py_OutRef out, c11_vec3);
PK_API void py_newvec2i(py_OutRef out, c11_vec2i);
PK_API void py_newvec3i(py_OutRef out, c11_vec3i);
PK_API void py_newcolor32(py_OutRef out, c11_color32);
PK_API c11_mat3x3* py_newmat3x3(py_OutRef out);
PK_API c11_vec2 py_tovec2(py_Ref self);
PK_API c11_vec3 py_tovec3(py_Ref self);
PK_API c11_vec2i py_tovec2i(py_Ref self);
PK_API c11_vec3i py_tovec3i(py_Ref self);
PK_API c11_mat3x3* py_tomat3x3(py_Ref self);
PK_API c11_color32 py_tocolor32(py_Ref self);
/************* Others *************/
@ -755,10 +799,11 @@ enum py_PredefinedType {
tp_bool,
tp_str,
tp_str_iterator,
tp_list, // c11_vector
tp_tuple, // N slots
tp_array_iterator,
tp_slice, // 3 slots (start, stop, step)
tp_list, // c11_vector
tp_tuple, // N slots
tp_list_iterator, // 1 slot
tp_tuple_iterator, // 1 slot
tp_slice, // 3 slots (start, stop, step)
tp_range,
tp_range_iterator,
tp_module,

View File

@ -124,3 +124,4 @@ bool METHOD(contains)(NAME* self, K key) {
#undef less
#undef partial_less
#undef equal
#undef hash

View File

@ -14,7 +14,7 @@ public:
// get the python exception object
object& exception() { return m_exception; }
~python_error() { std::free(m_what); }
~python_error() { py_free(m_what); }
bool match(py_Type type) const { return py_isinstance(m_exception.ptr(), type); }

View File

@ -0,0 +1,5 @@
from array2d import array2d
from vmath import color32
def loads(data: bytes) -> array2d[color32]: ...
def dumps(image: array2d[color32]) -> bytes: ...

View File

@ -186,6 +186,7 @@ class color32:
def __eq__(self, other: object) -> bool: ...
def __ne__(self, other: object) -> bool: ...
def __repr__(self) -> str: ...
def __hash__(self) -> int: ...
@property
def r(self) -> int: ...

View File

@ -1,7 +1,6 @@
#include "pocketpy/common/algorithm.h"
#include "pocketpy/config.h"
#include <string.h>
#include <stdlib.h>
static bool _stable_sort_merge(char* a,
char* a_end,

View File

@ -1,15 +1,12 @@
#include "pocketpy/common/chunkedvector.h"
#include <stdlib.h>
#include <string.h>
#include "pocketpy/common/utils.h"
#include "pocketpy/config.h"
#include "pocketpy/pocketpy.h"
#include <assert.h>
#if defined(_MSC_VER)
#include <intrin.h>
#endif
inline static int c11_bit_length(unsigned long x) {
PK_INLINE static int c11__bit_length(unsigned long x) {
#if(defined(__clang__) || defined(__GNUC__))
return x == 0 ? 0 : (int)sizeof(unsigned long) * 8 - __builtin_clzl(x);
#elif defined(_MSC_VER)
@ -39,7 +36,6 @@ void c11_chunkedvector__ctor(c11_chunkedvector* self, int elem_size, int initial
self->initial_chunks = initial_chunks;
void* chunks_data = PK_MALLOC(elem_size * ((1U << (unsigned int)initial_chunks) - 1));
for(int i = 0; i < initial_chunks; i++) {
// TODO: optimize?
c11_chunkedvector_chunk chunk = {.length = 0,
.capacity = 1U << i,
.data = (char*)chunks_data + elem_size * ((1U << i) - 1U)};
@ -59,10 +55,11 @@ void c11_chunkedvector__dtor(c11_chunkedvector* self) {
void* c11_chunkedvector__emplace(c11_chunkedvector* self) {
if(self->length == self->capacity) {
#ifndef NDEBUG
c11_chunkedvector_chunk last_chunk = c11_vector__back(c11_chunkedvector_chunk, &self->chunks);
assert(last_chunk.capacity == last_chunk.length);
#endif
#ifndef NDEBUG
c11_chunkedvector_chunk last_chunk =
c11_vector__back(c11_chunkedvector_chunk, &self->chunks);
assert(last_chunk.capacity == last_chunk.length);
#endif
c11_chunkedvector_chunk chunk = {
.length = 0,
.capacity = 1U << (unsigned int)self->chunks.length,
@ -70,9 +67,14 @@ void* c11_chunkedvector__emplace(c11_chunkedvector* self) {
self->capacity += chunk.capacity;
c11_vector__push(c11_chunkedvector_chunk, &self->chunks, chunk);
}
int last_chunk_index = c11_bit_length(self->length + 1) - 1;
#if 1
int last_chunk_index = c11__bit_length(self->length + 1) - 1;
c11_chunkedvector_chunk* last_chunk =
c11__at(c11_chunkedvector_chunk, &self->chunks, last_chunk_index);
#else
// This is not correct, because there is some pre-allocated chunks
c11_chunkedvector_chunk* last_chunk = &c11_vector__back(c11_chunkedvector_chunk, &self->chunks);
#endif
void* p = (char*)last_chunk->data + self->elem_size * last_chunk->length;
last_chunk->length++;
self->length++;
@ -80,7 +82,7 @@ void* c11_chunkedvector__emplace(c11_chunkedvector* self) {
}
void* c11_chunkedvector__at(c11_chunkedvector* self, int index) {
int chunk_index = c11_bit_length(index + 1) - 1;
int chunk_index = c11__bit_length(index + 1) - 1;
c11_chunkedvector_chunk* chunk = c11__at(c11_chunkedvector_chunk, &self->chunks, chunk_index);
return (char*)chunk->data + (index + 1 - (1U << (unsigned int)chunk_index)) * self->elem_size;
}

View File

@ -1,7 +1,6 @@
#include "pocketpy/common/memorypool.h"
#include "pocketpy/pocketpy.h"
#include "pocketpy/config.h"
#include <stdlib.h>
#include <stdbool.h>
void FixedMemoryPool__ctor(FixedMemoryPool* self, int BlockSize, int BlockCount) {

View File

@ -2,9 +2,9 @@
#include "pocketpy/common/name.h"
#include "pocketpy/common/str.h"
#include "pocketpy/common/threads.h"
#include "pocketpy/pocketpy.h"
#include <stdatomic.h>
#include "pocketpy/common/threads.h"
#include <assert.h>
typedef struct NameBucket NameBucket;
@ -17,7 +17,9 @@ typedef struct NameBucket {
static struct {
NameBucket* table[0x10000];
#if PK_ENABLE_THREADS
atomic_flag lock;
#endif
} pk_string_table;
#define MAGIC_METHOD(x) py_Name x;
@ -43,9 +45,11 @@ void pk_names_finalize() {
}
py_Name py_namev(c11_sv name) {
#if PK_ENABLE_THREADS
while(atomic_flag_test_and_set(&pk_string_table.lock)) {
c11_thrd_yield();
}
#endif
uint64_t hash = c11_sv__hash(name);
int index = hash & 0xFFFF;
NameBucket* p = pk_string_table.table[index];
@ -61,7 +65,9 @@ py_Name py_namev(c11_sv name) {
p = p->next;
}
if(found) {
#if PK_ENABLE_THREADS
atomic_flag_clear(&pk_string_table.lock);
#endif
return (py_Name)p;
}
@ -78,7 +84,9 @@ py_Name py_namev(c11_sv name) {
assert(prev->next == NULL);
prev->next = bucket;
}
#if PK_ENABLE_THREADS
atomic_flag_clear(&pk_string_table.lock);
#endif
return (py_Name)bucket;
}

View File

@ -1,5 +1,7 @@
#include "pocketpy/common/socket.h"
#if PK_ENABLE_OS
#include <stddef.h>
#if defined (_WIN32) || defined (_WIN64)
@ -120,3 +122,5 @@ int c11_socket_get_last_error(){
#undef SOCKET_HANDLERTOFD
#undef SOCKET_FDTOHANDLER
#endif // PK_ENABLE_OS

View File

@ -1,7 +1,6 @@
#include "pocketpy/objects/sourcedata.h"
#include "pocketpy/common/sstream.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
static void SourceData__ctor(struct SourceData* self,
@ -25,6 +24,16 @@ static void SourceData__ctor(struct SourceData* self,
source++;
}
self->source = c11_sbuf__submit(&ss);
// remove trailing newline
int last_index = self->source->size - 1;
while(last_index >= 0 && isspace(self->source->data[last_index])) {
last_index--;
}
if(last_index >= 0) {
self->source->size = last_index + 1;
self->source->data[last_index + 1] = '\0';
}
self->is_dynamic = is_dynamic;
c11_vector__push(const char*, &self->line_starts, self->source->data);
}

View File

@ -4,8 +4,8 @@
#include "pocketpy/pocketpy.h"
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>

View File

@ -2,11 +2,10 @@
#include "pocketpy/common/sstream.h"
#include "pocketpy/common/utils.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
c11_string* c11_string__new(const char* data) { return c11_string__new2(data, strlen(data)); }
@ -190,6 +189,26 @@ uint64_t c11_sv__hash(c11_sv self) {
return hash;
}
c11_vector /* T=c11_sv */ c11_sv__splitwhitespace(c11_sv self) {
c11_vector retval;
c11_vector__ctor(&retval, sizeof(c11_sv));
const char* data = self.data;
int i = 0;
for(int j = 0; j < self.size; j++) {
if(isspace(data[j])) {
assert(j >= i);
c11_sv tmp = {data + i, j - i};
c11_vector__push(c11_sv, &retval, tmp);
i = j + 1;
}
}
if(i <= self.size) {
c11_sv tmp = {data + i, self.size - i};
c11_vector__push(c11_sv, &retval, tmp);
}
return retval;
}
c11_vector /* T=c11_sv */ c11_sv__split(c11_sv self, char sep) {
c11_vector retval;
c11_vector__ctor(&retval, sizeof(c11_sv));

View File

@ -1,6 +1,7 @@
#include "pocketpy/export.h"
#include "pocketpy/common/threads.h"
#if PK_ENABLE_THREADS
#if PK_USE_PTHREADS
bool c11_thrd_create(c11_thrd_t* thrd, c11_thrd_retval_t (*func)(void*), void* arg) {
@ -19,4 +20,6 @@ bool c11_thrd_create(c11_thrd_t* thrd, c11_thrd_retval_t (*func)(void*), void* a
void c11_thrd_yield() { thrd_yield(); }
#endif
#endif
#endif // PK_ENABLE_THREADS

View File

@ -1,6 +1,5 @@
#include "pocketpy/common/vector.h"
#include <stdlib.h>
#include <string.h>
#include "pocketpy/common/utils.h"
#include "pocketpy/config.h"
@ -40,7 +39,7 @@ void c11_vector__reserve(c11_vector* self, int capacity) {
void c11_vector__clear(c11_vector* self) { self->length = 0; }
void* c11_vector__emplace(c11_vector* self) {
if(self->length == self->capacity) c11_vector__reserve(self, self->capacity * 2);
if(self->length == self->capacity) { c11_vector__reserve(self, c11_vector__nextcap(self)); }
void* p = (char*)self->data + (size_t)self->elem_size * (size_t)self->length;
self->length++;
return p;
@ -63,8 +62,17 @@ void* c11_vector__submit(c11_vector* self, int* length) {
return retval;
}
void c11_vector__swap(c11_vector *self, c11_vector *other){
void c11_vector__swap(c11_vector* self, c11_vector* other) {
c11_vector tmp = *self;
*self = *other;
*other = tmp;
}
int c11_vector__nextcap(c11_vector* self) {
if(self->capacity < 1024) {
return self->capacity * 2;
} else {
// increase by 25%
return self->capacity + (self->capacity >> 2);
}
}

View File

@ -28,6 +28,7 @@ typedef struct ExprVt {
bool is_subscr; // SubscrExpr
bool is_starred; // StarredExpr
bool is_binary; // BinaryExpr
bool is_ternary; // TernaryExpr
void (*dtor)(Expr*);
} ExprVt;
@ -873,7 +874,11 @@ void TernaryExpr__emit_(Expr* self_, Ctx* ctx) {
}
TernaryExpr* TernaryExpr__new(int line) {
const static ExprVt Vt = {.dtor = TernaryExpr__dtor, .emit_ = TernaryExpr__emit_};
const static ExprVt Vt = {
.dtor = TernaryExpr__dtor,
.emit_ = TernaryExpr__emit_,
.is_ternary = true,
};
TernaryExpr* self = PK_MALLOC(sizeof(TernaryExpr));
self->vt = &Vt;
self->line = line;
@ -1681,6 +1686,10 @@ static Error* exprTernary(Compiler* self) {
e->cond = Ctx__s_popx(ctx());
e->true_expr = Ctx__s_popx(ctx());
Ctx__s_push(ctx(), (Expr*)e);
if(e->cond->vt->is_ternary || e->false_expr->vt->is_ternary || e->true_expr->vt->is_ternary) {
return SyntaxError(self, "nested ternary expressions without `()` are ambiguous");
}
return NULL;
}
@ -2856,13 +2865,14 @@ static Error* compile_stmt(Compiler* self) {
bool is_typed_name = false; // e.g. x: int
// eat variable's type hint if it is a single name
if(Ctx__s_top(ctx())->vt->is_name) {
const ExprVt* top_vt = Ctx__s_top(ctx())->vt;
if(top_vt->is_name || top_vt->is_attrib) {
if(match(TK_COLON)) {
c11_sv type_hint;
check(consume_type_hints_sv(self, &type_hint));
is_typed_name = true;
if(ctx()->is_compiling_class) {
if(ctx()->is_compiling_class && top_vt->is_name) {
NameExpr* ne = (NameExpr*)Ctx__s_top(ctx());
int index = Ctx__add_const_string(ctx(), type_hint);
Ctx__emit_(ctx(), OP_LOAD_CONST, index, BC_KEEPLINE);

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@
#include "pocketpy/objects/codeobject.h"
#include "pocketpy/pocketpy.h"
#include <stdbool.h>
#include <assert.h>
void ValueStack__ctor(ValueStack* self) {
self->sp = self->begin;

View File

@ -4,6 +4,7 @@
#include "pocketpy/objects/base.h"
#include "pocketpy/pocketpy.h"
#include <stdbool.h>
#include <assert.h>
void pk_newgenerator(py_Ref out, py_Frame* frame, py_TValue* begin, py_TValue* end) {
Generator* ud = py_newobject(out, tp_generator, 1, sizeof(Generator));
@ -20,7 +21,7 @@ void Generator__dtor(Generator* ud) {
if(ud->frame) Frame__delete(ud->frame);
}
static bool generator__next__(int argc, py_Ref argv) {
bool generator__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Generator* ud = py_touserdata(argv);
py_StackRef p0 = py_peek(0);

View File

@ -3,6 +3,7 @@
#include "pocketpy/interpreter/objectpool.h"
#include "pocketpy/objects/base.h"
#include "pocketpy/pocketpy.h"
#include <assert.h>
void ManagedHeap__ctor(ManagedHeap* self) {
MultiPool__ctor(&self->small_objects);
@ -33,25 +34,26 @@ void ManagedHeap__dtor(ManagedHeap* self) {
void ManagedHeap__collect_if_needed(ManagedHeap* self) {
if(!self->gc_enabled) return;
if(self->gc_counter < self->gc_threshold) return;
self->gc_counter = 0;
int freed = ManagedHeap__collect(self);
// adjust `gc_threshold` based on `freed_ma`
self->freed_ma[0] = self->freed_ma[1];
self->freed_ma[1] = self->freed_ma[2];
self->freed_ma[2] = freed;
int avg_freed = (self->freed_ma[0] + self->freed_ma[1] + self->freed_ma[2]) / 3;
const int upper = PK_GC_MIN_THRESHOLD * 8;
const int upper = PK_GC_MIN_THRESHOLD * 16;
const int lower = PK_GC_MIN_THRESHOLD / 2;
float free_ratio = (float)avg_freed / self->gc_threshold;
int new_threshold = self->gc_threshold * (1 / free_ratio);
int new_threshold = self->gc_threshold * (1.5f / free_ratio);
// printf("gc_threshold=%d, avg_freed=%d, new_threshold=%d\n", self->gc_threshold, avg_freed,
// new_threshold);
self->gc_threshold = c11__min(c11__max(new_threshold, lower), upper);
}
int ManagedHeap__collect(ManagedHeap* self) {
self->gc_counter = 0;
ManagedHeap__mark(self);
int freed = ManagedHeap__sweep(self);
// printf("GC: collected %d objects\n", freed);
return freed;
}

View File

@ -1,4 +1,5 @@
#include "pocketpy/interpreter/line_profiler.h"
#include <assert.h>
void LineProfiler__ctor(LineProfiler* self) {
c11_smallmap_p2i__ctor(&self->records);

View File

@ -1,12 +1,10 @@
#include "pocketpy/interpreter/objectpool.h"
#include "pocketpy/config.h"
#include "pocketpy/objects/object.h"
#include "pocketpy/common/sstream.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
static PoolArena* PoolArena__new(int block_size) {
@ -16,7 +14,6 @@ static PoolArena* PoolArena__new(int block_size) {
self->block_size = block_size;
self->block_count = block_count;
self->unused_length = block_count;
self->unused = PK_MALLOC(sizeof(int) * block_count);
for(int i = 0; i < block_count; i++) {
self->unused[i] = i;
}
@ -29,7 +26,6 @@ static void PoolArena__delete(PoolArena* self) {
PyObject* obj = (PyObject*)(self->data + i * self->block_size);
if(obj->type != 0) PyObject__dtor(obj);
}
PK_FREE(self->unused);
PK_FREE(self);
}

View File

@ -1,4 +1,5 @@
#include "pocketpy/interpreter/vm.h"
#include <assert.h>
py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name) {
assert(ti != NULL);
@ -10,17 +11,17 @@ py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name) {
return NULL;
}
py_ItemRef py_tpfindname(py_Type type, py_Name name) {
PK_INLINE py_ItemRef py_tpfindname(py_Type type, py_Name name) {
py_TypeInfo* ti = pk_typeinfo(type);
return pk_tpfindname(ti, name);
}
py_Ref py_tpfindmagic(py_Type t, py_Name name) {
PK_INLINE py_Ref py_tpfindmagic(py_Type t, py_Name name) {
// assert(py_ismagicname(name));
return py_tpfindname(t, name);
}
py_Type py_tpbase(py_Type t) {
PK_INLINE py_Type py_tpbase(py_Type t) {
assert(t);
py_TypeInfo* ti = pk_typeinfo(t);
return ti->base;
@ -44,7 +45,7 @@ const char* py_tpname(py_Type type) {
return py_name2str(name);
}
py_TypeInfo* pk_typeinfo(py_Type type) {
PK_INLINE py_TypeInfo* pk_typeinfo(py_Type type) {
#ifndef NDEBUG
int length = pk_current_vm->types.length;
if(type < 0 || type >= length) {
@ -60,11 +61,11 @@ static void py_TypeInfo__common_init(py_Name name,
const py_GlobalRef module,
void (*dtor)(void*),
bool is_python,
bool is_sealed,
bool is_final,
py_TypeInfo* self,
py_TValue* typeobject) {
py_TypeInfo* base_ti = base ? pk_typeinfo(base) : NULL;
if(base_ti && base_ti->is_sealed) {
if(base_ti && base_ti->is_final) {
c11__abort("type '%s' is not an acceptable base type", py_name2str(base_ti->name));
}
@ -78,7 +79,12 @@ static void py_TypeInfo__common_init(py_Name name,
if(!dtor && base) dtor = base_ti->dtor;
self->is_python = is_python;
self->is_sealed = is_sealed;
self->is_final = is_final;
self->getattribute = NULL;
self->setattribute = NULL;
self->delattribute = NULL;
self->getunboundmethod = NULL;
self->annotations = *py_NIL();
self->dtor = dtor;
@ -90,7 +96,7 @@ py_Type pk_newtype(const char* name,
const py_GlobalRef module,
void (*dtor)(void*),
bool is_python,
bool is_sealed) {
bool is_final) {
py_Type index = pk_current_vm->types.length;
py_TypeInfo* self = py_newobject(py_retval(), tp_type, -1, sizeof(py_TypeInfo));
py_TypeInfo__common_init(py_name(name),
@ -99,7 +105,7 @@ py_Type pk_newtype(const char* name,
module,
dtor,
is_python,
is_sealed,
is_final,
self,
py_retval());
TypePointer* pointer = c11_vector__emplace(&pk_current_vm->types);
@ -113,7 +119,7 @@ py_Type pk_newtypewithmode(py_Name name,
const py_GlobalRef module,
void (*dtor)(void*),
bool is_python,
bool is_sealed,
bool is_final,
enum py_CompileMode mode) {
if(mode == RELOAD_MODE && module != NULL) {
py_ItemRef old_class = py_getdict(module, name);
@ -131,7 +137,7 @@ py_Type pk_newtypewithmode(py_Name name,
module,
dtor,
is_python,
is_sealed,
is_final,
self,
&self->self);
TypePointer* pointer = c11__at(TypePointer, &pk_current_vm->types, index);
@ -141,7 +147,7 @@ py_Type pk_newtypewithmode(py_Name name,
}
}
return pk_newtype(py_name2str(name), base, module, dtor, is_python, is_sealed);
return pk_newtype(py_name2str(name), base, module, dtor, is_python, is_final);
}
py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, void (*dtor)(void*)) {
@ -149,4 +155,23 @@ py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, vo
py_Type type = pk_newtype(name, base, module, dtor, false, false);
if(module) py_setdict(module, py_name(name), py_tpobject(type));
return type;
}
}
void py_tpsetfinal(py_Type type) {
assert(type);
py_TypeInfo* ti = pk_typeinfo(type);
ti->is_final = true;
}
void py_tphookattributes(py_Type type,
bool (*getattribute)(py_Ref self, py_Name name),
bool (*setattribute)(py_Ref self, py_Name name, py_Ref val),
bool (*delattribute)(py_Ref self, py_Name name),
bool (*getunboundmethod)(py_Ref self, py_Name name)) {
assert(type);
py_TypeInfo* ti = pk_typeinfo(type);
ti->getattribute = getattribute;
ti->setattribute = setattribute;
ti->delattribute = delattribute;
ti->getunboundmethod = getunboundmethod;
}

View File

@ -1,6 +1,5 @@
#include "pocketpy/interpreter/vm.h"
#include "pocketpy/common/memorypool.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/interpreter/generator.h"
#include "pocketpy/interpreter/modules.h"
@ -10,6 +9,7 @@
#include "pocketpy/common/_generated.h"
#include "pocketpy/pocketpy.h"
#include <stdbool.h>
#include <assert.h>
static char* pk_default_importfile(const char* path) {
#if PK_ENABLE_OS
@ -59,6 +59,7 @@ void VM__ctor(VM* self) {
self->main = NULL;
self->callbacks.importfile = pk_default_importfile;
self->callbacks.lazyimport = NULL;
self->callbacks.print = pk_default_print;
self->callbacks.flush = pk_default_flush;
self->callbacks.getchr = pk_default_getchr;
@ -109,7 +110,8 @@ void VM__ctor(VM* self) {
validate(tp_list, pk_list__register());
validate(tp_tuple, pk_tuple__register());
validate(tp_array_iterator, pk_array_iterator__register());
validate(tp_list_iterator, pk_list_iterator__register());
validate(tp_tuple_iterator, pk_tuple_iterator__register());
validate(tp_slice, pk_slice__register());
validate(tp_range, pk_range__register());
@ -234,6 +236,7 @@ void VM__ctor(VM* self) {
pk__add_module_conio();
pk__add_module_lz4(); // optional
pk__add_module_libhv(); // optional
pk__add_module_cute_png(); // optional
pk__add_module_pkpy();
// add python builtins

View File

@ -1,4 +1,5 @@
#include "pocketpy/interpreter/vm.h"
#include <assert.h>
void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) {
return;
@ -123,5 +124,8 @@ py_Ref py_name2ref(py_Name name) {
void PyObject__dtor(PyObject* self) {
py_Dtor dtor = c11__getitem(TypePointer, &pk_current_vm->types, self->type).dtor;
if(dtor) dtor(PyObject__userdata(self));
if(self->slots == -1) NameDict__dtor(PyObject__dict(self));
if(self->slots == -1) {
NameDict* dict = PyObject__dict(self);
NameDict__dtor(dict);
}
}

View File

@ -16,7 +16,7 @@ static bool c11_array2d__set(c11_array2d* self, int col, int row, py_Ref value)
return true;
}
c11_array2d* py_newarray2d(py_OutRef out, int n_cols, int n_rows) {
c11_array2d* c11_newarray2d(py_OutRef out, int n_cols, int n_rows) {
int numel = n_cols * n_rows;
c11_array2d* ud = py_newobject(out, tp_array2d, numel, sizeof(c11_array2d));
ud->header.n_cols = n_cols;
@ -112,7 +112,10 @@ static bool array2d_like_index(int argc, py_Ref argv) {
int code = py_equal(item, value);
if(code == -1) return false;
if(code == 1) {
py_newvec2i(py_retval(), (c11_vec2i){{i, j}});
py_newvec2i(py_retval(),
(c11_vec2i){
{i, j}
});
return true;
}
}
@ -176,7 +179,7 @@ static bool array2d_like_map(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
c11_array2d_like* self = py_touserdata(argv);
py_Ref f = py_arg(1);
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_Ref item = self->f_get(self, i, j);
@ -228,7 +231,7 @@ static bool _array2d_like_broadcasted_zip_with(int argc, py_Ref argv, py_Name op
} else {
other = NULL;
}
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_Ref lhs = self->f_get(self, i, j);
@ -254,7 +257,7 @@ static bool array2d_like_zip_with(int argc, py_Ref argv) {
c11_array2d_like* other = py_touserdata(py_arg(1));
py_Ref f = py_arg(2);
if(!_array2d_like_check_same_shape(self, other)) return false;
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_push(f);
@ -299,7 +302,7 @@ DEF_ARRAY2D_LIKE__MAGIC_ZIP_WITH(__xor__, __xor__, 0)
static bool array2d_like__invert__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_array2d_like* self = py_touserdata(argv);
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_Ref item = self->f_get(self, i, j);
@ -316,7 +319,7 @@ static bool array2d_like_copy(int argc, py_Ref argv) {
// def copy(self) -> 'array2d': ...
PY_CHECK_ARGC(1);
c11_array2d_like* self = py_touserdata(argv);
c11_array2d* res = py_newarray2d(py_retval(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_retval(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_Ref item = self->f_get(self, i, j);
@ -637,7 +640,7 @@ static bool array2d_like_get_bounding_rect(int argc, py_Ref argv) {
static bool array2d_like_count_neighbors(int argc, py_Ref argv) {
PY_CHECK_ARGC(3);
c11_array2d_like* self = py_touserdata(argv);
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
py_Ref value = py_arg(1);
const char* neighborhood = py_tostr(py_arg(2));
@ -703,7 +706,7 @@ static bool array2d_like_convolve(int argc, py_Ref argv) {
int ksize = kernel->n_cols;
if(ksize % 2 == 0) return ValueError("kernel size must be odd");
int ksize_half = ksize / 2;
c11_array2d* res = py_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
c11_array2d* res = c11_newarray2d(py_pushtmp(), self->n_cols, self->n_rows);
for(int j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) {
py_i64 sum = 0;
@ -800,7 +803,7 @@ static void register_array2d_like(py_Ref mod) {
}
}
static bool array2d_like_iterator__next__(int argc, py_Ref argv) {
bool array2d_like_iterator__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_array2d_like_iterator* self = py_touserdata(argv);
if(self->j >= self->array->n_rows) return StopIteration();
@ -834,7 +837,7 @@ static bool array2d__new__(int argc, py_Ref argv) {
int n_cols = argv[1]._i64;
int n_rows = argv[2]._i64;
if(n_cols <= 0 || n_rows <= 0) return ValueError("array2d() expected positive dimensions");
c11_array2d* ud = py_newarray2d(py_pushtmp(), n_cols, n_rows);
c11_array2d* ud = c11_newarray2d(py_pushtmp(), n_cols, n_rows);
// setup initial values
if(py_callable(default_)) {
for(int j = 0; j < n_rows; j++) {
@ -876,7 +879,7 @@ static bool array2d_fromlist_STATIC(int argc, py_Ref argv) {
return ValueError("fromlist() expected a list of lists with the same length");
}
}
c11_array2d* res = py_newarray2d(py_retval(), n_cols, n_rows);
c11_array2d* res = c11_newarray2d(py_retval(), n_cols, n_rows);
for(int j = 0; j < n_rows; j++) {
py_Ref row_j = py_list_getitem(argv, j);
for(int i = 0; i < n_cols; i++) {
@ -1359,4 +1362,30 @@ void pk__add_module_array2d() {
register_array2d(mod);
register_array2d_view(mod);
register_chunked_array2d(mod);
}
void py_newarray2d(py_OutRef out, int width, int height) { c11_newarray2d(out, width, height); }
int py_array2d_getwidth(py_Ref self) {
assert(self->type == tp_array2d);
c11_array2d* ud = py_touserdata(self);
return ud->header.n_cols;
}
int py_array2d_getheight(py_Ref self) {
assert(self->type == tp_array2d);
c11_array2d* ud = py_touserdata(self);
return ud->header.n_rows;
}
py_ObjectRef py_array2d_getitem(py_Ref self, int x, int y) {
assert(self->type == tp_array2d);
c11_array2d* ud = py_touserdata(self);
return c11_array2d__get(ud, x, y);
}
void py_array2d_setitem(py_Ref self, int x, int y, py_Ref value) {
assert(self->type == tp_array2d);
c11_array2d* ud = py_touserdata(self);
c11_array2d__set(ud, x, y, value);
}

View File

@ -1,5 +1,4 @@
#include "pocketpy/pocketpy.h"
#include <stdlib.h>
#if PY_SYS_PLATFORM == 0

View File

@ -16,7 +16,7 @@ static bool Enum__wrapper_field(py_Name name, py_Ref value, void* ctx) {
}
static void Enum__on_end_subclass(py_TypeInfo* derived_ti) {
derived_ti->is_sealed = true;
derived_ti->is_final = true;
py_applydict(&derived_ti->self, Enum__wrapper_field, &derived_ti->self);
}

View File

@ -1,6 +1,5 @@
#ifdef PK_BUILD_MODULE_LZ4
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "pocketpy/pocketpy.h"

View File

@ -1,8 +1,4 @@
#include "pocketpy/pocketpy.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/objects/object.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/interpreter/vm.h"
#include <math.h>

View File

@ -2,7 +2,7 @@
#include "pocketpy/pocketpy.h"
#include "pocketpy/interpreter/vm.h"
#if PK_ENABLE_OS == 1
#if PK_ENABLE_OS
#include <errno.h>

View File

@ -656,7 +656,7 @@ bool py_pickle_loads_body(const unsigned char* p, int memo_length, c11_smallmap_
case PKL_ARRAY2D: {
int n_cols = pkl__read_int(&p);
int n_rows = pkl__read_int(&p);
c11_array2d* arr = py_newarray2d(py_pushtmp(), n_cols, n_rows);
c11_array2d* arr = c11_newarray2d(py_pushtmp(), n_cols, n_rows);
int total_size = arr->header.numel * sizeof(py_TValue);
memcpy(arr->data, p, total_size);
for(int i = 0; i < arr->header.numel; i++) {

View File

@ -106,6 +106,8 @@ static bool pkpy_watchdog_end(int argc, py_Ref argv) {
}
#endif
#if PK_ENABLE_THREADS
typedef struct c11_ComputeThread c11_ComputeThread;
typedef struct {
@ -456,6 +458,8 @@ static void pk_ComputeThread__register(py_Ref mod) {
py_bindmethod(type, "eval", ComputeThread_eval);
}
#endif // PK_ENABLE_THREADS
static void pkpy_configmacros_add(py_Ref dict, const char* key, int val) {
assert(dict->type == tp_dict);
py_TValue tmp;
@ -508,11 +512,14 @@ void pk__add_module_pkpy() {
py_bindfunc(mod, "watchdog_end", pkpy_watchdog_end);
#endif
#if PK_ENABLE_THREADS
pk_ComputeThread__register(mod);
#endif
py_Ref configmacros = py_emplacedict(mod, py_name("configmacros"));
py_newdict(configmacros);
pkpy_configmacros_add(configmacros, "PK_ENABLE_OS", PK_ENABLE_OS);
pkpy_configmacros_add(configmacros, "PK_ENABLE_THREADS", PK_ENABLE_THREADS);
pkpy_configmacros_add(configmacros, "PK_ENABLE_DETERMINISM", PK_ENABLE_DETERMINISM);
pkpy_configmacros_add(configmacros, "PK_ENABLE_WATCHDOG", PK_ENABLE_WATCHDOG);
pkpy_configmacros_add(configmacros, "PK_GC_MIN_THRESHOLD", PK_GC_MIN_THRESHOLD);

View File

@ -145,7 +145,7 @@ static bool Random__init__(int argc, py_Ref argv) {
// do nothing
} else if(argc == 2) {
mt19937* ud = py_touserdata(py_arg(0));
if(!py_isnone(&argv[1])){
if(!py_isnone(&argv[1])) {
PY_CHECK_ARG_TYPE(1, tp_int);
py_i64 seed = py_toint(py_arg(1));
mt19937__seed(ud, (uint32_t)seed);
@ -328,4 +328,32 @@ __ERROR:
#undef MATRIX_A
#undef UPPER_MASK
#undef LOWER_MASK
#undef ADD_INST_BOUNDMETHOD
#undef ADD_INST_BOUNDMETHOD
void py_newRandom(py_OutRef out) {
py_Type type = py_gettype("random", py_name("Random"));
assert(type != 0);
mt19937* ud = py_newobject(out, type, 0, sizeof(mt19937));
mt19937__ctor(ud);
}
void py_Random_seed(py_Ref self, py_i64 seed) {
mt19937* ud = py_touserdata(self);
mt19937__seed(ud, (uint32_t)seed);
}
py_f64 py_Random_random(py_Ref self) {
mt19937* ud = py_touserdata(self);
return mt19937__random(ud);
}
py_f64 py_Random_uniform(py_Ref self, py_f64 a, py_f64 b) {
mt19937* ud = py_touserdata(self);
return mt19937__uniform(ud, a, b);
}
py_i64 py_Random_randint(py_Ref self, py_i64 a, py_i64 b) {
mt19937* ud = py_touserdata(self);
if(a > b) { c11__abort("randint(a, b): a must be less than or equal to b"); }
return mt19937__randint(ud, a, b);
}

View File

@ -1,6 +1,4 @@
#include "pocketpy/objects/base.h"
#include "pocketpy/pocketpy.h"
#include <stdlib.h>
static bool traceback_format_exc(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);

View File

@ -58,25 +58,23 @@ c11_vec2i py_tovec2i(py_Ref self) {
void py_newvec3(py_OutRef out, c11_vec3 v) {
out->type = tp_vec3;
out->is_ptr = false;
c11_vec3* data = (c11_vec3*)(&out->extra);
*data = v;
out->_vec3 = v;
}
c11_vec3 py_tovec3(py_Ref self) {
assert(self->type == tp_vec3);
return *(c11_vec3*)(&self->extra);
return self->_vec3;
}
void py_newvec3i(py_OutRef out, c11_vec3i v) {
out->type = tp_vec3i;
out->is_ptr = false;
c11_vec3i* data = (c11_vec3i*)(&out->extra);
*data = v;
out->_vec3i = v;
}
c11_vec3i py_tovec3i(py_Ref self) {
assert(self->type == tp_vec3i);
return *(c11_vec3i*)(&self->extra);
return self->_vec3i;
}
c11_mat3x3* py_newmat3x3(py_OutRef out) {
@ -1013,6 +1011,14 @@ static bool color32__repr__(int argc, py_Ref argv) {
return true;
}
static bool color32__hash__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_color32 color = py_tocolor32(argv);
uint32_t* color_int = (uint32_t*)&color;
py_newint(py_retval(), *color_int);
return true;
}
static bool color32_ansi_fg(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
c11_color32 color = py_tocolor32(argv);
@ -1258,6 +1264,7 @@ void pk__add_module_vmath() {
py_bindmagic(color32, __repr__, color32__repr__);
py_bindmagic(color32, __eq__, color32__eq__);
py_bindmagic(color32, __ne__, color32__ne__);
py_bindmagic(color32, __hash__, color32__hash__);
py_bindproperty(color32, "r", color32__r, NULL);
py_bindproperty(color32, "g", color32__g, NULL);
py_bindproperty(color32, "b", color32__b, NULL);

View File

@ -2,6 +2,7 @@
#include "pocketpy/common/utils.h"
#include "pocketpy/pocketpy.h"
#include <stdint.h>
#include <assert.h>
void Bytecode__set_signed_arg(Bytecode* self, int arg) {
self->arg = (int16_t)arg;

View File

@ -4,21 +4,30 @@
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#define HASH_PROBE_0(__k, ok, i) \
ok = false; \
i = (uintptr_t)(__k)&self->mask; \
do { \
if(self->items[i].key == (__k)) { \
ok = true; \
break; \
} \
if(self->items[i].key == NULL) break; \
i = (5 * i + 1) & self->mask; \
} while(true);
#define HASH_PROBE_1(__k, ok, i) \
ok = false; \
i = (uintptr_t)(__k) & self->mask; \
i = (uintptr_t)(__k)&self->mask; \
while(self->items[i].key != NULL) { \
if(self->items[i].key == (__k)) { \
ok = true; \
break; \
} \
i = (i + 1) & self->mask; \
i = (5 * i + 1) & self->mask; \
}
#define HASH_PROBE_0 HASH_PROBE_1
static void NameDict__set_capacity_and_alloc_items(NameDict* self, int val) {
self->capacity = val;
self->critical_size = val * self->load_factor;
@ -101,18 +110,24 @@ bool NameDict__del(NameDict* self, py_Name key) {
self->items[i].key = NULL;
self->items[i].value = *py_NIL();
self->length--;
// tidy
uintptr_t pre_z = i;
uintptr_t z = (i + 1) & self->mask;
while(self->items[z].key != NULL) {
uintptr_t h = (uintptr_t)self->items[z].key & self->mask;
if(h != i) break;
// std::swap(_items[pre_z], _items[z]);
NameDict_KV tmp = self->items[pre_z];
self->items[pre_z] = self->items[z];
self->items[z] = tmp;
pre_z = z;
z = (z + 1) & self->mask;
/* tidy */
uint32_t posToRemove = i;
uint32_t posToShift = posToRemove;
while(true) {
posToShift = (5 * posToShift + 1) & self->mask;
if(self->items[posToShift].key == NULL) break;
uintptr_t hash_z = (uintptr_t)self->items[posToShift].key;
uintptr_t insertPos = hash_z & self->mask;
bool cond1 = insertPos <= posToRemove;
bool cond2 = posToRemove <= posToShift;
if((cond1 && cond2) ||
// chain wrapped around capacity
(posToShift < insertPos && (cond1 || cond2))) {
NameDict_KV tmp = self->items[posToRemove];
self->items[posToRemove] = self->items[posToShift];
self->items[posToShift] = tmp;
posToRemove = posToShift;
}
}
return true;
}

View File

@ -2,14 +2,16 @@
#include "pocketpy/pocketpy.h"
#include <assert.h>
void* PyObject__userdata(PyObject* self) { return self->flex + PK_OBJ_SLOTS_SIZE(self->slots); }
PK_INLINE void* PyObject__userdata(PyObject* self) {
return self->flex + PK_OBJ_SLOTS_SIZE(self->slots);
}
NameDict* PyObject__dict(PyObject* self) {
PK_INLINE NameDict* PyObject__dict(PyObject* self) {
assert(self->slots == -1);
return (NameDict*)(self->flex);
}
py_TValue* PyObject__slots(PyObject* self) {
PK_INLINE py_TValue* PyObject__slots(PyObject* self) {
assert(self->slots >= 0);
return (py_TValue*)(self->flex);
}

View File

@ -9,6 +9,8 @@ int64_t py_toint(py_Ref self) {
return self->_i64;
}
void* py_totrivial(py_Ref self) { return &self->_chars; }
double py_tofloat(py_Ref self) {
assert(self->type == tp_float);
return self->_f64;

View File

@ -30,9 +30,7 @@ void py_initialize() {
bool is_little_endian = *(char*)&x == 1;
if(!is_little_endian) c11__abort("is_little_endian != true");
// check py_TValue; 16 bytes to make py_arg() macro work
static_assert(sizeof(py_CFunction) <= 8, "sizeof(py_CFunction) > 8");
static_assert(sizeof(py_TValue) == 16, "sizeof(py_TValue) != 16");
static_assert(sizeof(py_TValue) == 24, "sizeof(py_TValue) != 24");
static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4");
pk_current_vm = pk_all_vm[0] = &pk_default_vm;
@ -47,6 +45,12 @@ void py_initialize() {
pk_initialized = true;
}
void* py_malloc(size_t size) { return PK_MALLOC(size); }
void* py_realloc(void* ptr, size_t size) { return PK_REALLOC(ptr, size); }
void py_free(void* ptr) { PK_FREE(ptr); }
py_GlobalRef py_True() { return &_True; }
py_GlobalRef py_False() { return &_False; }
@ -178,7 +182,7 @@ bool py_vectorcall(uint16_t argc, uint16_t kwargc) {
return VM__vectorcall(pk_current_vm, argc, kwargc, false) != RES_ERROR;
}
py_Ref py_retval() { return &pk_current_vm->last_retval; }
PK_INLINE py_Ref py_retval() { return &pk_current_vm->last_retval; }
bool py_pushmethod(py_Name name) {
bool ok = pk_loadmethod(py_peek(-1), name);
@ -223,7 +227,21 @@ bool pk_loadmethod(py_StackRef self, py_Name name) {
self_bak = *self;
}
py_Ref cls_var = py_tpfindname(type, name);
py_TypeInfo* ti = pk_typeinfo(type);
if(ti->getunboundmethod) {
bool ok = ti->getunboundmethod(self, name);
if(ok) {
assert(py_retval()->type == tp_nativefunc || py_retval()->type == tp_function);
self[0] = *py_retval();
self[1] = self_bak;
return true;
} else {
return false;
}
}
py_Ref cls_var = pk_tpfindname(ti, name);
if(cls_var != NULL) {
switch(cls_var->type) {
case tp_function:
@ -238,7 +256,7 @@ bool pk_loadmethod(py_StackRef self, py_Name name) {
break;
case tp_classmethod:
self[0] = *py_getslot(cls_var, 0);
self[1] = pk_typeinfo(type)->self;
self[1] = ti->self;
break;
default: c11__unreachable();
}

View File

@ -28,32 +28,8 @@ static void py_ModuleInfo__dtor(py_ModuleInfo* mi) {
c11_string__delete(mi->path);
}
static bool module__name__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_ModuleInfo* mi = py_touserdata(argv);
py_newstrv(py_retval(), c11_string__sv(mi->name));
return true;
}
static bool module__package__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_ModuleInfo* mi = py_touserdata(argv);
py_newstrv(py_retval(), c11_string__sv(mi->package));
return true;
}
static bool module__path__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_ModuleInfo* mi = py_touserdata(argv);
py_newstrv(py_retval(), c11_string__sv(mi->path));
return true;
}
py_Type pk_module__register() {
py_Type type = pk_newtype("module", tp_object, NULL, (py_Dtor)py_ModuleInfo__dtor, false, true);
py_bindproperty(type, "__name__", module__name__, NULL);
py_bindproperty(type, "__package__", module__package__, NULL);
py_bindproperty(type, "__path__", module__path__, NULL);
return type;
}
@ -85,6 +61,13 @@ py_Ref py_newmodule(const char* path) {
BinTree__set(&pk_current_vm->modules, (void*)path, py_retval());
py_GlobalRef retval = py_getmodule(path);
mi->self = retval;
// setup __name__
py_newstrv(py_emplacedict(retval, __name__), c11_string__sv(mi->name));
// setup __package__
py_newstrv(py_emplacedict(retval, __package__), c11_string__sv(mi->package));
// setup __path__
py_newstrv(py_emplacedict(retval, __path__), c11_string__sv(mi->path));
return retval;
}
@ -146,6 +129,15 @@ int py_import(const char* path_cstr) {
return true;
}
if(vm->callbacks.lazyimport) {
py_GlobalRef lazymod = vm->callbacks.lazyimport(path_cstr);
if(lazymod) {
c11__rtassert(py_istype(lazymod, tp_module));
py_assign(py_retval(), lazymod);
return 1;
}
}
// try import
c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
c11_string* filename = c11_string__new3("%s.py", slashed_path->data);

View File

@ -1,13 +1,8 @@
#include "pocketpy/pocketpy.h"
#include "pocketpy/objects/object.h"
#include "pocketpy/objects/iterator.h"
#include "pocketpy/interpreter/vm.h"
typedef struct array_iterator {
py_TValue* p;
int length;
int index;
} array_iterator;
int pk_arrayview(py_Ref self, py_TValue** p) {
if(self->type == tp_list) {
*p = py_list_data(self);
@ -46,18 +41,6 @@ bool pk_wrapper__arrayequal(py_Type type, int argc, py_Ref argv) {
return true;
}
bool pk_arrayiter(py_Ref val) {
py_TValue* p;
int length = pk_arrayview(val, &p);
if(length == -1) return TypeError("expected list or tuple, got %t", val->type);
array_iterator* ud = py_newobject(py_retval(), tp_array_iterator, 1, sizeof(array_iterator));
ud->p = p;
ud->length = length;
ud->index = 0;
py_setslot(py_retval(), 0, val); // keep a reference to the object
return true;
}
bool pk_arraycontains(py_Ref self, py_Ref val) {
py_TValue* p;
int length = pk_arrayview(self, &p);
@ -74,25 +57,39 @@ bool pk_arraycontains(py_Ref self, py_Ref val) {
return true;
}
static bool array_iterator__iter__(int argc, py_Ref argv) {
bool list_iterator__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
*py_retval() = *argv;
return true;
}
static bool array_iterator__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
array_iterator* ud = py_touserdata(argv);
if(ud->index < ud->length) {
*py_retval() = ud->p[ud->index++];
list_iterator* ud = py_touserdata(argv);
if(ud->index < ud->vec->length) {
py_TValue* res = c11__at(py_TValue, ud->vec, ud->index);
py_assign(py_retval(), res);
ud->index++;
return true;
}
return StopIteration();
}
py_Type pk_array_iterator__register() {
py_Type type = pk_newtype("array_iterator", tp_object, NULL, NULL, false, true);
py_bindmagic(type, __iter__, array_iterator__iter__);
py_bindmagic(type, __next__, array_iterator__next__);
bool tuple_iterator__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
tuple_iterator* ud = py_touserdata(argv);
if(ud->index < ud->length) {
py_assign(py_retval(), ud->p + ud->index);
ud->index++;
return true;
}
return StopIteration();
}
py_Type pk_list_iterator__register() {
py_Type type = pk_newtype("list_iterator", tp_object, NULL, NULL, false, true);
py_bindmagic(type, __iter__, pk_wrapper__self);
py_bindmagic(type, __next__, list_iterator__next__);
return type;
}
py_Type pk_tuple_iterator__register() {
py_Type type = pk_newtype("tuple_iterator", tp_object, NULL, NULL, false, true);
py_bindmagic(type, __iter__, pk_wrapper__self);
py_bindmagic(type, __next__, tuple_iterator__next__);
return type;
}

View File

@ -5,6 +5,16 @@
#include "pocketpy/interpreter/types.h"
#include "pocketpy/interpreter/vm.h"
typedef struct {
Dict* dict; // weakref for slot 0
Dict dict_backup;
DictEntry* curr;
DictEntry* end;
int mode; // 0: keys, 1: values, 2: items
} DictIterator;
#define Dict__step(x) ((x) < mask ? (x) + 1 : 0)
static uint32_t Dict__next_cap(uint32_t cap) {
switch(cap) {
case 7: return 17;
@ -51,19 +61,36 @@ static uint32_t Dict__next_cap(uint32_t cap) {
}
}
typedef struct {
DictEntry* curr;
DictEntry* end;
int mode; // 0: keys, 1: values, 2: items
} DictIterator;
static uint64_t Dict__hash_2nd(uint64_t key) {
// https://gist.github.com/badboy/6267743
key = (~key) + (key << 21); // key = (key << 21) - key - 1
key = key ^ (key >> 24);
key = (key + (key << 3)) + (key << 8); // key * 265
key = key ^ (key >> 14);
key = (key + (key << 2)) + (key << 4); // key * 21
key = key ^ (key >> 28);
key = key + (key << 31);
return key;
}
static void Dict__ctor(Dict* self, uint32_t capacity, int entries_capacity) {
self->length = 0;
self->capacity = capacity;
self->indices = PK_MALLOC(self->capacity * sizeof(DictIndex));
memset(self->indices, -1, self->capacity * sizeof(DictIndex));
size_t indices_size;
if(self->capacity < UINT16_MAX) {
self->index_is_short = true;
indices_size = self->capacity * sizeof(uint16_t);
self->null_index_value = UINT16_MAX;
} else {
self->index_is_short = false;
indices_size = self->capacity * sizeof(uint32_t);
self->null_index_value = UINT32_MAX;
}
self->indices = PK_MALLOC(indices_size);
memset(self->indices, -1, indices_size);
c11_vector__ctor(&self->entries, sizeof(DictEntry));
c11_vector__reserve(&self->entries, entries_capacity);
}
@ -75,70 +102,118 @@ static void Dict__dtor(Dict* self) {
c11_vector__dtor(&self->entries);
}
static bool Dict__try_get(Dict* self, py_TValue* key, DictEntry** out) {
py_i64 hash;
if(!py_hash(key, &hash)) return false;
int idx = (uint64_t)hash % self->capacity;
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
int idx2 = self->indices[idx]._[i];
if(idx2 == -1) continue;
static uint32_t Dict__get_index(Dict* self, uint32_t index) {
if(self->index_is_short) {
uint16_t* indices = self->indices;
return indices[index];
} else {
uint32_t* indices = self->indices;
return indices[index];
}
}
static void Dict__swap_null_index(Dict* self, uint32_t pre_z, uint32_t z) {
if(self->index_is_short) {
uint16_t* indices = self->indices;
assert(indices[pre_z] == UINT16_MAX);
indices[pre_z] = indices[z];
indices[z] = UINT16_MAX;
} else {
uint32_t* indices = self->indices;
assert(indices[pre_z] == UINT32_MAX);
indices[pre_z] = indices[z];
indices[z] = UINT32_MAX;
}
}
static void Dict__set_index(Dict* self, uint32_t index, uint32_t value) {
if(self->index_is_short) {
uint16_t* indices = self->indices;
indices[index] = (uint16_t)value;
} else {
uint32_t* indices = self->indices;
indices[index] = value;
}
}
static bool Dict__probe(Dict* self,
py_TValue* key,
uint64_t* p_hash,
uint32_t* p_idx,
DictEntry** p_entry) {
py_i64 h_user;
if(!py_hash(key, &h_user)) return false;
if(py_isstr(key)) {
*p_hash = (uint64_t)h_user;
} else {
*p_hash = Dict__hash_2nd((uint64_t)h_user);
}
uint32_t mask = self->capacity - 1;
uint32_t idx = (*p_hash) % self->capacity;
while(true) {
uint32_t idx2 = Dict__get_index(self, idx);
if(idx2 == self->null_index_value) break;
DictEntry* entry = c11__at(DictEntry, &self->entries, idx2);
if(entry->hash == (uint64_t)hash) {
if(entry->hash == (*p_hash)) {
int res = py_equal(&entry->key, key);
if(res == 1) {
*out = entry;
*p_idx = idx;
*p_entry = entry;
return true;
}
if(res == -1) return false; // error
}
// try next index
idx = Dict__step(idx);
}
*out = NULL;
// not found
*p_idx = idx;
*p_entry = NULL;
return true;
}
static bool Dict__try_get(Dict* self, py_TValue* key, DictEntry** out) {
uint64_t hash;
uint32_t idx;
return Dict__probe(self, key, &hash, &idx, out);
}
static void Dict__clear(Dict* self) {
memset(self->indices, -1, self->capacity * sizeof(DictIndex));
size_t indices_size = self->index_is_short ? self->capacity * sizeof(uint16_t)
: self->capacity * sizeof(uint32_t);
memset(self->indices, -1, indices_size);
c11_vector__clear(&self->entries);
self->length = 0;
}
static void Dict__rehash_2x(Dict* self) {
Dict old_dict = *self;
uint32_t new_capacity = self->capacity;
__RETRY:
// use next capacity
new_capacity = Dict__next_cap(new_capacity);
uint32_t new_capacity = Dict__next_cap(old_dict.capacity);
uint32_t mask = new_capacity - 1;
// create a new dict with new capacity
Dict__ctor(self, new_capacity, old_dict.entries.capacity);
// move entries from old dict to new dict
for(int i = 0; i < old_dict.entries.length; i++) {
DictEntry* old_entry = c11__at(DictEntry, &old_dict.entries, i);
if(py_isnil(&old_entry->key)) continue;
int idx = old_entry->hash % new_capacity;
bool success = false;
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
int idx2 = self->indices[idx]._[i];
if(idx2 == -1) {
// insert new entry (empty slot)
if(py_isnil(&old_entry->key)) continue; // skip deleted
uint32_t idx = old_entry->hash % new_capacity;
while(true) {
uint32_t idx2 = Dict__get_index(self, idx);
if(idx2 == self->null_index_value) {
c11_vector__push(DictEntry, &self->entries, *old_entry);
self->indices[idx]._[i] = self->entries.length - 1;
Dict__set_index(self, idx, self->entries.length - 1);
self->length++;
success = true;
break;
}
}
if(!success) {
Dict__dtor(self);
goto __RETRY;
// try next index
idx = Dict__step(idx);
}
}
// done
Dict__dtor(&old_dict);
}
static void Dict__compact_entries(Dict* self) {
int* mappings = PK_MALLOC(self->entries.length * sizeof(int));
uint32_t* mappings = PK_MALLOC(self->entries.length * sizeof(uint32_t));
int n = 0;
for(int i = 0; i < self->entries.length; i++) {
@ -153,96 +228,97 @@ static void Dict__compact_entries(Dict* self) {
}
self->entries.length = n;
// update indices
for(uint32_t i = 0; i < self->capacity; i++) {
for(int j = 0; j < PK_DICT_MAX_COLLISION; j++) {
int idx = self->indices[i]._[j];
if(idx == -1) continue;
self->indices[i]._[j] = mappings[idx];
}
for(uint32_t idx = 0; idx < self->capacity; idx++) {
uint32_t idx2 = Dict__get_index(self, idx);
if(idx2 == self->null_index_value) continue;
Dict__set_index(self, idx, mappings[idx2]);
}
PK_FREE(mappings);
}
static bool Dict__set(Dict* self, py_TValue* key, py_TValue* val) {
py_i64 hash;
if(!py_hash(key, &hash)) return false;
int idx = (uint64_t)hash % self->capacity;
int bad_hash_count = 0;
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
int idx2 = self->indices[idx]._[i];
if(idx2 == -1) {
// insert new entry
DictEntry* new_entry = c11_vector__emplace(&self->entries);
new_entry->hash = (uint64_t)hash;
new_entry->key = *key;
new_entry->val = *val;
self->indices[idx]._[i] = self->entries.length - 1;
self->length++;
return true;
}
uint64_t hash;
uint32_t idx;
DictEntry* entry;
if(!Dict__probe(self, key, &hash, &idx, &entry)) return false;
if(entry) {
// update existing entry
DictEntry* entry = c11__at(DictEntry, &self->entries, idx2);
// check if they have the same hash
if(entry->hash == (uint64_t)hash) {
// check if they are equal
int res = py_equal(&entry->key, key);
if(res == 1) {
entry->val = *val;
return true;
}
if(res == -1) return false; // error
// res == 0
bad_hash_count++;
}
entry->val = *val;
return true;
}
// no empty slot found
if(bad_hash_count == PK_DICT_MAX_COLLISION) {
// all `PK_DICT_MAX_COLLISION` slots have the same hash but different keys
// we are unable to solve this collision via rehashing
return RuntimeError("dict: %d/%d/%d: maximum collision reached (hash=%i)",
self->entries.length,
self->entries.capacity,
self->capacity,
hash);
}
if(self->capacity >= (uint32_t)self->entries.length * 10) {
return RuntimeError("dict: %d/%d/%d: minimum load factor reached",
self->entries.length,
self->entries.capacity,
self->capacity);
}
Dict__rehash_2x(self);
return Dict__set(self, key, val);
// insert new entry
DictEntry* new_entry = c11_vector__emplace(&self->entries);
new_entry->hash = hash;
new_entry->key = *key;
new_entry->val = *val;
Dict__set_index(self, idx, self->entries.length - 1);
self->length++;
// check if we need to rehash
float load_factor = (float)self->length / self->capacity;
if(load_factor > (self->index_is_short ? 0.3f : 0.4f)) Dict__rehash_2x(self);
return true;
}
/// Delete an entry from the dict.
/// -1: error, 0: not found, 1: found and deleted
static int Dict__pop(Dict* self, py_Ref key) {
py_i64 hash;
if(!py_hash(key, &hash)) return -1;
int idx = (uint64_t)hash % self->capacity;
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) {
int idx2 = self->indices[idx]._[i];
if(idx2 == -1) continue;
DictEntry* entry = c11__at(DictEntry, &self->entries, idx2);
if(entry->hash == (uint64_t)hash) {
int res = py_equal(&entry->key, key);
if(res == 1) {
*py_retval() = entry->val;
py_newnil(&entry->key);
self->indices[idx]._[i] = -1;
self->length--;
if(self->length < self->entries.length / 2) Dict__compact_entries(self);
return 1;
}
if(res == -1) return -1; // error
// Dict__log_index(self, "before pop");
uint64_t hash;
uint32_t idx;
DictEntry* entry;
if(!Dict__probe(self, key, &hash, &idx, &entry)) return -1;
if(!entry) return 0; // not found
// found the entry, delete and return it
py_assign(py_retval(), &entry->val);
Dict__set_index(self, idx, self->null_index_value);
py_newnil(&entry->key);
py_newnil(&entry->val);
self->length--;
/* tidy */
// https://github.com/OpenHFT/Chronicle-Map/blob/820573a68471509ffc1b0584454f4a67c0be1b84/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java#L156
uint32_t mask = self->capacity - 1;
uint32_t posToRemove = idx;
uint32_t posToShift = posToRemove;
// int probe_count = 0;
// int swap_count = 0;
while(true) {
posToShift = Dict__step(posToShift);
uint32_t idx_z = Dict__get_index(self, posToShift);
if(idx_z == self->null_index_value) break;
uint64_t hash_z = c11__at(DictEntry, &self->entries, idx_z)->hash;
uint32_t insertPos = (uint64_t)hash_z % self->capacity;
// the following condition essentially means circular permutations
// of three (r = posToRemove, s = posToShift, i = insertPos)
// positions are accepted:
// [...i..r...s.] or
// [...r..s...i.] or
// [...s..i...r.]
bool cond1 = insertPos <= posToRemove;
bool cond2 = posToRemove <= posToShift;
if((cond1 && cond2) ||
// chain wrapped around capacity
(posToShift < insertPos && (cond1 || cond2))) {
Dict__swap_null_index(self, posToRemove, posToShift);
posToRemove = posToShift;
// swap_count++;
}
// probe_count++;
}
return 0;
// printf("Dict__pop: probe_count=%d, swap_count=%d\n", probe_count, swap_count);
// compact entries if necessary
if(self->entries.length > 16 && (self->length < self->entries.length >> 1)) {
Dict__compact_entries(self); // compact entries
}
// Dict__log_index(self, "after pop");
return 1;
}
static void DictIterator__ctor(DictIterator* self, Dict* dict, int mode) {
assert(mode >= 0 && mode <= 2);
self->dict = dict;
self->dict_backup = *dict; // backup the dict
self->curr = dict->entries.data;
self->end = self->curr + dict->entries.length;
self->mode = mode;
@ -257,18 +333,22 @@ static DictEntry* DictIterator__next(DictIterator* self) {
return retval;
}
static bool DictIterator__modified(DictIterator* self) {
return memcmp(self->dict, &self->dict_backup, sizeof(Dict)) != 0;
}
///////////////////////////////
static bool dict__new__(int argc, py_Ref argv) {
py_Type cls = py_totype(argv);
int slots = cls == tp_dict ? 0 : -1;
Dict* ud = py_newobject(py_retval(), cls, slots, sizeof(Dict));
Dict__ctor(ud, 7, 8);
Dict__ctor(ud, 17, 4);
return true;
}
void py_newdict(py_OutRef out) {
Dict* ud = py_newobject(out, tp_dict, 0, sizeof(Dict));
Dict__ctor(ud, 7, 8);
Dict__ctor(ud, 17, 4);
}
static bool dict__init__(int argc, py_Ref argv) {
@ -426,12 +506,17 @@ static bool dict_copy(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv);
Dict* new_dict = py_newobject(py_retval(), tp_dict, 0, sizeof(Dict));
new_dict->capacity = self->capacity;
new_dict->length = self->length;
new_dict->capacity = self->capacity;
new_dict->null_index_value = self->null_index_value;
new_dict->index_is_short = self->index_is_short;
// copy entries
new_dict->entries = c11_vector__copy(&self->entries);
// copy indices
new_dict->indices = PK_MALLOC(new_dict->capacity * sizeof(DictIndex));
memcpy(new_dict->indices, self->indices, new_dict->capacity * sizeof(DictIndex));
size_t indices_size = self->index_is_short ? self->capacity * sizeof(uint16_t)
: self->capacity * sizeof(uint32_t);
new_dict->indices = PK_MALLOC(indices_size);
memcpy(new_dict->indices, self->indices, indices_size);
return true;
}
@ -525,9 +610,10 @@ py_Type pk_dict__register() {
}
//////////////////////////
static bool dict_items__next__(int argc, py_Ref argv) {
bool dict_items__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
DictIterator* iter = py_touserdata(py_arg(0));
if(DictIterator__modified(iter)) return RuntimeError("dictionary modified during iteration");
DictEntry* entry = (DictIterator__next(iter));
if(entry) {
switch(iter->mode) {
@ -641,4 +727,4 @@ bool py_dict_apply(py_Ref self, bool (*f)(py_Ref, py_Ref, void*), void* ctx) {
return true;
}
#undef PK_DICT_MAX_COLLISION
#undef Dict__step

View File

@ -3,6 +3,7 @@
#include "pocketpy/common/utils.h"
#include "pocketpy/interpreter/vm.h"
#include "pocketpy/interpreter/types.h"
#include "pocketpy/objects/iterator.h"
#include "pocketpy/common/sstream.h"
void py_newlist(py_OutRef out) {
@ -394,7 +395,11 @@ static bool list_sort(int argc, py_Ref argv) {
static bool list__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
return pk_arrayiter(argv);
list_iterator* ud = py_newobject(py_retval(), tp_list_iterator, 1, sizeof(list_iterator));
ud->vec = py_touserdata(argv);
ud->index = 0;
py_setslot(py_retval(), 0, argv); // keep a reference to the object
return true;
}
static bool list__contains__(int argc, py_Ref argv) {

View File

@ -175,6 +175,32 @@ static bool int__mod__(int argc, py_Ref argv) {
return true;
}
static bool float__mod__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
py_f64 lhs = py_tofloat(&argv[0]);
py_f64 rhs;
if(try_castfloat(&argv[1], &rhs)) {
if(rhs == 0.0) return ZeroDivisionError("float modulo by zero");
py_newfloat(py_retval(), fmod(lhs, rhs));
return true;
}
py_newnotimplemented(py_retval());
return true;
}
static bool float__rmod__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
py_f64 rhs = py_tofloat(&argv[0]);
py_f64 lhs;
if(try_castfloat(&argv[1], &lhs)) {
if(rhs == 0.0) return ZeroDivisionError("float modulo by zero");
py_newfloat(py_retval(), fmod(lhs, rhs));
return true;
}
py_newnotimplemented(py_retval());
return true;
}
static bool int__divmod__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_int);
@ -245,24 +271,6 @@ static bool float__repr__(int argc, py_Ref argv) {
return true;
}
union c11_8bytes {
py_i64 _i64;
py_f64 _f64;
union {
uint32_t upper;
uint32_t lower;
} bits;
};
static py_i64 c11_8bytes__hash(union c11_8bytes u) {
// https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
const uint32_t C = 2654435761;
u.bits.upper *= C;
u.bits.lower *= C;
return u._i64;
}
static bool int__hash__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_assign(py_retval(), argv);
@ -272,8 +280,9 @@ static bool int__hash__(int argc, py_Ref argv) {
static bool float__hash__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_f64 val = py_tofloat(&argv[0]);
union c11_8bytes u = {._f64 = val};
py_newint(py_retval(), c11_8bytes__hash(u));
py_i64 h_user;
memcpy(&h_user, &val, sizeof(py_f64));
py_newint(py_retval(), h_user);
return true;
}
@ -525,6 +534,10 @@ void pk_number__register() {
py_bindmagic(tp_int, __mod__, int__mod__);
py_bindmagic(tp_int, __divmod__, int__divmod__);
// fmod
py_bindmagic(tp_float, __mod__, float__mod__);
py_bindmagic(tp_float, __rmod__, float__rmod__);
// int.__invert__ & int.<BITWISE OP>
py_bindmagic(tp_int, __invert__, int__invert__);

View File

@ -1,4 +1,5 @@
#include "pocketpy/interpreter/typeinfo.h"
#include "pocketpy/interpreter/bindings.h"
#include "pocketpy/interpreter/vm.h"
#include "pocketpy/objects/base.h"
#include "pocketpy/pocketpy.h"
@ -77,12 +78,39 @@ bool py_iter(py_Ref val) {
int py_next(py_Ref val) {
VM* vm = pk_current_vm;
py_Ref tmp = py_tpfindmagic(val->type, __next__);
if(!tmp) {
TypeError("'%t' object is not an iterator", val->type);
return -1;
switch(val->type) {
case tp_generator:
if(generator__next__(1, val)) return 1;
break;
case tp_array2d_like_iterator:
if(array2d_like_iterator__next__(1, val)) return 1;
break;
case tp_list_iterator:
if(list_iterator__next__(1, val)) return 1;
break;
case tp_tuple_iterator:
if(tuple_iterator__next__(1, val)) return 1;
break;
case tp_dict_iterator:
if(dict_items__next__(1, val)) return 1;
break;
case tp_range_iterator:
if(range_iterator__next__(1, val)) return 1;
break;
case tp_str_iterator:
if(str_iterator__next__(1, val)) return 1;
break;
default: {
py_Ref tmp = py_tpfindmagic(val->type, __next__);
if(!tmp) {
TypeError("'%t' object is not an iterator", val->type);
return -1;
}
if(py_call(tmp, 1, val)) return 1;
break;
}
}
if(py_call(tmp, 1, val)) return 1;
if(vm->curr_exception.type == tp_StopIteration) {
vm->last_retval = vm->curr_exception;
py_clearexc(NULL);
@ -94,6 +122,8 @@ int py_next(py_Ref val) {
bool py_getattr(py_Ref self, py_Name name) {
// https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
py_TypeInfo* ti = pk_typeinfo(self->type);
if(ti->getattribute) return ti->getattribute(self, name);
py_Ref cls_var = pk_tpfindname(ti, name);
if(cls_var) {
// handle descriptor
@ -184,8 +214,10 @@ bool py_getattr(py_Ref self, py_Name name) {
}
bool py_setattr(py_Ref self, py_Name name, py_Ref val) {
py_Type type = self->type;
py_Ref cls_var = py_tpfindname(type, name);
py_TypeInfo* ti = pk_typeinfo(self->type);
if(ti->setattribute) return ti->setattribute(self, name, val);
py_Ref cls_var = pk_tpfindname(ti, name);
if(cls_var) {
// handle descriptor
if(py_istype(cls_var, tp_property)) {
@ -211,6 +243,9 @@ bool py_setattr(py_Ref self, py_Name name, py_Ref val) {
}
bool py_delattr(py_Ref self, py_Name name) {
py_TypeInfo* ti = pk_typeinfo(self->type);
if(ti->delattribute) return ti->delattribute(self, name);
if(self->is_ptr && self->_obj->slots == -1) {
if(py_deldict(self, name)) return true;
return AttributeError(self, name);

View File

@ -68,9 +68,9 @@ static bool range_iterator__new__(int argc, py_Ref argv) {
return true;
}
static bool range_iterator__next__(int argc, py_Ref argv) {
bool range_iterator__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
RangeIterator* ud = py_touserdata(py_arg(0));
RangeIterator* ud = py_touserdata(argv);
if(ud->range.step > 0) {
if(ud->current >= ud->range.stop) return StopIteration();
} else {

View File

@ -9,7 +9,7 @@
void py_newstr(py_OutRef out, const char* data) { py_newstrv(out, (c11_sv){data, strlen(data)}); }
char* py_newstrn(py_OutRef out, int size) {
if(size < 8) {
if(size < 16) {
out->type = tp_str;
out->is_ptr = false;
c11_string* ud = (c11_string*)(&out->extra);
@ -327,7 +327,7 @@ static bool str_split(int argc, py_Ref argv) {
if(argc > 2) return TypeError("split() takes at most 2 arguments");
if(argc == 1) {
// sep = None
res = c11_sv__split(self, ' ');
res = c11_sv__splitwhitespace(self);
discard_empty = true;
}
if(argc == 2) {
@ -513,7 +513,7 @@ py_Type pk_str__register() {
return type;
}
static bool str_iterator__next__(int argc, py_Ref argv) {
bool str_iterator__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
int* ud = py_touserdata(&argv[0]);
int size;

View File

@ -3,6 +3,7 @@
#include "pocketpy/common/utils.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/objects/object.h"
#include "pocketpy/objects/iterator.h"
#include "pocketpy/interpreter/vm.h"
py_ObjectRef py_newtuple(py_OutRef out, int n) {
@ -144,7 +145,12 @@ static bool tuple__lt__(int argc, py_Ref argv) {
static bool tuple__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
return pk_arrayiter(argv);
tuple_iterator* ud = py_newobject(py_retval(), tp_tuple_iterator, 1, sizeof(tuple_iterator));
ud->p = py_tuple_data(argv);
ud->length = py_tuple_len(argv);
ud->index = 0;
py_setslot(py_retval(), 0, argv); // keep a reference to the object
return true;
}
static bool tuple__contains__(int argc, py_Ref argv) {

View File

@ -8,12 +8,12 @@ py_Ref py_getreg(int i) { return pk_current_vm->reg + i; }
void py_setreg(int i, py_Ref val) { pk_current_vm->reg[i] = *val; }
py_Ref py_getdict(py_Ref self, py_Name name) {
PK_INLINE py_Ref py_getdict(py_Ref self, py_Name name) {
assert(self && self->is_ptr);
return NameDict__try_get(PyObject__dict(self->_obj), name);
}
void py_setdict(py_Ref self, py_Name name, py_Ref val) {
PK_INLINE void py_setdict(py_Ref self, py_Name name, py_Ref val) {
assert(self && self->is_ptr);
NameDict__set(PyObject__dict(self->_obj), name, val);
}

View File

@ -4,7 +4,7 @@
#include "pocketpy/objects/object.h"
#include "pocketpy/interpreter/vm.h"
bool py_istype(py_Ref self, py_Type type) { return self->type == type; }
PK_INLINE bool py_istype(py_Ref self, py_Type type) { return self->type == type; }
bool py_checktype(py_Ref self, py_Type type) {
if(self->type == type) return true;

View File

@ -13,6 +13,13 @@ void py_newint(py_OutRef out, py_i64 val) {
out->_i64 = val;
}
void py_newtrivial(py_OutRef out, py_Type type, void* data, int size) {
out->type = type;
out->is_ptr = false;
assert(size <= 16);
memcpy(&out->_chars, data, size);
}
void py_newfloat(py_OutRef out, py_f64 val) {
out->type = tp_float;
out->is_ptr = false;

View File

@ -1,6 +1,5 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
@ -20,7 +19,7 @@ static char* read_file(const char* path) {
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
char* buffer = malloc(size + 1);
char* buffer = PK_MALLOC(size + 1);
size = fread(buffer, 1, size, file);
buffer[size] = 0;
return buffer;
@ -86,7 +85,7 @@ int main(int argc, char** argv) {
char* source = read_file(filename);
if(source) {
if(!py_exec(source, filename, EXEC_MODE, NULL)) py_printexc();
free(source);
PK_FREE(source);
}
}

View File

@ -109,3 +109,8 @@ assert abs(0.0) == 0.0
# exit(1)
# except ValueError:
# pass
assert eq(10 % 4, 2)
assert eq(10.5 % 4, 2.5)
assert eq(10 % 4.5, 1.0)
assert eq(10.5 % 4.5, 1.5)

View File

@ -57,6 +57,9 @@ assert 'aa bb cccc'.split('cc') == ['aa bb ', '', '']
assert '.a.b.'.split('.') == ['', 'a', 'b', '']
assert '.a...b.'.split('.') == ['', 'a', '', '', 'b', '']
# https://github.com/pocketpy/pocketpy/issues/378
assert "a b \n c\td".split() == ['a', 'b', 'c', 'd']
try:
'a'.split('')
exit(1)
@ -228,3 +231,7 @@ assert f"{(1, 2, 3)}" == "(1, 2, 3)"
# stack=[1,2,3,4]
# assert f"{stack[2:]}" == '[3, 4]'
assert id('1' * 16) is not None
assert id('1' * 15) is None

View File

@ -115,30 +115,7 @@ assert a.pop(1) == 2
assert a.pop(1, None) is None
n = 2 ** 17
a = {}
for i in range(n):
a[str(i)] = i
for i in range(n):
y = a[str(i)]
for i in range(n):
del a[str(i)]
# namedict delete test
# class A: pass
# a = A()
# b = ['0', '1']
# for i in range(len(data)):
# z = data[i]
# setattr(a, str(z), i)
# b.append(z)
# if i % 3 == 0:
# y = b.pop()
# delattr(a, y)
# test getitem
d = {}
for i in range(-1000, 1000):
d[i] = i
@ -155,3 +132,37 @@ assert list(d) == ['1', 222, '333']
assert list(d.keys()) == ['1', 222, '333']
assert list(d.values()) == [1, 2, 3]
assert list(d.items()) == [('1', 1), (222, 2), ('333', 3)]
# test del
n = 2 ** 17
a = {}
for i in range(n):
a[str(i)] = i
for i in range(n):
del a[str(i)]
assert len(a) == 0
# test del with int keys
if 0:
n = 2 ** 17
a = {}
for i in range(n):
a[i] = i
for i in range(n):
del a[i]
assert len(a) == 0
#######################
# namedict delete test
class A: pass
a = A()
b = ['0', '1']
for i in range(len(data)):
z = data[i]
setattr(a, str(z), i)
b.append(z)
if i % 3 == 0:
y = b.pop()
delattr(a, y)

View File

@ -126,7 +126,13 @@ class MyClass:
b, c = 1, 2
d = b + c
def __init__(self, m, n) -> None:
self.m: int = m
self.n: float = n
assert MyClass.a == (1, 2, 3)
assert MyClass.b == 1
assert MyClass.c == 2
assert MyClass.d == 3
assert MyClass(1, 2).m == 1

View File

@ -74,3 +74,5 @@ exec(code, {'x': 42, 'res': res})
assert res == [42, 42]
assert x == 33
# test removing trailing newlines
assert eval('[1, 2, 3]\n \n') == [1, 2, 3]

View File

@ -157,4 +157,16 @@ assert a == 5
a, b, c = (1, 2, 3) if True else (4, 5, 6)
assert a == 1
assert b == 2
assert c == 3
assert c == 3
# https://github.com/pocketpy/pocketpy/issues/376
xs = [0]
res = []
for x in xs:
res.append(x)
if x == 100:
break
xs.append(x+1)
assert res == list(range(101))
assert xs == res

View File

@ -1,4 +1,11 @@
from pkpy import ComputeThread
from pkpy import configmacros
if configmacros['PK_ENABLE_THREADS'] == 1:
from pkpy import ComputeThread
else:
print('threads is not enabled, skipping test...')
exit()
import time
thread_1 = ComputeThread(1)

View File

@ -103,9 +103,4 @@ class A:
bad_dict = {A(): 1, A(): 2, A(): 3, A(): 4}
assert len(bad_dict) == 4
try:
bad_dict[A()] = 5 # error
exit(1)
except RuntimeError as e:
assert 'maximum collision reached' in str(e)