Merge branch 'main' into pr/379

This commit is contained in:
blueloveTH 2025-08-09 12:33:46 +08:00
commit 9462b55aa4
96 changed files with 4247 additions and 1584 deletions

View File

@ -36,7 +36,7 @@ jobs:
shell: bash shell: bash
run: | run: |
mkdir -p output/x86_64 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 main.exe output/x86_64
cp pocketpy.dll output/x86_64 cp pocketpy.dll output/x86_64
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
@ -76,7 +76,7 @@ jobs:
run: | run: |
python scripts/check_pragma_once.py include python scripts/check_pragma_once.py include
mkdir -p output/x86_64 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 python scripts/run_tests.py
cp main output/x86_64 cp main output/x86_64
cp libpocketpy.so output/x86_64 cp libpocketpy.so output/x86_64
@ -96,7 +96,7 @@ jobs:
submodules: recursive submodules: recursive
- name: Compile and Test - name: Compile and Test
run: | 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 python scripts/run_tests.py
- name: Benchmark - name: Benchmark
run: python scripts/run_tests.py benchmark run: python scripts/run_tests.py benchmark
@ -162,7 +162,7 @@ jobs:
uses: jirutka/setup-alpine@v1 uses: jirutka/setup-alpine@v1
with: with:
arch: ${{ matrix.arch }} 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 - name: Build and Test
run: | run: |
echo "Building for architecture: ${{ matrix.arch }}" echo "Building for architecture: ${{ matrix.arch }}"

2
.gitignore vendored
View File

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

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) project(libhv_bindings)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)

View File

@ -5,12 +5,12 @@ project(lz4)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON) 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 ${CMAKE_CURRENT_LIST_DIR}/lz4/lib
) )
target_include_directories(lz4 INTERFACE target_include_directories(${PROJECT_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}
) )

View File

