Compare commits

...

15 Commits

Author SHA1 Message Date
blueloveTH
8bc7bf3bd7 Update bindings-cpp.md 2026-01-06 16:36:16 +08:00
blueloveTH
3c051df06a Create periphery.md 2026-01-06 16:32:25 +08:00
blueloveTH
5f8d9fefc3 update docs 2026-01-06 16:29:31 +08:00
blueloveTH
f10cc37c63 Create deploy.md 2026-01-06 16:17:01 +08:00
blueloveTH
a1bfebd30c Update 300_import.py 2026-01-06 15:18:04 +08:00
blueloveTH
f3bf7c6321 Update compileall.py 2026-01-06 15:15:50 +08:00
blueloveTH
3e182d5c6b add tests for .pyc 2026-01-06 15:11:39 +08:00
blueloveTH
d3d83bd126 minor fix 2026-01-06 14:56:27 +08:00
blueloveTH
b37d3cae86 revert main.c readfile 2026-01-06 14:45:51 +08:00
blueloveTH
82bd2838ce fix abs name 2026-01-06 14:32:00 +08:00
blueloveTH
45f18f8431 add some more registers 2026-01-06 14:09:11 +08:00
blueloveTH
0bdbf17d46 add mark for serializer 2026-01-06 13:53:26 +08:00
blueloveTH
07a65a8f21 add py_execo 2026-01-06 13:24:06 +08:00
blueloveTH
b4ba0d91d5 minor fix 2026-01-06 13:08:14 +08:00
blueloveTH
b775d239a5 fix import pyc 2026-01-06 11:24:33 +08:00
34 changed files with 269 additions and 77 deletions

View File

@ -49,17 +49,13 @@ These platforms are officially tested.
+ iOS 64-bit
+ Emscripten 32-bit
+ Raspberry Pi OS 64-bit
+ [Luckfox Pico SDK](https://github.com/LuckfoxTECH/luckfox-pico) 32-bit
## Quick Start
You have two options to integrate pkpy into your project.
#### Use the single header file
Download the `pocketpy.h` and `pocketpy.c` on our [GitHub Release](https://github.com/pocketpy/pocketpy/releases) page.
And `#include` it in your project.
#### Use CMake
#### Use CMake (Recommended)
Clone the whole repository as a submodule into your project,
In your CMakelists.txt, add the following lines:
@ -73,6 +69,11 @@ See [CMakeLists.txt](https://github.com/pocketpy/pocketpy/blob/main/CMakeLists.t
It is safe to use `main` branch in production if CI badge is green.
#### Use the single header file
Download the `pocketpy.h` and `pocketpy.c` on our [GitHub Release](https://github.com/pocketpy/pocketpy/releases) page.
And `#include` it in your project.
### Compile Flags
To compile it with your project, these flags must be set:

View File

@ -12,8 +12,9 @@ output_dir = sys.argv[3]
def do_compile(src_path, dst_path):
assert os.path.isfile(src_path)
cmd = f'{pkpy_exe} --compile "{src_path}" "{dst_path}"'
print(src_path)
assert os.system(cmd) == 0
if os.system(cmd) != 0:
print(src_path)
exit(1)
if os.path.isfile(source_dir):
if output_dir.endswith('.py'):

View File

@ -7,6 +7,7 @@ order: 17
## Quick Start
pkpy provides a [pybind11](https://pybind11.readthedocs.io/en/stable/) compatible layer which allows users to do convenient bindings.
Header files are located in the `include/pybind11` directory. Make sure you have added `-Iinclude` to your compiler flags.
To begin with, use `py::scoped_interpreter guard{}` to start the interpreter before using any Python objects.
Or explicitly call `py::interpreter::initialize()` and `py::interpreter::finalize()`.

32
docs/features/deploy.md Normal file
View File

@ -0,0 +1,32 @@
---
icon: dot
title: Deploy Bytecodes
order: 81
---
!!!
The feature requires pocketpy version >= `2.1.7`
!!!
You can deploy your pocketpy program as bytecode files, which slightly improves the loading speed of your program.
It also makes your users unable to get your source code directly, unless they do expensive reverse engineering.
To compile a `.py` file into a `.pyc` bytecode file, you need the command-line executable `main`,
which can be simply built by running `python cmake_build.py` in the repository root.
## Example
Once you have `main` executable, you can run the following command to compile `input_file.py`:
```sh
./main --compile input_file.py output_file.pyc
```
Alternatively, you can invoke the `compileall.py` script in the repository root.
It compiles all `.py` files in the specified directory into `.pyc` files.
```sh
python compileall.py ./main input_path output_path
```

View File

@ -22,7 +22,7 @@ The easiest way to test a feature is to [try it on your browser](https://pocketp
1. Descriptor protocol `__get__` and `__set__`. However, `@property` is implemented.
2. `__slots__` in class definition.
3. `else` clause in try..except.
3. `else` and `finally` clause in try..except.
4. Inplace methods like `__iadd__` and `__imul__`.
5. `__del__` in class definition.
6. Multiple inheritance.

View File

@ -44,6 +44,7 @@ These platforms are officially tested.
+ iOS 64-bit
+ Emscripten 32-bit
+ Raspberry Pi OS 64-bit
+ [Luckfox Pico SDK](https://github.com/LuckfoxTECH/luckfox-pico) 32-bit
## Star the repo

12
docs/modules/periphery.md Normal file
View File

@ -0,0 +1,12 @@
---
icon: package
label: periphery
---
!!!
This module is optional. Set option `PK_BUILD_MODULE_PERIPHERY` to `ON` in your `CMakeLists.txt` to enable it.
!!!
#### Source code
:::code source="../../include/typings/periphery.pyi" :::

View File

@ -6,12 +6,7 @@ label: Quick Start
You have two options to integrate pkpy into your project.
#### Use the single header file
Download the `pocketpy.h` and `pocketpy.c` on our [GitHub Release](https://github.com/pocketpy/pocketpy/releases) page.
And `#include` it in your project.
#### Use CMake
#### Use CMake (Recommended)
Clone the whole repository as a submodule into your project,
In your CMakelists.txt, add the following lines:
@ -25,6 +20,11 @@ See [CMakeLists.txt](https://github.com/pocketpy/pocketpy/blob/main/CMakeLists.t
It is safe to use `main` branch in production if CI badge is green.
#### Use the single header file
Download the `pocketpy.h` and `pocketpy.c` on our [GitHub Release](https://github.com/pocketpy/pocketpy/releases) page.
And `#include` it in your project.
### Compile flags
To compile it with your project, these flags must be set:

View File

@ -12,6 +12,7 @@ typedef struct c11_serializer {
void c11_serializer__ctor(c11_serializer* self, int16_t magic, int8_t major_ver, int8_t minor_ver);
void c11_serializer__dtor(c11_serializer* self);
void c11_serializer__write_mark(c11_serializer* self, char mark);
void c11_serializer__write_cstr(c11_serializer* self, const char*);
void c11_serializer__write_bytes(c11_serializer* self, const void* data, int size);
void* c11_serializer__submit(c11_serializer* self, int* size);
@ -27,6 +28,7 @@ typedef struct c11_deserializer {
void c11_deserializer__ctor(c11_deserializer* self, const void* data, int size);
void c11_deserializer__dtor(c11_deserializer* self);
void c11_deserializer__consume_mark(c11_deserializer* self, char expected);
bool c11_deserializer__check_header(c11_deserializer* self, int16_t magic, int8_t major_ver, int8_t minor_ver_min);
const char* c11_deserializer__read_cstr(c11_deserializer* self);
void* c11_deserializer__read_bytes(c11_deserializer* self, int size);

View File

@ -56,6 +56,7 @@ c11_sv c11_sv__slice2(c11_sv sv, int start, int stop);
c11_sv c11_sv__strip(c11_sv sv, c11_sv chars, bool left, bool right);
int c11_sv__index(c11_sv self, char c);
int c11_sv__rindex(c11_sv self, char c);
c11_sv c11_sv__filename(c11_sv self);
int c11_sv__index2(c11_sv self, c11_sv sub, int start);
int c11_sv__count(c11_sv self, c11_sv sub);
bool c11_sv__startswith(c11_sv self, c11_sv prefix);

View File

@ -58,8 +58,8 @@ typedef struct VM {
int recursion_depth;
int max_recursion_depth;
py_TValue reg[8]; // users' registers
void* ctx; // user-defined context
py_TValue reg[14]; // users' registers
void* ctx; // user-defined context
CachedNames cached_names;

View File

@ -107,8 +107,8 @@ typedef struct FuncDecl {
RefCounted rc;
CodeObject code; // strong ref
c11_vector /*T=int*/ args; // indices in co->varnames
c11_vector /*T=KwArg*/ kwargs; // indices in co->varnames
c11_vector /*T=int32_t*/ args; // indices in co->varnames
c11_vector /*T=FuncDeclKwArg*/ kwargs; // indices in co->varnames
int starred_arg; // index in co->varnames, -1 if no *arg
int starred_kwarg; // index in co->varnames, -1 if no **kwarg

View File

@ -185,6 +185,8 @@ PK_API bool py_compile(const char* source,
/// Compile a `.py` file into a `.pyc` file.
PK_API bool py_compilefile(const char* src_path,
const char* dst_path) PY_RAISE;
/// Run a compiled code object.
PK_API bool py_execo(const void* data, int size, const char* filename, py_Ref module) PY_RAISE PY_RETURN;
/// Run a source string.
/// @param source source string.
/// @param filename filename (for error messages).
@ -439,6 +441,13 @@ PK_API py_GlobalRef py_retval();
#define py_r6() py_getreg(6)
#define py_r7() py_getreg(7)
#define py_tmpr0() py_getreg(8)
#define py_tmpr1() py_getreg(9)
#define py_tmpr2() py_getreg(10)
#define py_tmpr3() py_getreg(11)
#define py_sysr0() py_getreg(12) // for debugger
#define py_sysr1() py_getreg(13) // for pybind11
/// Get an item from the object's `__dict__`.
/// Return `NULL` if not found.
PK_API py_ItemRef py_getdict(py_Ref self, py_Name name);

View File

@ -16,6 +16,7 @@ OPCODE(LOAD_TRUE)
OPCODE(LOAD_FALSE)
/**************************/
OPCODE(LOAD_SMALL_INT)
OPCODE(LOAD_NAME_AS_INT)
/**************************/
OPCODE(LOAD_ELLIPSIS)
OPCODE(LOAD_FUNCTION)

View File

@ -44,8 +44,7 @@ struct object_pool {
};
static void initialize(int size) noexcept {
// use 8th register.
pool = py_getreg(7);
pool = py_sysr1();
py_newtuple(pool, size);
indices_ = new std::vector<int>(size, 0);
}

View File

@ -3,14 +3,12 @@ import sys
import time
import subprocess
pkpy_exe = 'main.exe' if sys.platform == 'win32' else './main'
def test_file(filepath, cpython=False):
if cpython:
return os.system("python " + filepath) == 0
if sys.platform == 'win32':
code = os.system("main.exe " + filepath)
else:
code = os.system("./main " + filepath)
code = os.system(pkpy_exe + ' ' + filepath)
if code != 0:
print('Return code:', code)
return code == 0
@ -18,7 +16,7 @@ def test_file(filepath, cpython=False):
def test_dir(path):
print("Testing directory:", path)
for filename in sorted(os.listdir(path)):
if not filename.endswith('.py'):
if not filename.endswith('.py') and not filename.endswith('.pyc'):
continue
filepath = os.path.join(path, filename)
print("> " + filepath, flush=True)
@ -76,6 +74,8 @@ exit()
print(res.stdout)
exit(1)
code = os.system(f'python compileall.py {pkpy_exe} tests tmp/tests')
assert code == 0
if len(sys.argv) == 2:
assert 'benchmark' in sys.argv[1]
@ -84,5 +84,11 @@ else:
test_dir('tests/')
test_repl()
if os.path.exists('tmp/tests'):
print('-' * 50)
time.sleep(3)
test_dir('tmp/tests/')
print("ALL TESTS PASSED")

View File

@ -25,6 +25,10 @@ void c11_serializer__dtor(c11_serializer* self){
c11_vector__dtor(&self->data);
}
void c11_serializer__write_mark(c11_serializer *self, char mark) {
c11_serializer__write_i8(self, (int8_t)mark);
}
void c11_serializer__write_cstr(c11_serializer *self, const char* cstr) {
int len = (int)strlen(cstr);
c11_serializer__write_bytes(self, cstr, len + 1);
@ -56,6 +60,13 @@ bool c11_deserializer__error(c11_deserializer* self, const char* msg){
return false;
}
void c11_deserializer__consume_mark(c11_deserializer* self, char expected) {
int8_t mark = c11_deserializer__read_i8(self);
if (mark != (int8_t)expected) {
c11__abort("internal error: deserialize mark mismatch: expected %c but got %c", expected, (char)mark);
}
}
bool c11_deserializer__check_header(c11_deserializer* self, int16_t magic, int8_t major_ver, int8_t minor_ver_min){
if(self->size < 8){
return c11_deserializer__error(self, "bad header: size < 8");

View File

@ -89,9 +89,9 @@ void SourceData__snapshot(const struct SourceData* self,
c11_sbuf__write_cstr(ss, name);
}
c11_sbuf__write_char(ss, '\n');
const char *st = NULL, *ed;
if(SourceData__get_line(self, lineno, &st, &ed)) {
c11_sbuf__write_char(ss, '\n');
while(st < ed && isblank(*st))
++st;
if(st < ed) {
@ -108,5 +108,5 @@ void SourceData__snapshot(const struct SourceData* self,
}
}
if(!st) { c11_sbuf__write_cstr(ss, " <?>"); }
// if(!st) { c11_sbuf__write_cstr(ss, " <?>"); }
}

View File

@ -147,6 +147,14 @@ int c11_sv__rindex(c11_sv self, char c) {
return -1;
}
c11_sv c11_sv__filename(c11_sv self) {
int sep_index_1 = c11_sv__rindex(self, '/');
int sep_index_2 = c11_sv__rindex(self, '\\');
int sep_index = c11__max(sep_index_1, sep_index_2);
if(sep_index == -1) return self;
return c11_sv__slice(self, sep_index + 1);
}
int c11_sv__index2(c11_sv self, c11_sv sub, int start) {
if(sub.size == 0) return start;
int max_end = self.size - sub.size;

View File

@ -78,6 +78,7 @@ int c11_vector__nextcap(c11_vector* self) {
}
void c11_vector__extend(c11_vector* self, const void* p, int size) {
if (size <= 0) return;
int min_capacity = self->length + size;
if(self->capacity < min_capacity) {
int nextcap = c11_vector__nextcap(self);

View File

@ -77,6 +77,7 @@ static int Ctx__enter_block(Ctx* self, CodeBlockType type);
static void Ctx__exit_block(Ctx* self);
static int Ctx__emit_(Ctx* self, Opcode opcode, uint16_t arg, int line);
static int Ctx__emit_int(Ctx* self, int64_t value, int line);
static int Ctx__emit_name(Ctx* self, py_Name name, int line);
static void Ctx__patch_jump(Ctx* self, int index);
static void Ctx__emit_jump(Ctx* self, int target, int line);
static int Ctx__add_varname(Ctx* self, py_Name name);
@ -1070,7 +1071,12 @@ void CallExpr__emit_(Expr* self_, Ctx* ctx) {
c11__foreach(Expr*, &self->args, e) { vtemit_(*e, ctx); }
c11__foreach(CallExprKwArg, &self->kwargs, e) {
Ctx__emit_int(ctx, (uintptr_t)e->key, self->line);
if(e->key == 0) {
// special key for **kwargs
Ctx__emit_int(ctx, 0, self->line);
} else {
Ctx__emit_name(ctx, e->key, self->line);
}
vtemit_(e->val, ctx);
}
int KWARGC = self->kwargs.length;
@ -1196,6 +1202,12 @@ static int Ctx__emit_int(Ctx* self, int64_t value, int line) {
}
}
static int Ctx__emit_name(Ctx* self, py_Name name, int line) {
int index = Ctx__add_name(self, name);
assert(index <= UINT16_MAX);
return Ctx__emit_(self, OP_LOAD_NAME_AS_INT, (uint16_t)index, line);
}
static void Ctx__patch_jump(Ctx* self, int index) {
Bytecode* co_codes = (Bytecode*)self->co->codes.data;
int target = self->co->codes.length;
@ -1214,7 +1226,10 @@ static int Ctx__add_varname(Ctx* self, py_Name name) {
return CodeObject__add_varname(self->co, name);
}
static int Ctx__add_name(Ctx* self, py_Name name) { return CodeObject__add_name(self->co, name); }
static int Ctx__add_name(Ctx* self, py_Name name) {
assert(name != 0);
return CodeObject__add_name(self->co, name);
}
static int Ctx__add_const_string(Ctx* self, c11_sv key) {
if(key.size > 100) {

View File

@ -46,7 +46,7 @@ static struct c11_debugger {
c11_vector py_frames;
c11_smallmap_d2index scopes_query_cache;
#define python_vars py_r7()
#define python_vars py_sysr0()
} debugger;

View File

@ -189,6 +189,11 @@ __NEXT_STEP:
py_newint(SP()++, (int16_t)byte.arg);
DISPATCH();
}
case OP_LOAD_NAME_AS_INT: {
py_Name name = co_names[byte.arg];
py_newint(SP()++, (uintptr_t)name);
DISPATCH();
}
/*****************************************/
case OP_LOAD_ELLIPSIS: {
py_newellipsis(SP()++);

View File

@ -99,6 +99,8 @@ void VM__ctor(VM* self) {
self->recursion_depth = 0;
self->max_recursion_depth = 1000;
memset(self->reg, 0, sizeof(self->reg));
self->ctx = NULL;
self->curr_class = NULL;
self->curr_decl_based_function = NULL;

View File

@ -114,10 +114,8 @@ py_Ref py_name2ref(py_Name name) {
py_Ref res = CachedNames__try_get(d, name);
if(res != NULL) return res;
// not found, create a new one
py_StackRef tmp = py_pushtmp();
py_newstrv(tmp, py_name2sv(name));
CachedNames__set(d, name, tmp);
py_pop();
py_newstrv(py_tmpr0(), py_name2sv(name));
CachedNames__set(d, name, py_tmpr0());
return CachedNames__try_get(d, name);
}

View File

@ -72,7 +72,7 @@ static bool disassemble(CodeObject* co) {
pk_sprintf(&ss, " (%q)", py_tosv(path));
break;
}
case OP_LOAD_NAME:
case OP_LOAD_NAME: case OP_LOAD_NAME_AS_INT:
case OP_LOAD_GLOBAL:
case OP_LOAD_NONLOCAL:
case OP_STORE_GLOBAL:

View File

@ -19,7 +19,6 @@ static void CodeObject__serialize(c11_serializer* s,
static CodeObject CodeObject__deserialize(c11_deserializer* d, const char* filename, SourceData_ embedded_src);
// Serialize a py_TValue constant
// Supported types: None, int, float, bool, str, bytes, tuple, Ellipsis
static void TValue__serialize(c11_serializer* s, py_Ref val) {
c11_serializer__write_type(s, val->type);
// 1. co_consts: int | float | str
@ -116,33 +115,43 @@ static void CodeObject__serialize(c11_serializer* s,
// codes
_Static_assert(sizeof(Bytecode) == sizeof(uint16_t) * 2, "");
c11_serializer__write_i32(s, co->codes.length);
c11_serializer__write_mark(s, '[');
c11_serializer__write_bytes(s, co->codes.data, co->codes.length * sizeof(Bytecode));
c11_serializer__write_mark(s, ']');
// codes_ex
_Static_assert(sizeof(BytecodeEx) == sizeof(int32_t) * 2, "");
c11_serializer__write_i32(s, co->codes_ex.length);
c11_serializer__write_mark(s, '[');
c11_serializer__write_bytes(s, co->codes_ex.data, co->codes_ex.length * sizeof(BytecodeEx));
c11_serializer__write_mark(s, ']');
// consts
c11_serializer__write_i32(s, co->consts.length);
c11_serializer__write_mark(s, '[');
for(int i = 0; i < co->consts.length; i++) {
py_Ref val = c11__at(py_TValue, &co->consts, i);
TValue__serialize(s, val);
}
c11_serializer__write_mark(s, ']');
// varnames (as cstr via py_name2str)
c11_serializer__write_i32(s, co->varnames.length);
c11_serializer__write_mark(s, '[');
for(int i = 0; i < co->varnames.length; i++) {
py_Name name = c11__getitem(py_Name, &co->varnames, i);
c11_serializer__write_cstr(s, py_name2str(name));
}
c11_serializer__write_mark(s, ']');
// names (as cstr via py_name2str)
c11_serializer__write_i32(s, co->names.length);
c11_serializer__write_mark(s, '[');
for(int i = 0; i < co->names.length; i++) {
py_Name name = c11__getitem(py_Name, &co->names, i);
c11_serializer__write_cstr(s, py_name2str(name));
}
c11_serializer__write_mark(s, ']');
// nlocals
c11_serializer__write_i32(s, co->nlocals);
@ -150,14 +159,19 @@ static void CodeObject__serialize(c11_serializer* s,
// blocks
_Static_assert(sizeof(CodeBlock) == sizeof(int32_t) * 5, "");
c11_serializer__write_i32(s, co->blocks.length);
c11_serializer__write_mark(s, '[');
c11_serializer__write_bytes(s, co->blocks.data, co->blocks.length * sizeof(CodeBlock));
c11_serializer__write_mark(s, ']');
// func_decls
c11_serializer__write_i32(s, co->func_decls.length);
c11_serializer__write_mark(s, '[');
for(int i = 0; i < co->func_decls.length; i++) {
const FuncDecl* decl = c11__getitem(FuncDecl_, &co->func_decls, i);
FuncDecl__serialize(s, decl, co->src);
c11_serializer__write_mark(s, '|');
}
c11_serializer__write_mark(s, ']');
// start_line, end_line
c11_serializer__write_i32(s, co->start_line);
@ -191,54 +205,70 @@ static CodeObject CodeObject__deserialize(c11_deserializer* d, const char* filen
// codes
int codes_len = c11_deserializer__read_i32(d);
c11_deserializer__consume_mark(d, '[');
c11_vector__extend(&co.codes,
c11_deserializer__read_bytes(d, codes_len * sizeof(Bytecode)),
codes_len);
c11_deserializer__consume_mark(d, ']');
// codes_ex
int codes_ex_len = c11_deserializer__read_i32(d);
c11_deserializer__consume_mark(d, '[');
c11_vector__extend(&co.codes_ex,
c11_deserializer__read_bytes(d, codes_ex_len * sizeof(BytecodeEx)),
codes_ex_len);
c11_deserializer__consume_mark(d, ']');
// consts
int consts_len = c11_deserializer__read_i32(d);
c11_deserializer__consume_mark(d, '[');
for(int i = 0; i < consts_len; i++) {
py_Ref p_val = c11_vector__emplace(&co.consts);
TValue__deserialize(d, p_val);
}
c11_deserializer__consume_mark(d, ']');
// varnames
int varnames_len = c11_deserializer__read_i32(d);
c11_deserializer__consume_mark(d, '[');
for(int i = 0; i < varnames_len; i++) {
const char* s = c11_deserializer__read_cstr(d);
py_Name n = py_name(s);
c11_vector__push(py_Name, &co.varnames, n);
c11_smallmap_n2d__set(&co.varnames_inv, n, i);
}
c11_deserializer__consume_mark(d, ']');
// names
int names_len = c11_deserializer__read_i32(d);
c11_deserializer__consume_mark(d, '[');
for(int i = 0; i < names_len; i++) {
const char* s = c11_deserializer__read_cstr(d);
py_Name n = py_name(s);
c11_vector__push(py_Name, &co.names, n);
c11_smallmap_n2d__set(&co.names_inv, n, i);
}
c11_deserializer__consume_mark(d, ']');
// nlocals
co.nlocals = c11_deserializer__read_i32(d);
// blocks
int blocks_len = c11_deserializer__read_i32(d);
c11_deserializer__consume_mark(d, '[');
c11_vector__extend(&co.blocks,
c11_deserializer__read_bytes(d, blocks_len * sizeof(CodeBlock)),
blocks_len);
c11_deserializer__consume_mark(d, ']');
// func_decls
int func_decls_len = c11_deserializer__read_i32(d);
c11_deserializer__consume_mark(d, '[');
for(int i = 0; i < func_decls_len; i++) {
FuncDecl_ decl = FuncDecl__deserialize(d, src);
c11_vector__push(FuncDecl_, &co.func_decls, decl);
c11_deserializer__consume_mark(d, '|');
}
c11_deserializer__consume_mark(d, ']');
// start_line, end_line
co.start_line = c11_deserializer__read_i32(d);
@ -252,20 +282,26 @@ static void FuncDecl__serialize(c11_serializer* s,
const FuncDecl* decl,
const struct SourceData* parent_src) {
// CodeObject (embedded)
c11_serializer__write_mark(s, '{');
CodeObject__serialize(s, &decl->code, parent_src);
c11_serializer__write_mark(s, '}');
// args
c11_serializer__write_i32(s, decl->args.length);
c11_serializer__write_mark(s, '[');
c11_serializer__write_bytes(s, decl->args.data, decl->args.length * sizeof(int32_t));
c11_serializer__write_mark(s, ']');
// kwargs
c11_serializer__write_i32(s, decl->kwargs.length);
c11_serializer__write_mark(s, '[');
for(int i = 0; i < decl->kwargs.length; i++) {
FuncDeclKwArg* kw = c11__at(FuncDeclKwArg, &decl->kwargs, i);
c11_serializer__write_i32(s, kw->index);
c11_serializer__write_cstr(s, py_name2str(kw->key));
TValue__serialize(s, &kw->value);
}
c11_serializer__write_mark(s, ']');
// starred_arg, starred_kwarg
c11_serializer__write_i32(s, decl->starred_arg);
@ -285,7 +321,7 @@ static void FuncDecl__serialize(c11_serializer* s,
// Deserialize FuncDecl
static FuncDecl_ FuncDecl__deserialize(c11_deserializer* d, SourceData_ embedded_src) {
FuncDecl* self = PK_MALLOC(sizeof(FuncDecl));
FuncDecl_ self = PK_MALLOC(sizeof(FuncDecl));
self->rc.count = 1;
self->rc.dtor = (void (*)(void*))FuncDecl__dtor;
@ -294,16 +330,21 @@ static FuncDecl_ FuncDecl__deserialize(c11_deserializer* d, SourceData_ embedded
c11_smallmap_n2d__ctor(&self->kw_to_index);
// CodeObject (embedded)
c11_deserializer__consume_mark(d, '{');
self->code = CodeObject__deserialize(d, NULL, embedded_src);
c11_deserializer__consume_mark(d, '}');
// args
int args_len = c11_deserializer__read_i32(d);
c11_deserializer__consume_mark(d, '[');
c11_vector__extend(&self->args,
c11_deserializer__read_bytes(d, args_len * sizeof(int32_t)),
args_len);
c11_deserializer__consume_mark(d, ']');
// kwargs
int kwargs_len = c11_deserializer__read_i32(d);
c11_deserializer__consume_mark(d, '[');
for(int i = 0; i < kwargs_len; i++) {
FuncDeclKwArg* kw = c11_vector__emplace(&self->kwargs);
kw->index = c11_deserializer__read_i32(d);
@ -312,6 +353,8 @@ static FuncDecl_ FuncDecl__deserialize(c11_deserializer* d, SourceData_ embedded
TValue__deserialize(d, &kw->value);
c11_smallmap_n2d__set(&self->kw_to_index, kw->key, kw->index);
}
c11_deserializer__consume_mark(d, ']');
// starred_arg
self->starred_arg = c11_deserializer__read_i32(d);
// starred_kwarg

View File

@ -149,6 +149,20 @@ bool py_exec(const char* source, const char* filename, enum py_CompileMode mode,
return ok;
}
bool py_execo(const void* data, int size, const char* filename, py_Ref module) {
CodeObject co;
char* err = CodeObject__loads(data, size, filename, &co);
if(err == NULL) {
c11__rtassert(co.src->mode == EXEC_MODE);
c11__rtassert(co.src->is_dynamic == false);
bool ok = pk_exec(&co, module);
CodeObject__dtor(&co);
return ok;
} else {
return RuntimeError("bad code object %s: %s", filename, err);
}
}
bool py_eval(const char* source, py_Ref module) {
return py_exec(source, "<string>", EVAL_MODE, module);
}

View File

@ -1,8 +1,8 @@
#include "pocketpy/interpreter/vm.h"
py_Ref py_getreg(int i) { return pk_current_vm->reg + i; }
PK_INLINE 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; }
PK_INLINE void py_setreg(int i, py_Ref val) { pk_current_vm->reg[i] = *val; }
PK_INLINE py_Ref py_retval() { return &pk_current_vm->last_retval; }

View File

@ -78,8 +78,10 @@ int py_import(const char* path_cstr) {
while(dot_count < path.size && path.data[dot_count] == '.')
dot_count++;
c11_sv top_filename = c11_string__sv(vm->top_frame->co->src->filename);
int is_init = c11_sv__endswith(top_filename, (c11_sv){"__init__.py", 11});
// */__init__.py[c]
c11_sv top_filepath = c11_string__sv(vm->top_frame->co->src->filename);
c11_sv top_filename = c11_sv__filename(top_filepath);
int is_init = c11__sveq2(top_filename, "__init__.py") || c11__sveq2(top_filename, "__init__.pyc");
py_ModuleInfo* mi = py_touserdata(vm->top_frame->module);
c11_sv package_sv = c11_string__sv(mi->path);
@ -186,20 +188,10 @@ __SUCCESS:
bool ok;
if(is_pyc) {
CodeObject co;
char* err = CodeObject__loads(data, data_size, filename->data, &co);
if(err == NULL) {
c11__rtassert(co.src->mode == EXEC_MODE);
c11__rtassert(co.src->is_dynamic == false);
ok = pk_exec(&co, mod);
} else {
RuntimeError("failed to load %s: %s", filename->data, err);
ok = false;
}
ok = py_execo(data, data_size, filename->data, mod);
} else {
ok = py_exec(data, filename->data, EXEC_MODE, mod);
}
py_assign(py_retval(), mod);
c11_string__delete(filename);

View File

@ -9,18 +9,17 @@
#include <windows.h>
#endif
static char* read_file(const char* path) {
FILE* file = fopen(path, "rb");
if(file == NULL) {
printf("Error: file not found\n");
return NULL;
}
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
static char* readfile(const char* path, int* data_size) {
FILE* f = fopen(path, "rb");
if(f == NULL) return NULL;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
char* buffer = PK_MALLOC(size + 1);
size = fread(buffer, 1, size, file);
size = fread(buffer, 1, size, f);
buffer[size] = 0;
fclose(f);
if(data_size) *data_size = (int)size;
return buffer;
}
@ -115,9 +114,28 @@ int main(int argc, char** argv) {
if(profile) py_profiler_begin();
if(debug) py_debugger_waitforattach("127.0.0.1", 6110);
char* source = read_file(filename);
if(source) {
if(!py_exec(source, filename, EXEC_MODE, NULL)) py_printexc();
int data_size;
char* data = readfile(filename, &data_size);
// check filename endswith .pyc
bool is_pyc = false;
int filename_len = (int)strlen(filename);
if(filename_len >= 4) {
if(filename[filename_len - 4] == '.' &&
filename[filename_len - 3] == 'p' &&
filename[filename_len - 2] == 'y' &&
filename[filename_len - 1] == 'c') {
is_pyc = true;
}
}
if(data) {
bool ok;
if(is_pyc) {
ok = py_execo(data, data_size, filename, NULL);
} else {
ok = py_exec(data, filename, EXEC_MODE, NULL);
}
if(!ok) py_printexc();
if(profile) {
char* json_report = py_profiler_report();
@ -128,8 +146,11 @@ int main(int argc, char** argv) {
}
PK_FREE(json_report);
}
PK_FREE(source);
PK_FREE(data);
} else {
printf("Error: cannot open file '%s'\n", filename);
py_finalize();
return 1;
}
}

View File

@ -3,7 +3,14 @@ try:
except ImportError:
exit(0)
os.chdir('tests')
import sys
is_pyc = sys.argv[0].endswith('.pyc')
if is_pyc:
os.chdir('tmp/tests')
else:
os.chdir('tests')
assert os.getcwd().endswith('tests')
import test1
@ -49,8 +56,11 @@ from math import (
cos
)
# test reload (dummy)
import importlib
importlib.reload(test2.a)
assert __import__('math').pi > 3
# test reload (dummy)
if not is_pyc:
import importlib
importlib.reload(test2.a)

View File

@ -1,5 +1,7 @@
import sys
filename = 'tests/801_sys.py'
assert (sys.argv == [filename]), sys.argv
assert (sys.argv in [
['tests/801_sys.py'],
['tmp/tests/801_sys.pyc'],
]), sys.argv

View File

@ -1,4 +1,8 @@
import traceback
import sys
if sys.argv[0].endswith('.pyc'):
exit()
try:
a = {'123': 4}
@ -7,7 +11,7 @@ except KeyError:
actual = traceback.format_exc()
expected = '''Traceback (most recent call last):
File "tests/802_traceback.py", line 5
File "tests/802_traceback.py", line 9
b = a[6]
KeyError: 6'''