@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.10)
project(pocketpy) 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 11)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
@ -13,8 +17,7 @@ endif()
if(MSVC) if(MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8 /jumptablerdata") 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 /experimental:c11atomics)
add_compile_options(/wd4267 /wd4244 /wd4146)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox") 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) if(PK_ENABLE_OS)
add_definitions(-DPK_ENABLE_OS=1) 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() endif()
if(PK_ENABLE_DETERMINISM) if(PK_ENABLE_DETERMINISM)
add_subdirectory(3rd/dmath/dmath) add_subdirectory(3rd/dmath/dmath)
add_definitions(-DPK_ENABLE_DETERMINISM=1) add_definitions(-DPK_ENABLE_DETERMINISM=1)
else()
add_definitions(-DPK_ENABLE_DETERMINISM=0)
endif() endif()
if(PK_ENABLE_WATCHDOG) if(PK_ENABLE_WATCHDOG)
add_definitions(-DPK_ENABLE_WATCHDOG=1) add_definitions(-DPK_ENABLE_WATCHDOG=1)
else()
add_definitions(-DPK_ENABLE_WATCHDOG=0)
endif() endif()
if(PK_ENABLE_CUSTOM_SNAME) if(PK_ENABLE_CUSTOM_SNAME)
add_definitions(-DPK_ENABLE_CUSTOM_SNAME=1) 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() endif()
@ -73,6 +114,12 @@ if(PK_BUILD_MODULE_LIBHV)
add_definitions(-DPK_BUILD_MODULE_LIBHV) add_definitions(-DPK_BUILD_MODULE_LIBHV)
endif() 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) if(PK_BUILD_SHARED_LIB)
message(">> Building shared library") message(">> Building shared library")
add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC}) add_library(${PROJECT_NAME} SHARED ${POCKETPY_SRC})
@ -104,8 +151,10 @@ if(PK_ENABLE_DETERMINISM)
endif() endif()
endif() endif()
if(PK_ENABLE_THREADS)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} Threads::Threads) target_link_libraries(${PROJECT_NAME} Threads::Threads)
endif()
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
if(NOT PK_ENABLE_DETERMINISM) if(NOT PK_ENABLE_DETERMINISM)
@ -134,3 +183,12 @@ endif()
if(PK_BUILD_MODULE_LIBHV) if(PK_BUILD_MODULE_LIBHV)
target_link_libraries(${PROJECT_NAME} libhv_bindings) target_link_libraries(${PROJECT_NAME} libhv_bindings)
endif() 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() endif()
# system features # 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_DETERMINISM "" OFF)
option(PK_ENABLE_WATCHDOG "" OFF) option(PK_ENABLE_WATCHDOG "" OFF)
option(PK_ENABLE_CUSTOM_SNAME "" OFF) option(PK_ENABLE_CUSTOM_SNAME "" OFF)
option(PK_ENABLE_MIMALLOC "" OFF)
# modules # modules
option(PK_BUILD_MODULE_LZ4 "" OFF) option(PK_BUILD_MODULE_LZ4 "" OFF)
option(PK_BUILD_MODULE_LIBHV "" 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 # 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).

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. 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 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 + [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 ## Supported Platforms
@ -186,6 +187,7 @@ And these are the results of the primes benchmark on Intel i5-12400F, WSL (Ubunt
| | Description | | | 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. | | [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. | | [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. | | [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 \ -DPK_BUILD_SHARED_LIB=ON \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DPK_BUILD_MODULE_LZ4=ON \ -DPK_BUILD_MODULE_LZ4=ON \
-DPK_BUILD_MODULE_LIBHV=ON -DPK_BUILD_MODULE_LIBHV=ON \
-DPK_BUILD_CUTE_PNG=ON
cmake --build . --config Release cmake --build . --config Release

View File

@ -20,7 +20,7 @@ assert config in ['Debug', 'Release', 'RelWithDebInfo']
os.chdir("build") 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 assert code == 0
code = os.system(f"cmake --build . --config {config} -j 4") code = os.system(f"cmake --build . --config {config} -j 4")
assert code == 0 assert code == 0

View File

@ -5,3 +5,4 @@
-Iinclude/ -Iinclude/
-I3rd/lz4/ -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 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 + [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 ## 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 #pragma once
#include "pocketpy/common/vector.h" #include "pocketpy/common/vector.h"
typedef struct c11_chunkedvector_chunk { typedef struct c11_chunkedvector_chunk {
int length; int length;
int capacity; int capacity;

View File

@ -1,7 +1,10 @@
#pragma once #pragma once
#include "pocketpy/config.h"
#include <stdint.h> #include <stdint.h>
#if PK_ENABLE_OS
typedef void* c11_socket_handler; typedef void* c11_socket_handler;
enum c11_address_family { C11_AF_INET = 2 }; 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); int c11_socket_set_block(c11_socket_handler socket, int flag);
c11_socket_handler c11_socket_invalid_socket_handler(); c11_socket_handler c11_socket_invalid_socket_handler();
int c11_socket_get_last_error(); 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__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__split2(c11_sv self, c11_sv sep);
c11_vector /* T=c11_sv */ c11_sv__splitwhitespace(c11_sv self);
// misc // misc
int c11__unicode_index_to_byte(const char* data, int i); int c11__unicode_index_to_byte(const char* data, int i);

View File

@ -1,5 +1,9 @@
#pragma once #pragma once
#include "pocketpy/config.h"
#if PK_ENABLE_THREADS
#include <stdatomic.h> #include <stdatomic.h>
#include <stdbool.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); bool c11_thrd_create(c11_thrd_t* thrd, c11_thrd_retval_t (*func)(void*), void* arg);
void c11_thrd_yield(); void c11_thrd_yield();
#endif

View File

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

View File

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

View File

@ -1,17 +1,20 @@
#pragma once #pragma once
// clang-format off // clang-format off
#define PK_VERSION "2.1.0" #define PK_VERSION "2.1.1"
#define PK_VERSION_MAJOR 2 #define PK_VERSION_MAJOR 2
#define PK_VERSION_MINOR 1 #define PK_VERSION_MINOR 1
#define PK_VERSION_PATCH 0 #define PK_VERSION_PATCH 1
/*************** feature settings ***************/ /*************** feature settings ***************/
// Whether to compile os-related modules or not
#ifndef PK_ENABLE_OS // can be overridden by cmake #ifndef PK_ENABLE_OS // can be overridden by cmake
#define PK_ENABLE_OS 1 #define PK_ENABLE_OS 1
#endif #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 #ifndef PK_ENABLE_DETERMINISM // must be enabled from cmake
#define PK_ENABLE_DETERMINISM 0 #define PK_ENABLE_DETERMINISM 0
#endif #endif
@ -24,24 +27,15 @@
#define PK_ENABLE_CUSTOM_SNAME 0 #define PK_ENABLE_CUSTOM_SNAME 0
#endif #endif
#ifndef PK_ENABLE_MIMALLOC // can be overridden by cmake
#define PK_ENABLE_MIMALLOC 0
#endif
// GC min threshold // GC min threshold
#ifndef PK_GC_MIN_THRESHOLD // can be overridden by cmake #ifndef PK_GC_MIN_THRESHOLD // can be overridden by cmake
#define PK_GC_MIN_THRESHOLD 32768 #define PK_GC_MIN_THRESHOLD 32768
#endif #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 // 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` // The actual size in bytes equals `sizeof(py_TValue) * PK_VM_STACK_SIZE`
#ifndef PK_VM_STACK_SIZE // can be overridden by cmake #ifndef PK_VM_STACK_SIZE // can be overridden by cmake
@ -81,3 +75,31 @@
#define restrict #define restrict
#endif #endif
#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

@ -61,3 +61,15 @@
#else #else
#define PK_DEPRECATED #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_vec2i origin;
} c11_array2d_view; } 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 */ /* chunked_array2d */
#define SMALLMAP_T__HEADER #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

@ -24,5 +24,6 @@ void LineProfiler__begin(LineProfiler* self);
void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame); void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame);
void LineProfiler__end(LineProfiler* self); void LineProfiler__end(LineProfiler* self);
void LineProfiler__reset(LineProfiler* self); void LineProfiler__reset(LineProfiler* self);
c11_string* LineProfiler__get_report(LineProfiler* self);
void LineProfiler__tracefunc(py_Frame* frame, enum py_TraceEvent event); void LineProfiler_tracefunc(py_Frame* frame, enum py_TraceEvent event);

View File

@ -32,3 +32,9 @@ void pk__add_module_libhv();
#else #else
#define pk__add_module_libhv() #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

@ -11,8 +11,13 @@ typedef struct PoolArena {
int block_size; int block_size;
int block_count; int block_count;
int unused_length; int unused_length;
int* unused;
union {
char data[kPoolArenaSize]; char data[kPoolArenaSize];
int64_t _align64;
};
int unused[];
} PoolArena; } PoolArena;
typedef struct Pool { typedef struct Pool {

View File

@ -14,7 +14,12 @@ typedef struct py_TypeInfo {
py_GlobalRef module; py_GlobalRef module;
bool is_python; // is it a python class? (not derived from c object) 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_TValue annotations;
py_Dtor dtor; // destructor for this type, NULL if no dtor 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, const py_GlobalRef module,
void (*dtor)(void*), void (*dtor)(void*),
bool is_python, bool is_python,
bool is_sealed); bool is_final);
py_Type pk_newtypewithmode(py_Name name, py_Type pk_newtypewithmode(py_Name name,
@ -38,4 +43,4 @@ py_Type pk_newtypewithmode(py_Name name,
const py_GlobalRef module, const py_GlobalRef module,
void (*dtor)(void*), void (*dtor)(void*),
bool is_python, bool is_python,
bool is_sealed, enum py_CompileMode mode); bool is_final, enum py_CompileMode mode);

View File

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

View File

@ -67,7 +67,7 @@ typedef struct VM {
NameDict compile_time_funcs; NameDict compile_time_funcs;
py_StackRef curr_class; 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; TraceInfo trace_info;
WatchdogInfo watchdog_info; WatchdogInfo watchdog_info;
LineProfiler line_profiler; 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__normalize_index(int* index, int length);
bool pk__object_new(int argc, py_Ref argv); 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); 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); int pk_arrayview(py_Ref self, py_TValue** p);
bool pk_wrapper__arrayequal(py_Type type, int argc, py_Ref argv); 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_arraycontains(py_Ref self, py_Ref val);
bool pk_loadmethod(py_StackRef self, py_Name name); 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_dict_items__register();
py_Type pk_list__register(); py_Type pk_list__register();
py_Type pk_tuple__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_slice__register();
py_Type pk_function__register(); py_Type pk_function__register();
py_Type pk_nativefunc__register(); py_Type pk_nativefunc__register();

View File

@ -19,8 +19,10 @@ typedef struct py_TValue {
PyObject* _obj; PyObject* _obj;
c11_vec2 _vec2; c11_vec2 _vec2;
c11_vec2i _vec2i; c11_vec2i _vec2i;
c11_vec3 _vec3;
c11_vec3i _vec3i;
c11_color32 _color32; c11_color32 _color32;
void* _ptr; void* _ptr;
char _chars[8]; char _chars[16];
}; };
} py_TValue; } 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 #ifdef PK_IS_PUBLIC_INCLUDE
typedef struct py_TValue { typedef struct py_TValue {
int64_t _[2]; py_Type type;
bool is_ptr;
int extra;
union {
int64_t _i64;
char _chars[16];
};
} py_TValue; } py_TValue;
#endif #endif
@ -70,8 +77,10 @@ typedef void (*py_TraceFunc)(py_Frame* frame, enum py_TraceEvent);
/// A struct contains the callbacks of the VM. /// A struct contains the callbacks of the VM.
typedef struct py_Callbacks { 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*); 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. /// Used by `print` to output a string.
void (*print)(const char*); void (*print)(const char*);
/// Flush the output buffer of `print`. /// Flush the output buffer of `print`.
@ -124,6 +133,13 @@ PK_API int py_gc_collect();
/// Setup the callbacks for the current VM. /// Setup the callbacks for the current VM.
PK_API py_Callbacks* py_callbacks(); 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. /// Begin the watchdog with `timeout` in milliseconds.
/// `PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature. /// `PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature.
/// You need to call `py_watchdog_end()` later. /// You need to call `py_watchdog_end()` later.
@ -201,6 +217,8 @@ PK_API py_GlobalRef py_NIL();
/// Create an `int` object. /// Create an `int` object.
PK_API void py_newint(py_OutRef, py_i64); 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. /// Create a `float` object.
PK_API void py_newfloat(py_OutRef, py_f64); PK_API void py_newfloat(py_OutRef, py_f64);
/// Create a `bool` object. /// 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`. /// Convert an `int` object in python to `int64_t`.
PK_API py_i64 py_toint(py_Ref); 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`. /// Convert a `float` object in python to `double`.
PK_API py_f64 py_tofloat(py_Ref); PK_API py_f64 py_tofloat(py_Ref);
/// Cast a `int` or `float` object in python to `double`. /// 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); PK_API const char* py_tpname(py_Type type);
/// Call a type to create a new instance. /// 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; 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. /// Check if the object is an instance of the given type exactly.
/// Raise `TypeError` if the check fails. /// 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); PK_API py_ItemRef py_getbuiltin(py_Name name);
/// Get the last return value. /// Get the last return value.
/// Please note that `py_retval()` cannot be used as input argument.
PK_API py_GlobalRef py_retval(); PK_API py_GlobalRef py_retval();
/// Get an item from the object's `__dict__`. /// Get an item from the object's `__dict__`.
@ -671,6 +701,14 @@ PK_API bool py_json_loads(const char* source) PY_RAISE PY_RETURN;
PK_API bool py_pickle_dumps(py_Ref val) PY_RAISE PY_RETURN; PK_API bool py_pickle_dumps(py_Ref val) PY_RAISE PY_RETURN;
/// Python equivalent to `pickle.loads(val)`. /// Python equivalent to `pickle.loads(val)`.
PK_API bool py_pickle_loads(const unsigned char* data, int size) PY_RAISE PY_RETURN; PK_API bool py_pickle_loads(const unsigned char* data, int size) PY_RAISE PY_RETURN;
/************* Profiler *************/
PK_API void py_profiler_begin();
PK_API void py_profiler_end();
PK_API void py_profiler_reset();
PK_API char* py_profiler_report();
/************* Unchecked Functions *************/ /************* Unchecked Functions *************/
PK_API py_ObjectRef py_tuple_data(py_Ref self); PK_API py_ObjectRef py_tuple_data(py_Ref self);
@ -715,19 +753,33 @@ PK_API bool
/// noexcept /// noexcept
PK_API int py_dict_len(py_Ref self); 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 *************/ /************* vmath module *************/
void py_newvec2(py_OutRef out, c11_vec2); PK_API void py_newvec2(py_OutRef out, c11_vec2);
void py_newvec3(py_OutRef out, c11_vec3); PK_API void py_newvec3(py_OutRef out, c11_vec3);
void py_newvec2i(py_OutRef out, c11_vec2i); PK_API void py_newvec2i(py_OutRef out, c11_vec2i);
void py_newvec3i(py_OutRef out, c11_vec3i); PK_API void py_newvec3i(py_OutRef out, c11_vec3i);
void py_newcolor32(py_OutRef out, c11_color32); PK_API void py_newcolor32(py_OutRef out, c11_color32);
c11_mat3x3* py_newmat3x3(py_OutRef out); PK_API c11_mat3x3* py_newmat3x3(py_OutRef out);
c11_vec2 py_tovec2(py_Ref self); PK_API c11_vec2 py_tovec2(py_Ref self);
c11_vec3 py_tovec3(py_Ref self); PK_API c11_vec3 py_tovec3(py_Ref self);
c11_vec2i py_tovec2i(py_Ref self); PK_API c11_vec2i py_tovec2i(py_Ref self);
c11_vec3i py_tovec3i(py_Ref self); PK_API c11_vec3i py_tovec3i(py_Ref self);
c11_mat3x3* py_tomat3x3(py_Ref self); PK_API c11_mat3x3* py_tomat3x3(py_Ref self);
c11_color32 py_tocolor32(py_Ref self); PK_API c11_color32 py_tocolor32(py_Ref self);
/************* Others *************/ /************* Others *************/
@ -757,7 +809,8 @@ enum py_PredefinedType {
tp_str_iterator, tp_str_iterator,
tp_list, // c11_vector tp_list, // c11_vector
tp_tuple, // N slots tp_tuple, // N slots
tp_array_iterator, tp_list_iterator, // 1 slot
tp_tuple_iterator, // 1 slot
tp_slice, // 3 slots (start, stop, step) tp_slice, // 3 slots (start, stop, step)
tp_range, tp_range,
tp_range_iterator, tp_range_iterator,

View File

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

View File

@ -14,7 +14,7 @@ public:
// get the python exception object // get the python exception object
object& exception() { return m_exception; } 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); } 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

@ -28,15 +28,19 @@ def currentvm() -> int:
def watchdog_begin(timeout: int): def watchdog_begin(timeout: int):
""" """Begin the watchdog with `timeout` in milliseconds.
Begin the watchdog with `timeout` in milliseconds.
`PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature. `PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature.
You need to call `watchdog_end()` later. You need to call `watchdog_end()` later.
If `timeout` is reached, `TimeoutError` will be raised. If `timeout` is reached, `TimeoutError` will be raised.
""" """
def watchdog_end(): def watchdog_end() -> None:
"""Reset the watchdog.""" """End the watchdog after a call to `watchdog_begin()`."""
def profiler_begin() -> None: ...
def profiler_end() -> None: ...
def profiler_reset() -> None: ...
def profiler_report() -> dict[str, list[list]]: ...
class ComputeThread: class ComputeThread:
def __init__(self, vm_index: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]): ... def __init__(self, vm_index: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]): ...

View File

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

View File

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

View File

@ -1,15 +1,12 @@
#include "pocketpy/common/chunkedvector.h" #include "pocketpy/common/chunkedvector.h"
#include "pocketpy/pocketpy.h"
#include <stdlib.h> #include <assert.h>
#include <string.h>
#include "pocketpy/common/utils.h"
#include "pocketpy/config.h"
#if defined(_MSC_VER) #if defined(_MSC_VER)
#include <intrin.h> #include <intrin.h>
#endif #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__)) #if(defined(__clang__) || defined(__GNUC__))
return x == 0 ? 0 : (int)sizeof(unsigned long) * 8 - __builtin_clzl(x); return x == 0 ? 0 : (int)sizeof(unsigned long) * 8 - __builtin_clzl(x);
#elif defined(_MSC_VER) #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; self->initial_chunks = initial_chunks;
void* chunks_data = PK_MALLOC(elem_size * ((1U << (unsigned int)initial_chunks) - 1)); void* chunks_data = PK_MALLOC(elem_size * ((1U << (unsigned int)initial_chunks) - 1));
for(int i = 0; i < initial_chunks; i++) { for(int i = 0; i < initial_chunks; i++) {
// TODO: optimize?
c11_chunkedvector_chunk chunk = {.length = 0, c11_chunkedvector_chunk chunk = {.length = 0,
.capacity = 1U << i, .capacity = 1U << i,
.data = (char*)chunks_data + elem_size * ((1U << i) - 1U)}; .data = (char*)chunks_data + elem_size * ((1U << i) - 1U)};
@ -60,7 +56,8 @@ void c11_chunkedvector__dtor(c11_chunkedvector* self) {
void* c11_chunkedvector__emplace(c11_chunkedvector* self) { void* c11_chunkedvector__emplace(c11_chunkedvector* self) {
if(self->length == self->capacity) { if(self->length == self->capacity) {
#ifndef NDEBUG #ifndef NDEBUG
c11_chunkedvector_chunk last_chunk = c11_vector__back(c11_chunkedvector_chunk, &self->chunks); c11_chunkedvector_chunk last_chunk =
c11_vector__back(c11_chunkedvector_chunk, &self->chunks);
assert(last_chunk.capacity == last_chunk.length); assert(last_chunk.capacity == last_chunk.length);
#endif #endif
c11_chunkedvector_chunk chunk = { c11_chunkedvector_chunk chunk = {
@ -70,9 +67,14 @@ void* c11_chunkedvector__emplace(c11_chunkedvector* self) {
self->capacity += chunk.capacity; self->capacity += chunk.capacity;
c11_vector__push(c11_chunkedvector_chunk, &self->chunks, chunk); 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_chunkedvector_chunk* last_chunk =
c11__at(c11_chunkedvector_chunk, &self->chunks, last_chunk_index); 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; void* p = (char*)last_chunk->data + self->elem_size * last_chunk->length;
last_chunk->length++; last_chunk->length++;
self->length++; self->length++;
@ -80,7 +82,7 @@ void* c11_chunkedvector__emplace(c11_chunkedvector* self) {
} }
void* c11_chunkedvector__at(c11_chunkedvector* self, int index) { 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); 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; 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/common/memorypool.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/config.h"
#include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
void FixedMemoryPool__ctor(FixedMemoryPool* self, int BlockSize, int BlockCount) { void FixedMemoryPool__ctor(FixedMemoryPool* self, int BlockSize, int BlockCount) {

View File

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

View File

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

View File

@ -1,7 +1,6 @@
#include "pocketpy/objects/sourcedata.h" #include "pocketpy/objects/sourcedata.h"
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
#include <ctype.h> #include <ctype.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
static void SourceData__ctor(struct SourceData* self, static void SourceData__ctor(struct SourceData* self,
@ -25,6 +24,16 @@ static void SourceData__ctor(struct SourceData* self,
source++; source++;
} }
self->source = c11_sbuf__submit(&ss); 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; self->is_dynamic = is_dynamic;
c11_vector__push(const char*, &self->line_starts, self->source->data); c11_vector__push(const char*, &self->line_starts, self->source->data);
} }

View File

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

View File

@ -2,11 +2,10 @@
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <string.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h>
c11_string* c11_string__new(const char* data) { return c11_string__new2(data, strlen(data)); } 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; 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 /* T=c11_sv */ c11_sv__split(c11_sv self, char sep) {
c11_vector retval; c11_vector retval;
c11_vector__ctor(&retval, sizeof(c11_sv)); c11_vector__ctor(&retval, sizeof(c11_sv));

View File

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

View File

@ -1,6 +1,5 @@
#include "pocketpy/common/vector.h" #include "pocketpy/common/vector.h"
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
#include "pocketpy/config.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__clear(c11_vector* self) { self->length = 0; }
void* c11_vector__emplace(c11_vector* self) { 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; void* p = (char*)self->data + (size_t)self->elem_size * (size_t)self->length;
self->length++; self->length++;
return p; return p;
@ -68,3 +67,12 @@ void c11_vector__swap(c11_vector *self, c11_vector *other){
*self = *other; *self = *other;
*other = tmp; *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_subscr; // SubscrExpr
bool is_starred; // StarredExpr bool is_starred; // StarredExpr
bool is_binary; // BinaryExpr bool is_binary; // BinaryExpr
bool is_ternary; // TernaryExpr
void (*dtor)(Expr*); void (*dtor)(Expr*);
} ExprVt; } ExprVt;
@ -873,7 +874,11 @@ void TernaryExpr__emit_(Expr* self_, Ctx* ctx) {
} }
TernaryExpr* TernaryExpr__new(int line) { 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)); TernaryExpr* self = PK_MALLOC(sizeof(TernaryExpr));
self->vt = &Vt; self->vt = &Vt;
self->line = line; self->line = line;
@ -1681,6 +1686,10 @@ static Error* exprTernary(Compiler* self) {
e->cond = Ctx__s_popx(ctx()); e->cond = Ctx__s_popx(ctx());
e->true_expr = Ctx__s_popx(ctx()); e->true_expr = Ctx__s_popx(ctx());
Ctx__s_push(ctx(), (Expr*)e); 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; return NULL;
} }
@ -2856,13 +2865,14 @@ static Error* compile_stmt(Compiler* self) {
bool is_typed_name = false; // e.g. x: int bool is_typed_name = false; // e.g. x: int
// eat variable's type hint if it is a single name // 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)) { if(match(TK_COLON)) {
c11_sv type_hint; c11_sv type_hint;
check(consume_type_hints_sv(self, &type_hint)); check(consume_type_hints_sv(self, &type_hint));
is_typed_name = true; 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()); NameExpr* ne = (NameExpr*)Ctx__s_top(ctx());
int index = Ctx__add_const_string(ctx(), type_hint); int index = Ctx__add_const_string(ctx(), type_hint);
Ctx__emit_(ctx(), OP_LOAD_CONST, index, BC_KEEPLINE); Ctx__emit_(ctx(), OP_LOAD_CONST, index, BC_KEEPLINE);

View File

@ -7,6 +7,7 @@
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/objects/error.h" #include "pocketpy/objects/error.h"
#include <stdbool.h> #include <stdbool.h>
#include <assert.h>
#include <time.h> #include <time.h>
static bool stack_format_object(VM* self, c11_sv spec); static bool stack_format_object(VM* self, c11_sv spec);
@ -30,6 +31,12 @@ static bool stack_format_object(VM* self, c11_sv spec);
goto __NEXT_STEP; \ goto __NEXT_STEP; \
} while(0) } while(0)
#define RESET_CO_CACHE() \
do { \
co_codes = frame->co->codes.data; \
co_names = frame->co->names.data; \
} while(0)
/* Stack manipulation macros */ /* Stack manipulation macros */
// https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123 // https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123
#define TOP() (self->stack.sp - 1) #define TOP() (self->stack.sp - 1)
@ -82,19 +89,16 @@ FrameResult VM__run_top_frame(VM* self) {
py_Frame* frame = self->top_frame; py_Frame* frame = self->top_frame;
Bytecode* co_codes; Bytecode* co_codes;
py_Name* co_names; py_Name* co_names;
Bytecode byte;
const py_Frame* base_frame = frame; const py_Frame* base_frame = frame;
while(true) {
Bytecode byte;
__NEXT_FRAME: __NEXT_FRAME:
if(self->recursion_depth >= self->max_recursion_depth) { if(self->recursion_depth >= self->max_recursion_depth) {
py_exception(tp_RecursionError, "maximum recursion depth exceeded"); py_exception(tp_RecursionError, "maximum recursion depth exceeded");
goto __ERROR; goto __ERROR;
} }
// NOTE: remember to change another occurrence after __ERROR_RE_RAISE: RESET_CO_CACHE();
co_codes = frame->co->codes.data;
co_names = frame->co->names.data;
frame->ip++; frame->ip++;
__NEXT_STEP: __NEXT_STEP:
@ -1037,7 +1041,7 @@ FrameResult VM__run_top_frame(VM* self) {
POP(); POP();
py_TypeInfo* base_ti = pk_typeinfo(base); py_TypeInfo* base_ti = pk_typeinfo(base);
if(base_ti->is_sealed) { if(base_ti->is_final) {
TypeError("type '%t' is not an acceptable base type", base); TypeError("type '%t' is not an acceptable base type", base);
goto __ERROR; goto __ERROR;
} }
@ -1083,9 +1087,7 @@ FrameResult VM__run_top_frame(VM* self) {
py_Name name = co_names[byte.arg]; py_Name name = co_names[byte.arg];
// TOP() can be a function, classmethod or custom decorator // TOP() can be a function, classmethod or custom decorator
py_Ref actual_func = TOP(); py_Ref actual_func = TOP();
if(actual_func->type == tp_classmethod) { if(actual_func->type == tp_classmethod) { actual_func = py_getslot(actual_func, 0); }
actual_func = py_getslot(actual_func, 0);
}
if(actual_func->type == tp_function) { if(actual_func->type == tp_function) {
Function* ud = py_touserdata(actual_func); Function* ud = py_touserdata(actual_func);
ud->clazz = self->curr_class->_obj; ud->clazz = self->curr_class->_obj;
@ -1110,8 +1112,7 @@ FrameResult VM__run_top_frame(VM* self) {
// [expr] // [expr]
py_push(TOP()); py_push(TOP());
if(!py_pushmethod(__enter__)) { if(!py_pushmethod(__enter__)) {
TypeError("'%t' object does not support the context manager protocol", TypeError("'%t' object does not support the context manager protocol", TOP()->type);
TOP()->type);
goto __ERROR; goto __ERROR;
} }
vectorcall_opcall(0, 0); vectorcall_opcall(0, 0);
@ -1121,8 +1122,7 @@ FrameResult VM__run_top_frame(VM* self) {
// [expr] // [expr]
py_push(TOP()); py_push(TOP());
if(!py_pushmethod(__exit__)) { if(!py_pushmethod(__exit__)) {
TypeError("'%t' object does not support the context manager protocol", TypeError("'%t' object does not support the context manager protocol", TOP()->type);
TOP()->type);
goto __ERROR; goto __ERROR;
} }
if(!py_vectorcall(0, 0)) goto __ERROR; if(!py_vectorcall(0, 0)) goto __ERROR;
@ -1226,6 +1226,7 @@ FrameResult VM__run_top_frame(VM* self) {
__ERROR_RE_RAISE: __ERROR_RE_RAISE:
do { do {
} while(0); } while(0);
int target = Frame__prepare_jump_exception_handler(frame, &self->stack); int target = Frame__prepare_jump_exception_handler(frame, &self->stack);
if(target >= 0) { if(target >= 0) {
// 1. Exception can be handled inside the current frame // 1. Exception can be handled inside the current frame
@ -1239,13 +1240,11 @@ FrameResult VM__run_top_frame(VM* self) {
return RES_ERROR; return RES_ERROR;
} }
frame = self->top_frame; frame = self->top_frame;
co_codes = frame->co->codes.data; RESET_CO_CACHE();
co_names = frame->co->names.data;
goto __ERROR; goto __ERROR;
} }
}
return RES_RETURN; c11__unreachable();
} }
const char* pk_op2str(py_Name op) { const char* pk_op2str(py_Name op) {
@ -1304,7 +1303,10 @@ bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop) {
py_newbool(py_retval(), !res); py_newbool(py_retval(), !res);
return true; return true;
} }
return TypeError("unsupported operand type(s) for '%s'", pk_op2str(op));
py_Type lhs_t = rop ? TOP()->type : SECOND()->type;
py_Type rhs_t = rop ? SECOND()->type : TOP()->type;
return TypeError("unsupported operand type(s) for '%s': '%t' and '%t'", pk_op2str(op), lhs_t, rhs_t);
} }
bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) { bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) {
@ -1522,6 +1524,7 @@ static bool stack_format_object(VM* self, c11_sv spec) {
#undef SP #undef SP
#undef INSERT_THIRD #undef INSERT_THIRD
#undef vectorcall_opcall #undef vectorcall_opcall
#undef RESET_CO_CACHE
void py_sys_settrace(py_TraceFunc func, bool reset) { void py_sys_settrace(py_TraceFunc func, bool reset) {
TraceInfo* info = &pk_current_vm->trace_info; TraceInfo* info = &pk_current_vm->trace_info;

View File

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

View File

@ -4,6 +4,7 @@
#include "pocketpy/objects/base.h" #include "pocketpy/objects/base.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include <stdbool.h> #include <stdbool.h>
#include <assert.h>
void pk_newgenerator(py_Ref out, py_Frame* frame, py_TValue* begin, py_TValue* end) { 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)); 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); 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); PY_CHECK_ARGC(1);
Generator* ud = py_touserdata(argv); Generator* ud = py_touserdata(argv);
py_StackRef p0 = py_peek(0); py_StackRef p0 = py_peek(0);

View File

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

View File

@ -1,61 +0,0 @@
#include "pocketpy/interpreter/line_profiler.h"
void LineProfiler__ctor(LineProfiler* self) {
c11_smallmap_p2i__ctor(&self->records);
self->prev_loc.src = NULL;
self->prev_time = 0;
self->enabled = false;
}
void LineProfiler__dtor(LineProfiler* self) {
for(int i = 0; i < self->records.length; i++) {
LineRecord* lines = c11__getitem(LineRecord*, &self->records, i);
PK_FREE(lines);
}
c11_smallmap_p2i__dtor(&self->records);
}
LineRecord* LineProfiler__get_record(LineProfiler* self, SourceLocation loc) {
LineRecord* lines = (LineRecord*)c11_smallmap_p2i__get(&self->records, loc.src, 0);
if(lines == NULL) {
int max_lineno = loc.src->line_starts.length;
lines = PK_MALLOC(sizeof(LineRecord) * (max_lineno + 1));
memset(lines, 0, sizeof(LineRecord) * (max_lineno + 1));
c11_smallmap_p2i__set(&self->records, loc.src, (py_i64)lines);
}
return &lines[loc.lineno];
}
void LineProfiler__begin(LineProfiler* self) {
assert(!self->enabled);
self->prev_loc.src = NULL;
self->prev_time = 0;
self->enabled = true;
}
void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame) {
assert(self->enabled);
clock_t now = clock();
if(self->prev_loc.src != NULL) {
LineRecord* line = LineProfiler__get_record(self, self->prev_loc);
line->hits++;
line->time += now - self->prev_time;
}
self->prev_loc = Frame__source_location(frame);
self->prev_time = now;
}
void LineProfiler__end(LineProfiler* self) {
assert(self->enabled);
if(self->prev_loc.src != NULL) {
LineRecord* line = LineProfiler__get_record(self, self->prev_loc);
line->hits++;
line->time += clock() - self->prev_time;
}
self->enabled = false;
}
void LineProfiler__reset(LineProfiler* self) {
LineProfiler__dtor(self);
LineProfiler__ctor(self);
}

View File

@ -0,0 +1,110 @@
#include "pocketpy/common/sstream.h"
#include "pocketpy/interpreter/line_profiler.h"
#include "pocketpy/objects/sourcedata.h"
#include <assert.h>
void LineProfiler__ctor(LineProfiler* self) {
c11_smallmap_p2i__ctor(&self->records);
self->prev_loc.src = NULL;
self->prev_time = 0;
self->enabled = false;
}
void LineProfiler__dtor(LineProfiler* self) {
if(self->prev_loc.src != NULL) PK_DECREF(self->prev_loc.src);
for(int i = 0; i < self->records.length; i++) {
c11_smallmap_p2i_KV kv = c11__getitem(c11_smallmap_p2i_KV, &self->records, i);
SourceData_ src = (SourceData_)kv.key;
PK_DECREF(src);
PK_FREE((void*)kv.value);
}
c11_smallmap_p2i__dtor(&self->records);
}
LineRecord* LineProfiler__get_record(LineProfiler* self, SourceLocation loc) {
LineRecord* lines = (LineRecord*)c11_smallmap_p2i__get(&self->records, loc.src, 0);
if(lines == NULL) {
int max_lineno = loc.src->line_starts.length;
lines = PK_MALLOC(sizeof(LineRecord) * (max_lineno + 1));
memset(lines, 0, sizeof(LineRecord) * (max_lineno + 1));
c11_smallmap_p2i__set(&self->records, loc.src, (py_i64)lines);
PK_INCREF(loc.src);
}
return &lines[loc.lineno];
}
void LineProfiler__begin(LineProfiler* self) {
assert(!self->enabled);
self->prev_loc.src = NULL;
self->prev_time = 0;
self->enabled = true;
}
void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame) {
assert(self->enabled);
clock_t now = clock();
if(self->prev_loc.src != NULL) {
LineRecord* line = LineProfiler__get_record(self, self->prev_loc);
line->hits++;
line->time += now - self->prev_time;
PK_DECREF(self->prev_loc.src);
}
self->prev_loc = Frame__source_location(frame);
PK_INCREF(self->prev_loc.src);
self->prev_time = now;
}
void LineProfiler__end(LineProfiler* self) {
assert(self->enabled);
if(self->prev_loc.src != NULL) {
LineRecord* line = LineProfiler__get_record(self, self->prev_loc);
line->hits++;
line->time += clock() - self->prev_time;
}
self->enabled = false;
}
void LineProfiler__reset(LineProfiler* self) {
LineProfiler__dtor(self);
LineProfiler__ctor(self);
}
c11_string* LineProfiler__get_report(LineProfiler* self) {
c11_sbuf sbuf;
c11_sbuf__ctor(&sbuf);
c11_sbuf__write_char(&sbuf, '{');
c11_sbuf__write_cstr(&sbuf, "\"version\": 1, ");
c11_sbuf__write_cstr(&sbuf, "\"CLOCKS_PER_SEC\": ");
c11_sbuf__write_i64(&sbuf, CLOCKS_PER_SEC);
c11_sbuf__write_cstr(&sbuf, ", \"records\": ");
c11_sbuf__write_char(&sbuf, '{');
for(int i = 0; i < self->records.length; i++) {
c11_smallmap_p2i_KV kv = c11__getitem(c11_smallmap_p2i_KV, &self->records, i);
SourceData_ src = (SourceData_)kv.key;
int line_record_length = src->line_starts.length + 1;
c11_sv src_name = c11_string__sv(src->filename);
c11_sbuf__write_quoted(&sbuf, src_name, '"');
c11_sbuf__write_cstr(&sbuf, ": [");
LineRecord* lines = (LineRecord*)kv.value;
bool is_first = true;
for(int j = 1; j < line_record_length; j++) {
// [<j>, <hits>, <time>]
if(lines[j].hits == 0 && lines[j].time == 0) continue;
if(!is_first) c11_sbuf__write_cstr(&sbuf, ", ");
c11_sbuf__write_cstr(&sbuf, "[");
c11_sbuf__write_int(&sbuf, j);
c11_sbuf__write_cstr(&sbuf, ", ");
c11_sbuf__write_i64(&sbuf, lines[j].hits);
c11_sbuf__write_cstr(&sbuf, ", ");
c11_sbuf__write_i64(&sbuf, lines[j].time);
c11_sbuf__write_cstr(&sbuf, "]");
is_first = false;
}
c11_sbuf__write_cstr(&sbuf, "]");
if(i < self->records.length - 1) c11_sbuf__write_cstr(&sbuf, ", ");
}
c11_sbuf__write_char(&sbuf, '}');
c11_sbuf__write_char(&sbuf, '}');
return c11_sbuf__submit(&sbuf);
}

View File

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

View File

@ -1,4 +1,5 @@
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include <assert.h>
py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name) { py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name) {
assert(ti != NULL); assert(ti != NULL);
@ -10,17 +11,17 @@ py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name) {
return NULL; 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); py_TypeInfo* ti = pk_typeinfo(type);
return pk_tpfindname(ti, name); 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)); // assert(py_ismagicname(name));
return py_tpfindname(t, name); return py_tpfindname(t, name);
} }
py_Type py_tpbase(py_Type t) { PK_INLINE py_Type py_tpbase(py_Type t) {
assert(t); assert(t);
py_TypeInfo* ti = pk_typeinfo(t); py_TypeInfo* ti = pk_typeinfo(t);
return ti->base; return ti->base;
@ -44,7 +45,7 @@ const char* py_tpname(py_Type type) {
return py_name2str(name); return py_name2str(name);
} }
py_TypeInfo* pk_typeinfo(py_Type type) { PK_INLINE py_TypeInfo* pk_typeinfo(py_Type type) {
#ifndef NDEBUG #ifndef NDEBUG
int length = pk_current_vm->types.length; int length = pk_current_vm->types.length;
if(type < 0 || type >= length) { if(type < 0 || type >= length) {
@ -60,11 +61,11 @@ static void py_TypeInfo__common_init(py_Name name,
const py_GlobalRef module, const py_GlobalRef module,
void (*dtor)(void*), void (*dtor)(void*),
bool is_python, bool is_python,
bool is_sealed, bool is_final,
py_TypeInfo* self, py_TypeInfo* self,
py_TValue* typeobject) { py_TValue* typeobject) {
py_TypeInfo* base_ti = base ? pk_typeinfo(base) : NULL; 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)); 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; if(!dtor && base) dtor = base_ti->dtor;
self->is_python = is_python; 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->annotations = *py_NIL();
self->dtor = dtor; self->dtor = dtor;
@ -90,7 +96,7 @@ py_Type pk_newtype(const char* name,
const py_GlobalRef module, const py_GlobalRef module,
void (*dtor)(void*), void (*dtor)(void*),
bool is_python, bool is_python,
bool is_sealed) { bool is_final) {
py_Type index = pk_current_vm->types.length; py_Type index = pk_current_vm->types.length;
py_TypeInfo* self = py_newobject(py_retval(), tp_type, -1, sizeof(py_TypeInfo)); py_TypeInfo* self = py_newobject(py_retval(), tp_type, -1, sizeof(py_TypeInfo));
py_TypeInfo__common_init(py_name(name), py_TypeInfo__common_init(py_name(name),
@ -99,7 +105,7 @@ py_Type pk_newtype(const char* name,
module, module,
dtor, dtor,
is_python, is_python,
is_sealed, is_final,
self, self,
py_retval()); py_retval());
TypePointer* pointer = c11_vector__emplace(&pk_current_vm->types); TypePointer* pointer = c11_vector__emplace(&pk_current_vm->types);
@ -113,7 +119,7 @@ py_Type pk_newtypewithmode(py_Name name,
const py_GlobalRef module, const py_GlobalRef module,
void (*dtor)(void*), void (*dtor)(void*),
bool is_python, bool is_python,
bool is_sealed, bool is_final,
enum py_CompileMode mode) { enum py_CompileMode mode) {
if(mode == RELOAD_MODE && module != NULL) { if(mode == RELOAD_MODE && module != NULL) {
py_ItemRef old_class = py_getdict(module, name); py_ItemRef old_class = py_getdict(module, name);
@ -131,7 +137,7 @@ py_Type pk_newtypewithmode(py_Name name,
module, module,
dtor, dtor,
is_python, is_python,
is_sealed, is_final,
self, self,
&self->self); &self->self);
TypePointer* pointer = c11__at(TypePointer, &pk_current_vm->types, index); 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*)) { py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, void (*dtor)(void*)) {
@ -150,3 +156,22 @@ py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, vo
if(module) py_setdict(module, py_name(name), py_tpobject(type)); if(module) py_setdict(module, py_name(name), py_tpobject(type));
return 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/interpreter/vm.h"
#include "pocketpy/common/memorypool.h" #include "pocketpy/common/memorypool.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
#include "pocketpy/interpreter/generator.h" #include "pocketpy/interpreter/generator.h"
#include "pocketpy/interpreter/modules.h" #include "pocketpy/interpreter/modules.h"
@ -10,6 +9,7 @@
#include "pocketpy/common/_generated.h" #include "pocketpy/common/_generated.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include <stdbool.h> #include <stdbool.h>
#include <assert.h>
static char* pk_default_importfile(const char* path) { static char* pk_default_importfile(const char* path) {
#if PK_ENABLE_OS #if PK_ENABLE_OS
@ -34,9 +34,36 @@ static void pk_default_flush() { fflush(stdout); }
static int pk_default_getchr() { return getchar(); } static int pk_default_getchr() { return getchar(); }
void LineProfiler__tracefunc(py_Frame* frame, enum py_TraceEvent event) { void py_profiler_begin() {
LineProfiler* self = &pk_current_vm->line_profiler; LineProfiler* lp = &pk_current_vm->line_profiler;
if(self->enabled && event == TRACE_EVENT_LINE) { LineProfiler__tracefunc_line(self, frame); } TraceInfo* trace_info = &pk_current_vm->trace_info;
if(trace_info->func == NULL) py_sys_settrace(LineProfiler_tracefunc, true);
c11__rtassert(trace_info->func == LineProfiler_tracefunc);
LineProfiler__begin(lp);
}
void py_profiler_end() {
LineProfiler* lp = &pk_current_vm->line_profiler;
LineProfiler__end(lp);
}
void py_profiler_reset() {
LineProfiler* lp = &pk_current_vm->line_profiler;
LineProfiler__reset(lp);
}
char* py_profiler_report() {
LineProfiler* lp = &pk_current_vm->line_profiler;
if(lp->enabled) LineProfiler__end(lp);
c11_string* s = LineProfiler__get_report(lp);
char* s_dup = c11_strdup(s->data);
c11_string__delete(s);
return s_dup;
}
void LineProfiler_tracefunc(py_Frame* frame, enum py_TraceEvent event) {
LineProfiler* lp = &pk_current_vm->line_profiler;
if(lp->enabled && event == TRACE_EVENT_LINE) LineProfiler__tracefunc_line(lp, frame);
} }
static int BinTree__cmp_cstr(void* lhs, void* rhs) { static int BinTree__cmp_cstr(void* lhs, void* rhs) {
@ -59,6 +86,7 @@ void VM__ctor(VM* self) {
self->main = NULL; self->main = NULL;
self->callbacks.importfile = pk_default_importfile; self->callbacks.importfile = pk_default_importfile;
self->callbacks.lazyimport = NULL;
self->callbacks.print = pk_default_print; self->callbacks.print = pk_default_print;
self->callbacks.flush = pk_default_flush; self->callbacks.flush = pk_default_flush;
self->callbacks.getchr = pk_default_getchr; self->callbacks.getchr = pk_default_getchr;
@ -109,7 +137,8 @@ void VM__ctor(VM* self) {
validate(tp_list, pk_list__register()); validate(tp_list, pk_list__register());
validate(tp_tuple, pk_tuple__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_slice, pk_slice__register());
validate(tp_range, pk_range__register()); validate(tp_range, pk_range__register());
@ -234,6 +263,7 @@ void VM__ctor(VM* self) {
pk__add_module_conio(); pk__add_module_conio();
pk__add_module_lz4(); // optional pk__add_module_lz4(); // optional
pk__add_module_libhv(); // optional pk__add_module_libhv(); // optional
pk__add_module_cute_png(); // optional
pk__add_module_pkpy(); pk__add_module_pkpy();
// add python builtins // add python builtins

View File

@ -1,4 +1,5 @@
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include <assert.h>
void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) { void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) {
return; return;
@ -123,5 +124,8 @@ py_Ref py_name2ref(py_Name name) {
void PyObject__dtor(PyObject* self) { void PyObject__dtor(PyObject* self) {
py_Dtor dtor = c11__getitem(TypePointer, &pk_current_vm->types, self->type).dtor; py_Dtor dtor = c11__getitem(TypePointer, &pk_current_vm->types, self->type).dtor;
if(dtor) dtor(PyObject__userdata(self)); 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; 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; int numel = n_cols * n_rows;
c11_array2d* ud = py_newobject(out, tp_array2d, numel, sizeof(c11_array2d)); c11_array2d* ud = py_newobject(out, tp_array2d, numel, sizeof(c11_array2d));
ud->header.n_cols = n_cols; 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); int code = py_equal(item, value);
if(code == -1) return false; if(code == -1) return false;
if(code == 1) { if(code == 1) {
py_newvec2i(py_retval(), (c11_vec2i){{i, j}}); py_newvec2i(py_retval(),
(c11_vec2i){
{i, j}
});
return true; return true;
} }
} }
@ -176,7 +179,7 @@ static bool array2d_like_map(int argc, py_Ref argv) {
PY_CHECK_ARGC(2); PY_CHECK_ARGC(2);
c11_array2d_like* self = py_touserdata(argv); c11_array2d_like* self = py_touserdata(argv);
py_Ref f = py_arg(1); 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 j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) { for(int i = 0; i < self->n_cols; i++) {
py_Ref item = self->f_get(self, i, j); 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 { } else {
other = NULL; 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 j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) { for(int i = 0; i < self->n_cols; i++) {
py_Ref lhs = self->f_get(self, i, j); 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)); c11_array2d_like* other = py_touserdata(py_arg(1));
py_Ref f = py_arg(2); py_Ref f = py_arg(2);
if(!_array2d_like_check_same_shape(self, other)) return false; 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 j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) { for(int i = 0; i < self->n_cols; i++) {
py_push(f); 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) { static bool array2d_like__invert__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
c11_array2d_like* self = py_touserdata(argv); 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 j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) { for(int i = 0; i < self->n_cols; i++) {
py_Ref item = self->f_get(self, i, j); 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': ... // def copy(self) -> 'array2d': ...
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
c11_array2d_like* self = py_touserdata(argv); 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 j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) { for(int i = 0; i < self->n_cols; i++) {
py_Ref item = self->f_get(self, i, j); 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) { static bool array2d_like_count_neighbors(int argc, py_Ref argv) {
PY_CHECK_ARGC(3); PY_CHECK_ARGC(3);
c11_array2d_like* self = py_touserdata(argv); 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); py_Ref value = py_arg(1);
const char* neighborhood = py_tostr(py_arg(2)); 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; int ksize = kernel->n_cols;
if(ksize % 2 == 0) return ValueError("kernel size must be odd"); if(ksize % 2 == 0) return ValueError("kernel size must be odd");
int ksize_half = ksize / 2; 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 j = 0; j < self->n_rows; j++) {
for(int i = 0; i < self->n_cols; i++) { for(int i = 0; i < self->n_cols; i++) {
py_i64 sum = 0; 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); PY_CHECK_ARGC(1);
c11_array2d_like_iterator* self = py_touserdata(argv); c11_array2d_like_iterator* self = py_touserdata(argv);
if(self->j >= self->array->n_rows) return StopIteration(); 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_cols = argv[1]._i64;
int n_rows = argv[2]._i64; int n_rows = argv[2]._i64;
if(n_cols <= 0 || n_rows <= 0) return ValueError("array2d() expected positive dimensions"); 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 // setup initial values
if(py_callable(default_)) { if(py_callable(default_)) {
for(int j = 0; j < n_rows; j++) { 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"); 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++) { for(int j = 0; j < n_rows; j++) {
py_Ref row_j = py_list_getitem(argv, j); py_Ref row_j = py_list_getitem(argv, j);
for(int i = 0; i < n_cols; i++) { for(int i = 0; i < n_cols; i++) {
@ -1360,3 +1363,29 @@ void pk__add_module_array2d() {
register_array2d_view(mod); register_array2d_view(mod);
register_chunked_array2d(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 "pocketpy/pocketpy.h"
#include <stdlib.h>
#if PY_SYS_PLATFORM == 0 #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) { 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); py_applydict(&derived_ti->self, Enum__wrapper_field, &derived_ti->self);
} }

View File

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

View File

@ -1,8 +1,4 @@
#include "pocketpy/pocketpy.h" #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 "pocketpy/interpreter/vm.h"
#include <math.h> #include <math.h>

View File

@ -2,7 +2,7 @@
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#if PK_ENABLE_OS == 1 #if PK_ENABLE_OS
#include <errno.h> #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: { case PKL_ARRAY2D: {
int n_cols = pkl__read_int(&p); int n_cols = pkl__read_int(&p);
int n_rows = 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); int total_size = arr->header.numel * sizeof(py_TValue);
memcpy(arr->data, p, total_size); memcpy(arr->data, p, total_size);
for(int i = 0; i < arr->header.numel; i++) { 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 #endif
#if PK_ENABLE_THREADS
typedef struct c11_ComputeThread c11_ComputeThread; typedef struct c11_ComputeThread c11_ComputeThread;
typedef struct { typedef struct {
@ -456,6 +458,8 @@ static void pk_ComputeThread__register(py_Ref mod) {
py_bindmethod(type, "eval", ComputeThread_eval); py_bindmethod(type, "eval", ComputeThread_eval);
} }
#endif // PK_ENABLE_THREADS
static void pkpy_configmacros_add(py_Ref dict, const char* key, int val) { static void pkpy_configmacros_add(py_Ref dict, const char* key, int val) {
assert(dict->type == tp_dict); assert(dict->type == tp_dict);
py_TValue tmp; py_TValue tmp;
@ -463,6 +467,42 @@ static void pkpy_configmacros_add(py_Ref dict, const char* key, int val) {
py_dict_setitem_by_str(dict, key, &tmp); py_dict_setitem_by_str(dict, key, &tmp);
} }
static bool pkpy_profiler_begin(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);
TraceInfo* trace_info = &pk_current_vm->trace_info;
if(trace_info->func == NULL) py_sys_settrace(LineProfiler_tracefunc, true);
if(trace_info->func != LineProfiler_tracefunc) {
return RuntimeError("LineProfiler_tracefunc() should be set as the trace function");
}
LineProfiler__begin(&pk_current_vm->line_profiler);
py_newnone(py_retval());
return true;
}
static bool pkpy_profiler_end(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);
LineProfiler__end(&pk_current_vm->line_profiler);
py_newnone(py_retval());
return true;
}
static bool pkpy_profiler_reset(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);
LineProfiler__reset(&pk_current_vm->line_profiler);
py_newnone(py_retval());
return true;
}
static bool pkpy_profiler_report(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);
LineProfiler* lp = &pk_current_vm->line_profiler;
if(lp->enabled) LineProfiler__end(lp);
c11_string* report = LineProfiler__get_report(lp);
bool ok = py_json_loads(report->data);
c11_string__delete(report);
return ok;
}
void pk__add_module_pkpy() { void pk__add_module_pkpy() {
py_Ref mod = py_newmodule("pkpy"); py_Ref mod = py_newmodule("pkpy");
@ -508,11 +548,19 @@ void pk__add_module_pkpy() {
py_bindfunc(mod, "watchdog_end", pkpy_watchdog_end); py_bindfunc(mod, "watchdog_end", pkpy_watchdog_end);
#endif #endif
#if PK_ENABLE_THREADS
pk_ComputeThread__register(mod); pk_ComputeThread__register(mod);
#endif
py_bindfunc(mod, "profiler_begin", pkpy_profiler_begin);
py_bindfunc(mod, "profiler_end", pkpy_profiler_end);
py_bindfunc(mod, "profiler_reset", pkpy_profiler_reset);
py_bindfunc(mod, "profiler_report", pkpy_profiler_report);
py_Ref configmacros = py_emplacedict(mod, py_name("configmacros")); py_Ref configmacros = py_emplacedict(mod, py_name("configmacros"));
py_newdict(configmacros); py_newdict(configmacros);
pkpy_configmacros_add(configmacros, "PK_ENABLE_OS", PK_ENABLE_OS); 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_DETERMINISM", PK_ENABLE_DETERMINISM);
pkpy_configmacros_add(configmacros, "PK_ENABLE_WATCHDOG", PK_ENABLE_WATCHDOG); pkpy_configmacros_add(configmacros, "PK_ENABLE_WATCHDOG", PK_ENABLE_WATCHDOG);
pkpy_configmacros_add(configmacros, "PK_GC_MIN_THRESHOLD", PK_GC_MIN_THRESHOLD); pkpy_configmacros_add(configmacros, "PK_GC_MIN_THRESHOLD", PK_GC_MIN_THRESHOLD);

View File

@ -159,10 +159,15 @@ static bool Random__init__(int argc, py_Ref argv) {
static bool Random_seed(int argc, py_Ref argv) { static bool Random_seed(int argc, py_Ref argv) {
PY_CHECK_ARGC(2); PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_int);
mt19937* ud = py_touserdata(py_arg(0)); mt19937* ud = py_touserdata(py_arg(0));
py_i64 seed = py_toint(py_arg(1)); py_i64 seed;
mt19937__seed(ud, seed); if(py_isnone(&argv[1])) {
seed = time_ns();
} else {
PY_CHECK_ARG_TYPE(1, tp_int);
seed = py_toint(py_arg(1));
}
mt19937__seed(ud, (uint32_t)seed);
py_newnone(py_retval()); py_newnone(py_retval());
return true; return true;
} }
@ -329,3 +334,31 @@ __ERROR:
#undef UPPER_MASK #undef UPPER_MASK
#undef LOWER_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 "pocketpy/pocketpy.h"
#include <stdlib.h>
static bool traceback_format_exc(int argc, py_Ref argv) { static bool traceback_format_exc(int argc, py_Ref argv) {
PY_CHECK_ARGC(0); 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) { void py_newvec3(py_OutRef out, c11_vec3 v) {
out->type = tp_vec3; out->type = tp_vec3;
out->is_ptr = false; out->is_ptr = false;
c11_vec3* data = (c11_vec3*)(&out->extra); out->_vec3 = v;
*data = v;
} }
c11_vec3 py_tovec3(py_Ref self) { c11_vec3 py_tovec3(py_Ref self) {
assert(self->type == tp_vec3); assert(self->type == tp_vec3);
return *(c11_vec3*)(&self->extra); return self->_vec3;
} }
void py_newvec3i(py_OutRef out, c11_vec3i v) { void py_newvec3i(py_OutRef out, c11_vec3i v) {
out->type = tp_vec3i; out->type = tp_vec3i;
out->is_ptr = false; out->is_ptr = false;
c11_vec3i* data = (c11_vec3i*)(&out->extra); out->_vec3i = v;
*data = v;
} }
c11_vec3i py_tovec3i(py_Ref self) { c11_vec3i py_tovec3i(py_Ref self) {
assert(self->type == tp_vec3i); assert(self->type == tp_vec3i);
return *(c11_vec3i*)(&self->extra); return self->_vec3i;
} }
c11_mat3x3* py_newmat3x3(py_OutRef out) { c11_mat3x3* py_newmat3x3(py_OutRef out) {
@ -1013,6 +1011,14 @@ static bool color32__repr__(int argc, py_Ref argv) {
return true; 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) { static bool color32_ansi_fg(int argc, py_Ref argv) {
PY_CHECK_ARGC(2); PY_CHECK_ARGC(2);
c11_color32 color = py_tocolor32(argv); c11_color32 color = py_tocolor32(argv);
@ -1258,6 +1264,7 @@ void pk__add_module_vmath() {
py_bindmagic(color32, __repr__, color32__repr__); py_bindmagic(color32, __repr__, color32__repr__);
py_bindmagic(color32, __eq__, color32__eq__); py_bindmagic(color32, __eq__, color32__eq__);
py_bindmagic(color32, __ne__, color32__ne__); py_bindmagic(color32, __ne__, color32__ne__);
py_bindmagic(color32, __hash__, color32__hash__);
py_bindproperty(color32, "r", color32__r, NULL); py_bindproperty(color32, "r", color32__r, NULL);
py_bindproperty(color32, "g", color32__g, NULL); py_bindproperty(color32, "g", color32__g, NULL);
py_bindproperty(color32, "b", color32__b, NULL); py_bindproperty(color32, "b", color32__b, NULL);

View File

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

View File

@ -4,7 +4,18 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <assert.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) \ #define HASH_PROBE_1(__k, ok, i) \
ok = false; \ ok = false; \
@ -14,11 +25,9 @@
ok = true; \ ok = true; \
break; \ 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) { static void NameDict__set_capacity_and_alloc_items(NameDict* self, int val) {
self->capacity = val; self->capacity = val;
self->critical_size = val * self->load_factor; 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].key = NULL;
self->items[i].value = *py_NIL(); self->items[i].value = *py_NIL();
self->length--; self->length--;
// tidy /* tidy */
uintptr_t pre_z = i; uint32_t posToRemove = i;
uintptr_t z = (i + 1) & self->mask; uint32_t posToShift = posToRemove;
while(self->items[z].key != NULL) { while(true) {
uintptr_t h = (uintptr_t)self->items[z].key & self->mask; posToShift = (5 * posToShift + 1) & self->mask;
if(h != i) break; if(self->items[posToShift].key == NULL) break;
// std::swap(_items[pre_z], _items[z]); uintptr_t hash_z = (uintptr_t)self->items[posToShift].key;
NameDict_KV tmp = self->items[pre_z]; uintptr_t insertPos = hash_z & self->mask;
self->items[pre_z] = self->items[z]; bool cond1 = insertPos <= posToRemove;
self->items[z] = tmp; bool cond2 = posToRemove <= posToShift;
pre_z = z; if((cond1 && cond2) ||
z = (z + 1) & self->mask; // 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; return true;
} }

View File

@ -2,14 +2,16 @@
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include <assert.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); assert(self->slots == -1);
return (NameDict*)(self->flex); return (NameDict*)(self->flex);
} }
py_TValue* PyObject__slots(PyObject* self) { PK_INLINE py_TValue* PyObject__slots(PyObject* self) {
assert(self->slots >= 0); assert(self->slots >= 0);
return (py_TValue*)(self->flex); return (py_TValue*)(self->flex);
} }

View File

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

View File

@ -30,9 +30,7 @@ void py_initialize() {
bool is_little_endian = *(char*)&x == 1; bool is_little_endian = *(char*)&x == 1;
if(!is_little_endian) c11__abort("is_little_endian != true"); 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_TValue) == 24, "sizeof(py_TValue) != 24");
static_assert(sizeof(py_CFunction) <= 8, "sizeof(py_CFunction) > 8");
static_assert(sizeof(py_TValue) == 16, "sizeof(py_TValue) != 16");
static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4"); static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4");
pk_current_vm = pk_all_vm[0] = &pk_default_vm; pk_current_vm = pk_all_vm[0] = &pk_default_vm;
@ -47,6 +45,12 @@ void py_initialize() {
pk_initialized = true; 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_True() { return &_True; }
py_GlobalRef py_False() { return &_False; } 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; 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 py_pushmethod(py_Name name) {
bool ok = pk_loadmethod(py_peek(-1), 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; 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) { if(cls_var != NULL) {
switch(cls_var->type) { switch(cls_var->type) {
case tp_function: case tp_function:
@ -238,7 +256,7 @@ bool pk_loadmethod(py_StackRef self, py_Name name) {
break; break;
case tp_classmethod: case tp_classmethod:
self[0] = *py_getslot(cls_var, 0); self[0] = *py_getslot(cls_var, 0);
self[1] = pk_typeinfo(type)->self; self[1] = ti->self;
break; break;
default: c11__unreachable(); default: c11__unreachable();
} }

View File

@ -28,32 +28,8 @@ static void py_ModuleInfo__dtor(py_ModuleInfo* mi) {
c11_string__delete(mi->path); 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 pk_module__register() {
py_Type type = pk_newtype("module", tp_object, NULL, (py_Dtor)py_ModuleInfo__dtor, false, true); 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; return type;
} }
@ -85,6 +61,13 @@ py_Ref py_newmodule(const char* path) {
BinTree__set(&pk_current_vm->modules, (void*)path, py_retval()); BinTree__set(&pk_current_vm->modules, (void*)path, py_retval());
py_GlobalRef retval = py_getmodule(path); py_GlobalRef retval = py_getmodule(path);
mi->self = retval; 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; return retval;
} }
@ -146,6 +129,15 @@ int py_import(const char* path_cstr) {
return true; 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 // try import
c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP); c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
c11_string* filename = c11_string__new3("%s.py", slashed_path->data); c11_string* filename = c11_string__new3("%s.py", slashed_path->data);

View File

@ -1,13 +1,8 @@
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/objects/iterator.h"
#include "pocketpy/interpreter/vm.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) { int pk_arrayview(py_Ref self, py_TValue** p) {
if(self->type == tp_list) { if(self->type == tp_list) {
*p = py_list_data(self); *p = py_list_data(self);
@ -46,18 +41,6 @@ bool pk_wrapper__arrayequal(py_Type type, int argc, py_Ref argv) {
return true; 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) { bool pk_arraycontains(py_Ref self, py_Ref val) {
py_TValue* p; py_TValue* p;
int length = pk_arrayview(self, &p); int length = pk_arrayview(self, &p);
@ -74,25 +57,39 @@ bool pk_arraycontains(py_Ref self, py_Ref val) {
return true; 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_CHECK_ARGC(1);
*py_retval() = *argv; list_iterator* ud = py_touserdata(argv);
return true; if(ud->index < ud->vec->length) {
} py_TValue* res = c11__at(py_TValue, ud->vec, ud->index);
py_assign(py_retval(), res);
static bool array_iterator__next__(int argc, py_Ref argv) { ud->index++;
PY_CHECK_ARGC(1);
array_iterator* ud = py_touserdata(argv);
if(ud->index < ud->length) {
*py_retval() = ud->p[ud->index++];
return true; return true;
} }
return StopIteration(); return StopIteration();
} }
py_Type pk_array_iterator__register() { bool tuple_iterator__next__(int argc, py_Ref argv) {
py_Type type = pk_newtype("array_iterator", tp_object, NULL, NULL, false, true); PY_CHECK_ARGC(1);
py_bindmagic(type, __iter__, array_iterator__iter__); tuple_iterator* ud = py_touserdata(argv);
py_bindmagic(type, __next__, array_iterator__next__); 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; return type;
} }

View File

@ -5,6 +5,16 @@
#include "pocketpy/interpreter/types.h" #include "pocketpy/interpreter/types.h"
#include "pocketpy/interpreter/vm.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) { static uint32_t Dict__next_cap(uint32_t cap) {
switch(cap) { switch(cap) {
case 7: return 17; case 7: return 17;
@ -51,19 +61,36 @@ static uint32_t Dict__next_cap(uint32_t cap) {
} }
} }
static uint64_t Dict__hash_2nd(uint64_t key) {
// https://gist.github.com/badboy/6267743
typedef struct { key = (~key) + (key << 21); // key = (key << 21) - key - 1
DictEntry* curr; key = key ^ (key >> 24);
DictEntry* end; key = (key + (key << 3)) + (key << 8); // key * 265
int mode; // 0: keys, 1: values, 2: items key = key ^ (key >> 14);
} DictIterator; 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) { static void Dict__ctor(Dict* self, uint32_t capacity, int entries_capacity) {
self->length = 0; self->length = 0;
self->capacity = capacity; 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__ctor(&self->entries, sizeof(DictEntry));
c11_vector__reserve(&self->entries, entries_capacity); c11_vector__reserve(&self->entries, entries_capacity);
} }
@ -75,70 +102,118 @@ static void Dict__dtor(Dict* self) {
c11_vector__dtor(&self->entries); c11_vector__dtor(&self->entries);
} }
static bool Dict__try_get(Dict* self, py_TValue* key, DictEntry** out) { static uint32_t Dict__get_index(Dict* self, uint32_t index) {
py_i64 hash; if(self->index_is_short) {
if(!py_hash(key, &hash)) return false; uint16_t* indices = self->indices;
int idx = (uint64_t)hash % self->capacity; return indices[index];
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) { } else {
int idx2 = self->indices[idx]._[i]; uint32_t* indices = self->indices;
if(idx2 == -1) continue; 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); 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); int res = py_equal(&entry->key, key);
if(res == 1) { if(res == 1) {
*out = entry; *p_idx = idx;
*p_entry = entry;
return true; return true;
} }
if(res == -1) return false; // error 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; 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) { 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); c11_vector__clear(&self->entries);
self->length = 0; self->length = 0;
} }
static void Dict__rehash_2x(Dict* self) { static void Dict__rehash_2x(Dict* self) {
Dict old_dict = *self; Dict old_dict = *self;
uint32_t new_capacity = self->capacity; uint32_t new_capacity = Dict__next_cap(old_dict.capacity);
uint32_t mask = new_capacity - 1;
__RETRY:
// use next capacity
new_capacity = Dict__next_cap(new_capacity);
// create a new dict with new capacity // create a new dict with new capacity
Dict__ctor(self, new_capacity, old_dict.entries.capacity); Dict__ctor(self, new_capacity, old_dict.entries.capacity);
// move entries from old dict to new dict // move entries from old dict to new dict
for(int i = 0; i < old_dict.entries.length; i++) { for(int i = 0; i < old_dict.entries.length; i++) {
DictEntry* old_entry = c11__at(DictEntry, &old_dict.entries, i); DictEntry* old_entry = c11__at(DictEntry, &old_dict.entries, i);
if(py_isnil(&old_entry->key)) continue; if(py_isnil(&old_entry->key)) continue; // skip deleted
int idx = old_entry->hash % new_capacity; uint32_t idx = old_entry->hash % new_capacity;
bool success = false; while(true) {
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) { uint32_t idx2 = Dict__get_index(self, idx);
int idx2 = self->indices[idx]._[i]; if(idx2 == self->null_index_value) {
if(idx2 == -1) {
// insert new entry (empty slot)
c11_vector__push(DictEntry, &self->entries, *old_entry); 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++; self->length++;
success = true;
break; break;
} }
} // try next index
if(!success) { idx = Dict__step(idx);
Dict__dtor(self);
goto __RETRY;
} }
} }
// done
Dict__dtor(&old_dict); Dict__dtor(&old_dict);
} }
static void Dict__compact_entries(Dict* self) { 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; int n = 0;
for(int i = 0; i < self->entries.length; i++) { for(int i = 0; i < self->entries.length; i++) {
@ -153,96 +228,97 @@ static void Dict__compact_entries(Dict* self) {
} }
self->entries.length = n; self->entries.length = n;
// update indices // update indices
for(uint32_t i = 0; i < self->capacity; i++) { for(uint32_t idx = 0; idx < self->capacity; idx++) {
for(int j = 0; j < PK_DICT_MAX_COLLISION; j++) { uint32_t idx2 = Dict__get_index(self, idx);
int idx = self->indices[i]._[j]; if(idx2 == self->null_index_value) continue;
if(idx == -1) continue; Dict__set_index(self, idx, mappings[idx2]);
self->indices[i]._[j] = mappings[idx];
}
} }
PK_FREE(mappings); PK_FREE(mappings);
} }
static bool Dict__set(Dict* self, py_TValue* key, py_TValue* val) { static bool Dict__set(Dict* self, py_TValue* key, py_TValue* val) {
py_i64 hash; uint64_t hash;
if(!py_hash(key, &hash)) return false; uint32_t idx;
int idx = (uint64_t)hash % self->capacity; DictEntry* entry;
int bad_hash_count = 0; if(!Dict__probe(self, key, &hash, &idx, &entry)) return false;
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) { if(entry) {
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;
}
// update existing 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; entry->val = *val;
return true; return true;
} }
if(res == -1) return false; // error // insert new entry
// res == 0 DictEntry* new_entry = c11_vector__emplace(&self->entries);
bad_hash_count++; new_entry->hash = hash;
} new_entry->key = *key;
} new_entry->val = *val;
// no empty slot found Dict__set_index(self, idx, self->entries.length - 1);
if(bad_hash_count == PK_DICT_MAX_COLLISION) { self->length++;
// all `PK_DICT_MAX_COLLISION` slots have the same hash but different keys // check if we need to rehash
// we are unable to solve this collision via rehashing float load_factor = (float)self->length / self->capacity;
return RuntimeError("dict: %d/%d/%d: maximum collision reached (hash=%i)", if(load_factor > (self->index_is_short ? 0.3f : 0.4f)) Dict__rehash_2x(self);
self->entries.length, return true;
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);
} }
/// Delete an entry from the dict. /// Delete an entry from the dict.
/// -1: error, 0: not found, 1: found and deleted /// -1: error, 0: not found, 1: found and deleted
static int Dict__pop(Dict* self, py_Ref key) { static int Dict__pop(Dict* self, py_Ref key) {
py_i64 hash; // Dict__log_index(self, "before pop");
if(!py_hash(key, &hash)) return -1; uint64_t hash;
int idx = (uint64_t)hash % self->capacity; uint32_t idx;
for(int i = 0; i < PK_DICT_MAX_COLLISION; i++) { DictEntry* entry;
int idx2 = self->indices[idx]._[i]; if(!Dict__probe(self, key, &hash, &idx, &entry)) return -1;
if(idx2 == -1) continue; if(!entry) return 0; // not found
DictEntry* entry = c11__at(DictEntry, &self->entries, idx2);
if(entry->hash == (uint64_t)hash) { // found the entry, delete and return it
int res = py_equal(&entry->key, key); py_assign(py_retval(), &entry->val);
if(res == 1) { Dict__set_index(self, idx, self->null_index_value);
*py_retval() = entry->val;
py_newnil(&entry->key); py_newnil(&entry->key);
self->indices[idx]._[i] = -1; py_newnil(&entry->val);
self->length--; self->length--;
if(self->length < self->entries.length / 2) Dict__compact_entries(self);
/* 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++;
}
// 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; return 1;
} }
if(res == -1) return -1; // error
}
}
return 0;
}
static void DictIterator__ctor(DictIterator* self, Dict* dict, int mode) { 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->curr = dict->entries.data;
self->end = self->curr + dict->entries.length; self->end = self->curr + dict->entries.length;
self->mode = mode; self->mode = mode;
@ -257,18 +333,22 @@ static DictEntry* DictIterator__next(DictIterator* self) {
return retval; 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) { static bool dict__new__(int argc, py_Ref argv) {
py_Type cls = py_totype(argv); py_Type cls = py_totype(argv);
int slots = cls == tp_dict ? 0 : -1; int slots = cls == tp_dict ? 0 : -1;
Dict* ud = py_newobject(py_retval(), cls, slots, sizeof(Dict)); Dict* ud = py_newobject(py_retval(), cls, slots, sizeof(Dict));
Dict__ctor(ud, 7, 8); Dict__ctor(ud, 17, 4);
return true; return true;
} }
void py_newdict(py_OutRef out) { void py_newdict(py_OutRef out) {
Dict* ud = py_newobject(out, tp_dict, 0, sizeof(Dict)); 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) { 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); PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv); Dict* self = py_touserdata(argv);
Dict* new_dict = py_newobject(py_retval(), tp_dict, 0, sizeof(Dict)); Dict* new_dict = py_newobject(py_retval(), tp_dict, 0, sizeof(Dict));
new_dict->capacity = self->capacity;
new_dict->length = self->length; 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); new_dict->entries = c11_vector__copy(&self->entries);
// copy indices // copy indices
new_dict->indices = PK_MALLOC(new_dict->capacity * sizeof(DictIndex)); size_t indices_size = self->index_is_short ? self->capacity * sizeof(uint16_t)
memcpy(new_dict->indices, self->indices, new_dict->capacity * sizeof(DictIndex)); : self->capacity * sizeof(uint32_t);
new_dict->indices = PK_MALLOC(indices_size);
memcpy(new_dict->indices, self->indices, indices_size);
return true; 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); PY_CHECK_ARGC(1);
DictIterator* iter = py_touserdata(py_arg(0)); DictIterator* iter = py_touserdata(py_arg(0));
if(DictIterator__modified(iter)) return RuntimeError("dictionary modified during iteration");
DictEntry* entry = (DictIterator__next(iter)); DictEntry* entry = (DictIterator__next(iter));
if(entry) { if(entry) {
switch(iter->mode) { 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; return true;
} }
#undef PK_DICT_MAX_COLLISION #undef Dict__step

View File

@ -3,6 +3,7 @@
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include "pocketpy/interpreter/types.h" #include "pocketpy/interpreter/types.h"
#include "pocketpy/objects/iterator.h"
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
void py_newlist(py_OutRef out) { 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) { static bool list__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); 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) { 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; 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) { static bool int__divmod__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2); PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_int); PY_CHECK_ARG_TYPE(1, tp_int);
@ -245,24 +271,6 @@ static bool float__repr__(int argc, py_Ref argv) {
return true; 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) { static bool int__hash__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
py_assign(py_retval(), argv); 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) { static bool float__hash__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
py_f64 val = py_tofloat(&argv[0]); py_f64 val = py_tofloat(&argv[0]);
union c11_8bytes u = {._f64 = val}; py_i64 h_user;
py_newint(py_retval(), c11_8bytes__hash(u)); memcpy(&h_user, &val, sizeof(py_f64));
py_newint(py_retval(), h_user);
return true; return true;
} }
@ -525,6 +534,10 @@ void pk_number__register() {
py_bindmagic(tp_int, __mod__, int__mod__); py_bindmagic(tp_int, __mod__, int__mod__);
py_bindmagic(tp_int, __divmod__, int__divmod__); 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> // int.__invert__ & int.<BITWISE OP>
py_bindmagic(tp_int, __invert__, int__invert__); py_bindmagic(tp_int, __invert__, int__invert__);

View File

@ -1,4 +1,5 @@
#include "pocketpy/interpreter/typeinfo.h" #include "pocketpy/interpreter/typeinfo.h"
#include "pocketpy/interpreter/bindings.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include "pocketpy/objects/base.h" #include "pocketpy/objects/base.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
@ -77,12 +78,39 @@ bool py_iter(py_Ref val) {
int py_next(py_Ref val) { int py_next(py_Ref val) {
VM* vm = pk_current_vm; VM* vm = pk_current_vm;
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__); py_Ref tmp = py_tpfindmagic(val->type, __next__);
if(!tmp) { if(!tmp) {
TypeError("'%t' object is not an iterator", val->type); TypeError("'%t' object is not an iterator", val->type);
return -1; return -1;
} }
if(py_call(tmp, 1, val)) return 1; if(py_call(tmp, 1, val)) return 1;
break;
}
}
if(vm->curr_exception.type == tp_StopIteration) { if(vm->curr_exception.type == tp_StopIteration) {
vm->last_retval = vm->curr_exception; vm->last_retval = vm->curr_exception;
py_clearexc(NULL); py_clearexc(NULL);
@ -94,6 +122,8 @@ int py_next(py_Ref val) {
bool py_getattr(py_Ref self, py_Name name) { bool py_getattr(py_Ref self, py_Name name) {
// https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
py_TypeInfo* ti = pk_typeinfo(self->type); py_TypeInfo* ti = pk_typeinfo(self->type);
if(ti->getattribute) return ti->getattribute(self, name);
py_Ref cls_var = pk_tpfindname(ti, name); py_Ref cls_var = pk_tpfindname(ti, name);
if(cls_var) { if(cls_var) {
// handle descriptor // 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) { bool py_setattr(py_Ref self, py_Name name, py_Ref val) {
py_Type type = self->type; py_TypeInfo* ti = pk_typeinfo(self->type);
py_Ref cls_var = py_tpfindname(type, name); if(ti->setattribute) return ti->setattribute(self, name, val);
py_Ref cls_var = pk_tpfindname(ti, name);
if(cls_var) { if(cls_var) {
// handle descriptor // handle descriptor
if(py_istype(cls_var, tp_property)) { 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) { 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(self->is_ptr && self->_obj->slots == -1) {
if(py_deldict(self, name)) return true; if(py_deldict(self, name)) return true;
return AttributeError(self, name); return AttributeError(self, name);

View File

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

View File

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

View File

@ -3,6 +3,7 @@
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
#include "pocketpy/common/sstream.h" #include "pocketpy/common/sstream.h"
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/objects/iterator.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
py_ObjectRef py_newtuple(py_OutRef out, int n) { 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) { static bool tuple__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); 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) { 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; } 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); assert(self && self->is_ptr);
return NameDict__try_get(PyObject__dict(self->_obj), name); 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); assert(self && self->is_ptr);
NameDict__set(PyObject__dict(self->_obj), name, val); NameDict__set(PyObject__dict(self->_obj), name, val);
} }

View File

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

View File

@ -13,6 +13,13 @@ void py_newint(py_OutRef out, py_i64 val) {
out->_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) { void py_newfloat(py_OutRef out, py_f64 val) {
out->type = tp_float; out->type = tp_float;
out->is_ptr = false; out->is_ptr = false;

View File

@ -109,3 +109,8 @@ assert abs(0.0) == 0.0
# exit(1) # exit(1)
# except ValueError: # except ValueError:
# pass # 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', '']
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: try:
'a'.split('') 'a'.split('')
exit(1) exit(1)
@ -228,3 +231,7 @@ assert f"{(1, 2, 3)}" == "(1, 2, 3)"
# stack=[1,2,3,4] # stack=[1,2,3,4]
# assert f"{stack[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 assert a.pop(1, None) is None
n = 2 ** 17 # test getitem
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)
d = {} d = {}
for i in range(-1000, 1000): for i in range(-1000, 1000):
d[i] = i d[i] = i
@ -155,3 +132,37 @@ assert list(d) == ['1', 222, '333']
assert list(d.keys()) == ['1', 222, '333'] assert list(d.keys()) == ['1', 222, '333']
assert list(d.values()) == [1, 2, 3] assert list(d.values()) == [1, 2, 3]
assert list(d.items()) == [('1', 1), (222, 2), ('333', 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 b, c = 1, 2
d = b + c 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.a == (1, 2, 3)
assert MyClass.b == 1 assert MyClass.b == 1
assert MyClass.c == 2 assert MyClass.c == 2
assert MyClass.d == 3 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 res == [42, 42]
assert x == 33 assert x == 33
# test removing trailing newlines
assert eval('[1, 2, 3]\n \n') == [1, 2, 3]

View File

@ -158,3 +158,15 @@ a, b, c = (1, 2, 3) if True else (4, 5, 6)
assert a == 1 assert a == 1
assert b == 2 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 configmacros
if configmacros['PK_ENABLE_THREADS'] == 1:
from pkpy import ComputeThread from pkpy import ComputeThread
else:
print('threads is not enabled, skipping test...')
exit()
import time import time
thread_1 = ComputeThread(1) thread_1 = ComputeThread(1)

View File

@ -103,9 +103,4 @@ class A:
bad_dict = {A(): 1, A(): 2, A(): 3, A(): 4} bad_dict = {A(): 1, A(): 2, A(): 3, A(): 4}
assert len(bad_dict) == 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)