codecin 5.5.3__tar.gz → 5.7.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codecin-5.5.3 → codecin-5.7.4}/CHANGELOG.md +201 -0
- {codecin-5.5.3/codecin.egg-info → codecin-5.7.4}/PKG-INFO +3 -2
- {codecin-5.5.3 → codecin-5.7.4}/README.md +2 -1
- {codecin-5.5.3 → codecin-5.7.4}/codecin/__init__.py +1 -1
- {codecin-5.5.3 → codecin-5.7.4}/codecin/aot.py +182 -2
- {codecin-5.5.3 → codecin-5.7.4}/codecin/assembler.py +5 -5
- {codecin-5.5.3 → codecin-5.7.4}/codecin/cin.py +1069 -75
- {codecin-5.5.3 → codecin-5.7.4}/codecin/cli.py +35 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/cpu.py +33 -17
- {codecin-5.5.3 → codecin-5.7.4}/codecin/debugger.py +39 -7
- {codecin-5.5.3 → codecin-5.7.4}/codecin/isa.py +46 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/jit.py +7 -1
- codecin-5.7.4/codecin/lib/bigint.cin +589 -0
- codecin-5.7.4/codecin/lib/bitset.cin +374 -0
- codecin-5.7.4/codecin/lib/codec.cin +563 -0
- codecin-5.7.4/codecin/lib/combin.cin +414 -0
- codecin-5.7.4/codecin/lib/csv.cin +405 -0
- codecin-5.7.4/codecin/lib/dp.cin +325 -0
- codecin-5.7.4/codecin/lib/fmt.cin +325 -0
- codecin-5.7.4/codecin/lib/frac.cin +252 -0
- codecin-5.7.4/codecin/lib/graph.cin +367 -0
- codecin-5.7.4/codecin/lib/heap.cin +199 -0
- codecin-5.7.4/codecin/lib/key.cin +54 -0
- codecin-5.7.4/codecin/lib/path.cin +372 -0
- codecin-5.7.4/codecin/lib/set.cin +269 -0
- codecin-5.7.4/codecin/lib/text.cin +323 -0
- codecin-5.7.4/codecin/lib/token.cin +183 -0
- codecin-5.7.4/codecin/lib/tree.cin +256 -0
- codecin-5.7.4/codecin/lib/unionfind.cin +115 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/memory.py +17 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/compiler/codegen.go +232 -31
- codecin-5.7.4/codecin/native/compiler/compile_test.go +1069 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/compiler/parser.go +269 -9
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/compiler/syscalls.go +39 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/compiler/tokenizer.go +61 -3
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/compiler/types.go +12 -8
- codecin-5.7.4/codecin/native/engine/host_test.go +139 -0
- codecin-5.7.4/codecin/native/engine/hostcrypto.go +30 -0
- codecin-5.7.4/codecin/native/engine/hostdesktop.go +173 -0
- codecin-5.7.4/codecin/native/engine/hostnet.go +77 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/isa_gen.go +39 -0
- codecin-5.7.4/codecin/native/engine/keyboard.go +269 -0
- codecin-5.7.4/codecin/native/engine/keyboard_other.go +87 -0
- codecin-5.7.4/codecin/native/engine/keyboard_stub.go +11 -0
- codecin-5.7.4/codecin/native/engine/keyboard_termios_darwin.go +9 -0
- codecin-5.7.4/codecin/native/engine/keyboard_termios_linux.go +9 -0
- codecin-5.7.4/codecin/native/engine/keyboard_test.go +219 -0
- codecin-5.7.4/codecin/native/engine/keyboard_windows.go +70 -0
- codecin-5.7.4/codecin/native/engine/mem_other.go +39 -0
- codecin-5.7.4/codecin/native/engine/mem_windows.go +35 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/system.go +126 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/termux.go +67 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/version_gen.go +1 -1
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/vm.go +160 -31
- codecin-5.7.4/codecin/version.py +274 -0
- {codecin-5.5.3 → codecin-5.7.4/codecin.egg-info}/PKG-INFO +3 -2
- {codecin-5.5.3 → codecin-5.7.4}/codecin.egg-info/SOURCES.txt +63 -0
- codecin-5.7.4/examples/enum_range_for.cin +64 -0
- codecin-5.7.4/examples/system_interaction.cin +73 -0
- {codecin-5.5.3 → codecin-5.7.4}/misc/vim/syntax/cin.vim +18 -0
- codecin-5.7.4/misc/vscode-cin/.vscodeignore +4 -0
- codecin-5.7.4/misc/vscode-cin/README.md +34 -0
- codecin-5.7.4/misc/vscode-cin/build.bat +3 -0
- codecin-5.7.4/misc/vscode-cin/language-configuration.json +87 -0
- codecin-5.7.4/misc/vscode-cin/package.json +52 -0
- codecin-5.7.4/misc/vscode-cin/snippets/cin.json +113 -0
- codecin-5.7.4/misc/vscode-cin/syntaxes/cin.tmLanguage.json +254 -0
- {codecin-5.5.3 → codecin-5.7.4}/pyproject.toml +3 -0
- codecin-5.7.4/script/bump_version.py +349 -0
- codecin-5.7.4/tests/test_aggregate_and_const.py +377 -0
- codecin-5.7.4/tests/test_all_libs_together.py +104 -0
- codecin-5.7.4/tests/test_aot.py +433 -0
- codecin-5.7.4/tests/test_cin_host_ext.py +378 -0
- codecin-5.7.4/tests/test_cin_syntax_ext.py +533 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_compiler_errors.py +8 -7
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_examples.py +1 -0
- codecin-5.7.4/tests/test_keyboard.py +133 -0
- codecin-5.7.4/tests/test_lib_bigint.py +322 -0
- codecin-5.7.4/tests/test_lib_bitset.py +472 -0
- codecin-5.7.4/tests/test_lib_codec.py +164 -0
- codecin-5.7.4/tests/test_lib_combin.py +276 -0
- codecin-5.7.4/tests/test_lib_csv.py +241 -0
- codecin-5.7.4/tests/test_lib_dp.py +256 -0
- codecin-5.7.4/tests/test_lib_fmt.py +230 -0
- codecin-5.7.4/tests/test_lib_frac.py +201 -0
- codecin-5.7.4/tests/test_lib_graph.py +353 -0
- codecin-5.7.4/tests/test_lib_heap.py +234 -0
- codecin-5.7.4/tests/test_lib_path.py +250 -0
- codecin-5.7.4/tests/test_lib_set.py +294 -0
- codecin-5.7.4/tests/test_lib_text.py +246 -0
- codecin-5.7.4/tests/test_lib_token.py +158 -0
- codecin-5.7.4/tests/test_lib_tree.py +130 -0
- codecin-5.7.4/tests/test_lib_unionfind.py +150 -0
- codecin-5.7.4/tests/test_p0_fixes.py +267 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_paths_consistency.py +1 -0
- codecin-5.7.4/tests/test_struct_value_semantics.py +440 -0
- codecin-5.7.4/tests/test_version.py +424 -0
- codecin-5.5.3/codecin/native/compiler/compile_test.go +0 -368
- codecin-5.5.3/examples/system_interaction.cin +0 -36
- codecin-5.5.3/tests/test_aot.py +0 -206
- codecin-5.5.3/tests/test_version.py +0 -58
- {codecin-5.5.3 → codecin-5.7.4}/LICENSE +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/MANIFEST.in +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/cache.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/config.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/console.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/crom.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/disasm.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/errors.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/array.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/bits.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/conv.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/gui.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/hash.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/io.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/json.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/math.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/matrix.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/queue.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/rand.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/sort.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/stat.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/str.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/termux.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/test.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/time.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/validate.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/lib/vec.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/logger.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/aot/aot.go +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/aot/stub_main.go.txt +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/build.ps1 +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/build.sh +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/audio.go +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/audio_other.go +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/audio_windows.go +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/canvas.go +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/crom.go +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/encode.go +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/engine/engine_test.go +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/go.mod +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/ir/ir.go +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native/main.go +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/native.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/registers.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin/stats.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin.egg-info/dependency_links.txt +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin.egg-info/entry_points.txt +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin.egg-info/requires.txt +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/codecin.egg-info/top_level.txt +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/examples/asm_constants.asm +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/examples/bitwise_builtins.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/examples/control_flow.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/examples/literals_types.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/examples/modules_demo.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/examples/stdlib_demo.cin +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/misc/vim/ftdetect/codecin.vim +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/misc/vim/syntax/codecinasm.vim +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/script/check_paths.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/script/gen_isa_docs.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/script/gen_native_isa.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/setup.cfg +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/setup.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_assembler_ext.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_cache.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_cin_host.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_cin_new_features.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_cin_syntax.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_cin_system.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_cli.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_cpu_interpreted.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_debugger.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_features_ab.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_features_crom_mmu.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_features_import.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_features_importloc.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_features_mmu.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_features_remote.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_index_subscript_type.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_isa_dispatch.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_isa_single_source.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_libs.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_libs_ext.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_literals_and_bom.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_memory_protection.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_native_hardening.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_native_lib_lookup.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_nested_subscript_codegen.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_no_go_cli.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_packaging.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_switch_semantics.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_three_paths.py +0 -0
- {codecin-5.5.3 → codecin-5.7.4}/tests/test_workflows.py +0 -0
|
@@ -5,6 +5,207 @@
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## [5.7.4] - 2026-09-28
|
|
9
|
+
|
|
10
|
+
一次「静默错误清零 + 标准库扩容」的发布:修掉一族会**静默破坏内存/静默给错结果**的
|
|
11
|
+
struct 缺陷与两处**同一程序两条路径结论不同**的分歧,并把官方标准库从 20 个扩到 36 个。
|
|
12
|
+
|
|
13
|
+
### 新增 (Added)
|
|
14
|
+
|
|
15
|
+
- **标准库 20 → 36 个**(全部为纯 CIN,只调用语言内建,三条执行路径一致):
|
|
16
|
+
- 数据结构与算法:`tree.cin`(`tree_`)、`unionfind.cin`(`uf_`)、`heap.cin`(`heap_`)、
|
|
17
|
+
`graph.cin`(`graph_`)、`dp.cin`(`dp_`)、`set.cin`(`set_`)、`bitset.cin`(`bs_`)
|
|
18
|
+
- 文本与格式化:`text.cin`(`txt_`)、`token.cin`(`tok_`)、`fmt.cin`(`fmt_`)、`csv.cin`(`csv_`)
|
|
19
|
+
- 数值与科学计算:`bigint.cin`(`bi_`,任意精度)、`frac.cin`(`fr_`,有理数)、
|
|
20
|
+
`combin.cin`(`comb_`,数论与组合)
|
|
21
|
+
- 工程与系统:`path.cin`(`path_`,纯字符串路径)、`codec.cin`(`codec_`,十六进制/URL/
|
|
22
|
+
Base64/RLE/摩尔斯/XOR)
|
|
23
|
+
- **`const` 命名常量**:`const int N = 4`(全局与局部),支持 `int/float/bool/string`,
|
|
24
|
+
纯编译期、不占数据段,可用于**数组维度**与任意表达式;可被同名局部变量遮蔽。
|
|
25
|
+
- **数组维度接受整型常量表达式**:`int a[N]`、`int a[2 + 3]`、`int a[enumMember + 1]`。
|
|
26
|
+
- **struct 聚合初始化**:`P p = {3, 4}`、`P ps[2] = {{1,2},{3,4}}`、嵌套 `Out o = {{5,6},7}`;
|
|
27
|
+
未列出的字段取默认值,初始化器过多是编译期错误。
|
|
28
|
+
- **`string +=`**:右侧任意可字符串化的值。
|
|
29
|
+
- **`exit(code)`**:立即结束程序,三条路径与 AOT 产物一致。
|
|
30
|
+
- **版本设施**:`codecin/version.py`(`version_info()` / `compare()` / `build_info()` 等,
|
|
31
|
+
`build_info()` 永不抛异常)、CLI 新增 `--build-info` 与 `--build-info --json`、
|
|
32
|
+
`script/bump_version.py`(改真源 → 重生成 Go 版本常量 → 插 CHANGELOG 骨架,含回滚与 `--dry-run`)。
|
|
33
|
+
|
|
34
|
+
### 修复 (Fixed)
|
|
35
|
+
|
|
36
|
+
- **struct 聚合初始化写穿指针槽**:`P p = {3, 4}` 会把第 2 个字面量写到 `FP + 0`,
|
|
37
|
+
**覆盖保存的帧指针**;全局 `P o = {3,4}` 覆盖对象指针。现按字段写入堆对象。
|
|
38
|
+
- **struct 数组成员访问写到绝对地址 0 / 8**:元素槽被当成指针解引用,
|
|
39
|
+
`ps[0].y = 1` 会**静默破坏数据段**(字符串字面量所在处)。现统一为
|
|
40
|
+
「槽里存对象指针」模型,每个元素各有对象。
|
|
41
|
+
- **初始化器过多时两条路径结论不同**:解释器越界写后返回残值、原生 VM 抛
|
|
42
|
+
`Address out of bounds`。现为编译期错误 `Too many initializers for struct P`。
|
|
43
|
+
- **struct 整体赋值是指针别名**:`P b = a; b.x = 7` 会改到 `a.x`,与文档承诺的
|
|
44
|
+
「值拷贝」不符。现按槽逐字拷贝(**传参仍保持文档规定的「引用可见」**)。
|
|
45
|
+
- **嵌套 struct 字段只按 1 槽推进**:`struct Out { In i; int c }` 的 `c` 压在 `i.b` 上;
|
|
46
|
+
且成员取值会把内嵌字段当指针解引用。两处均已修正。
|
|
47
|
+
- **`parse_array_literal` 静默丢弃尾部平铺元素**:`{{5, 6}, 7}` 里的 `7` 消失。
|
|
48
|
+
- **字符串字节语义分歧**:解释器侧 `+` / `substr` / `indexof` 先做 UTF-8 解码,
|
|
49
|
+
非法字节序列被替换成 U+FFFD,实测 `strlen(substr("中",2,1)+substr("中",1,1))`
|
|
50
|
+
解释器 9 / 原生 3。现全部改为字节级。
|
|
51
|
+
- **用户函数与内建同名时永远不会被调用**:`--no-native` 报宿主能力错误、原生路径
|
|
52
|
+
静默走宿主实现(两条路径结论不同)。现用户函数优先于同名内建。
|
|
53
|
+
- **局部 struct 字段默认值**:`MALLOC` 并不清零,此前「全零」只是堆恰好干净;
|
|
54
|
+
现于函数序言显式清零。
|
|
55
|
+
- **AOT 临时目录**:清理失败不再静默(记 warning 并给出残留绝对路径与手动删除命令);
|
|
56
|
+
每次构建前清扫超过 6 小时的 `.aotbuild-*` / `.aotprobe-*`;
|
|
57
|
+
新增 `[tool.setuptools.exclude-package-data]` 使残留不再进 wheel。
|
|
58
|
+
|
|
59
|
+
### 变更 (Changed)
|
|
60
|
+
|
|
61
|
+
- **无法自洽的 struct 写法改为编译期报错**(原先静默算错):多维 struct 数组
|
|
62
|
+
`P ps[2][3]`、struct 数组字段 `struct Bag { P items[3] }`、跨 struct 类型赋值
|
|
63
|
+
`P p; Q q; p = q;`、把 struct 写进标量槽 `int a[2]; a[0] = p;`。
|
|
64
|
+
- `tests/test_keyboard.py`、`tests/test_p0_fixes.py` 由 `tmp_path` 改用 `workdir` 夹具,
|
|
65
|
+
受限沙箱下不再出现 5 个 setup 阶段 `ERROR`。
|
|
66
|
+
- 新增跨库集成护栏 `tests/test_all_libs_together.py`:31 个纯 CIN 库同时 import 并各调用一个接口。
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## [5.6.0] - 2026-09-27
|
|
71
|
+
|
|
72
|
+
一次以「静默错误清零 + 安全加固」为主的修复发布,落地 `docs/SUGGESTIONS_NEXT.md`
|
|
73
|
+
第一轮评审的全部 P0/P1 项。所有语义修复均同步 Python 解释器与 Go 原生 VM 两条路径,
|
|
74
|
+
并新增 25 个回归测试(`tests/test_p0_fixes.py`);同时新增**键盘输入监听**宿主能力
|
|
75
|
+
(非阻塞轮询)与键盘场景的**顺序实时输出**、终端输入缓存兼容。
|
|
76
|
+
|
|
77
|
+
### 修复 (Fixed)
|
|
78
|
+
|
|
79
|
+
- **`enum` 末成员无尾逗号解析失败**:`expect(RBRACE)` 前未跳过换行导致
|
|
80
|
+
`enum Color { RED, GREEN = 5 }` 报 `Expected RBRACE but got NL`。
|
|
81
|
+
Python 编译器与 Go 编译器同步修复。
|
|
82
|
+
- **`input()` 真实现**:此前恒编译为 `MOV x0, 0`,任何输入都返回 0。现编译为
|
|
83
|
+
`IN` 指令:解释器从 `input_buffer` / 标准输入逐行读取(非法行返回 0),
|
|
84
|
+
Go 原生 VM 在缓冲耗尽后回退标准输入;CLI 启动时预读管道 stdin,
|
|
85
|
+
管道喂入(`echo 42 | ...`)与交互输入均可用。
|
|
86
|
+
- **`substr` / `indexof` 统一字节语义**:此前按字符索引,与 `strlen` / `s[i]`
|
|
87
|
+
的字节语义不一致,非 ASCII 字符串算错下标。现与 Go 侧一致按 UTF-8 字节切片/查找。
|
|
88
|
+
- **`sqrt` / `pow` 数学域错误对齐 NaN**:解释器此前对 `sqrt(-1.0)` 抛 CPython
|
|
89
|
+
`ValueError`,与 Go 路径(返回 NaN)不一致。现统一返回 NaN
|
|
90
|
+
(IEEE-754 允许同一 NaN 有多个位模式,不做精确位断言)。
|
|
91
|
+
- **CLI 编码兜底**:Windows GBK 控制台下输出非 ASCII 即崩,`main()` 入口对
|
|
92
|
+
stdout/stderr 强制 `reconfigure(encoding='utf-8', errors='replace')`。
|
|
93
|
+
- **Go 编译器无法构建**(既有):`genRangeFor` 的 `elemType` 参数误声明为
|
|
94
|
+
`*Node` 导致整个 compiler 包编译失败,改回 `*Type`;同步修正两个从未跑通的
|
|
95
|
+
Go 测试(range-for 标签前缀、enum 全局初始化器用例的期望值)。
|
|
96
|
+
|
|
97
|
+
### 安全 (Security)
|
|
98
|
+
|
|
99
|
+
- **`--sandbox` 真实现**:此前为空壳。现拦截全部宿主能力 SYS 调用
|
|
100
|
+
(`AUDIOPLAY=39` 起的文件/网络/桌面/进程类),并强制放弃原生 DLL 路径
|
|
101
|
+
回退解释器执行,保证拦截无旁路。
|
|
102
|
+
- **条件断点白名单求值**:`debugger` 的断点条件此前直接 `eval`,远程调试端口
|
|
103
|
+
可执行任意代码。现以 AST 白名单(字面量/寄存器名/算术比较/布尔逻辑)
|
|
104
|
+
校验后受限求值,拒绝 Call/Attribute/Subscript/推导式等一切可执行语法,
|
|
105
|
+
条件长度上限 512 字符。
|
|
106
|
+
|
|
107
|
+
### 加固 (Hardened)
|
|
108
|
+
|
|
109
|
+
- **JIT 栈保护补齐**:PUSH 生成代码补栈溢出检查(栈顶与堆区相撞即报错),
|
|
110
|
+
POP 补栈下溢检查,与解释器 / 原生 VM 对齐。
|
|
111
|
+
- **汇编器非法数字字面量报错**:`0x_`、`0b__` 等残缺字面量此前静默求值为 0,
|
|
112
|
+
现编译报错。
|
|
113
|
+
|
|
114
|
+
### 新增 (Added)
|
|
115
|
+
|
|
116
|
+
#### CIN 语法
|
|
117
|
+
|
|
118
|
+
- **`enum` 枚举**:`enum Color { RED, GREEN = 5, BLUE }`。成员是**编译期整数常量**,
|
|
119
|
+
未显式赋值时自动递增(首个成员从 `0` 起);`= <整数常量表达式>` 支持
|
|
120
|
+
`+ - * / % << >> & | ^` 与一元 `-` / `~`,可引用**先前已定义**的成员。
|
|
121
|
+
枚举类型名等价于 `int`(`Color c = BLUE`),成员可用于表达式、全局初始化与 `case` 标签;
|
|
122
|
+
成员只读,赋值报 `Cannot assign to enum member: X (constants are read-only)`。
|
|
123
|
+
- **范围 for**:`for (int v : arr)` 遍历定长数组元素(元素可为标量 / `string` / struct),
|
|
124
|
+
支持 `break` / `continue`。被遍历对象必须是定长数组:`int[]` 指针形式报
|
|
125
|
+
`range-for requires a fixed-size array`,多维数组报
|
|
126
|
+
`range-for over multi-dimensional arrays is not supported`(改用下标循环)。
|
|
127
|
+
- **`for` 初始化子句支持赋值**:`for (i = 0; i < n; i = i + 1)` 现在与类型声明一样合法。
|
|
128
|
+
- **`switch` 增强**:`case 1, 2, 7..9:` —— 逗号分隔的单值与**闭区间**范围可混用(含负数),
|
|
129
|
+
仍是 C 的贯穿语义;范围要求 `lo <= hi`,否则报 `Empty case range: lo..hi`。
|
|
130
|
+
- **转义扩展**:`\xH` / `\xHH` 写入**原始字节**(`"\xE4\xB8\xAD"` 即 `"中"` 的 UTF-8 三字节)、
|
|
131
|
+
`\uHHHH` / `\UHHHHHHHH` 按 **Unicode 码点**解析并以 UTF-8 编码写入;新增 `\a \b \f \v`
|
|
132
|
+
(字符字面量同样支持这四个)。`\u` / `\U` 位数不足或码点越界都有明确报错;未知转义保持原样。
|
|
133
|
+
- **多参数 `print` / `println`**:`println("a=", a, " b=", b)` 依次输出、**不加分隔符**;
|
|
134
|
+
`println()` 输出空行;单参数行为不变。
|
|
135
|
+
|
|
136
|
+
#### 宿主 API(36 个,全部 Go 原生实现)
|
|
137
|
+
|
|
138
|
+
- **路径与文件系统**:`path_join` `path_basename` `path_dirname` `path_abs`(string,路径分隔符随平台)、
|
|
139
|
+
`file_copy` `file_move` `dir_remove`(递归删除)`chdir`(int,`0`/`-1`)、
|
|
140
|
+
`is_dir`(`1`/`0`)、`file_mtime`(Unix 秒 / `-1`)、`temp_dir`(string)。
|
|
141
|
+
- **时间与系统**:`time_ms`、`sleep_ms`(单次上限 10 分钟 / 600000 ms)、`cpu_count`、
|
|
142
|
+
`arch_name`、`mem_info`(JSON `{"total_kb":N,"free_kb":M}`,未知平台为 0)、`is_android`。
|
|
143
|
+
- **网络**:`http_get` / `http_post`(返回响应体,失败空串;15 秒超时、8 MiB 上限)、
|
|
144
|
+
`download`(`0`/`-1`;非 2xx 算失败,落盘上限 256 MiB)。
|
|
145
|
+
- **编码与哈希**:`sha256`(十六进制小写)、`base64_encode`、`base64_decode`(非法输入空串)。
|
|
146
|
+
- **桌面集成**(Termux 优先,再按平台分发,命令缺失即优雅失败):`clipboard_get` / `clipboard_set`、
|
|
147
|
+
`notify`、`open_url`。Windows 用 PowerShell `Get-Clipboard` / `cmd /c clip`、`Wscript.Shell.Popup`
|
|
148
|
+
(10 秒自动消失)、`cmd /c start`;Linux 用 `wl-paste` / `xclip` / `xsel`、`notify-send`、`xdg-open`;
|
|
149
|
+
macOS 用 `pbpaste` / `pbcopy`、`osascript`、`open`;Android/Termux 用 `termux-clipboard-*`、
|
|
150
|
+
`termux-notification`、`termux-open-url`。
|
|
151
|
+
- **Android / Termux 扩展**(非 Android 环境一律优雅失败):`android_intent`(`am start`,Termux 下回退
|
|
152
|
+
`termux-am`)、`termux_call`、`termux_share`、`termux_torch`、`termux_volume`、`termux_brightness`、
|
|
153
|
+
`termux_camera_photo`、`termux_fingerprint`、`termux_sensor`。
|
|
154
|
+
- **`os_name()` 说明更新**:除 `"windows"` / `"darwin"` / `"linux"` 外,Android 原生构建返回 `"android"`。
|
|
155
|
+
|
|
156
|
+
#### 键盘输入监听(SYS 116..118,非阻塞轮询)
|
|
157
|
+
|
|
158
|
+
- **`key_hit()` / `get_key()` / `key_flush()`**:面向游戏循环 / TUI 的非阻塞键盘轮询,
|
|
159
|
+
核心实现全部由 Go 原生引擎承担(纯标准库,零第三方依赖)。
|
|
160
|
+
- `key_hit()` -> `1` 有待读按键 / `0` 无;
|
|
161
|
+
- `get_key()` -> 取出一个键码,无按键返回 `-1`;`0..255` 为原始字节
|
|
162
|
+
(Ctrl+字母 = 字母 & 0x1F,监听期间 Ctrl+C 不再终止程序),
|
|
163
|
+
方向键 / Home / End / PgUp / PgDn / Ins / Del 映射为 `1001..1010`,
|
|
164
|
+
F1..F10 映射为 `1021..1030`(Unix 转义序列与 Windows 扫描码在引擎内统一解码);
|
|
165
|
+
- `key_flush()` -> 清空键盘输入缓冲。
|
|
166
|
+
- **平台实现**:Windows 用 msvcrt `_kbhit`/`_getch`;Linux / macOS / Termux 用
|
|
167
|
+
termios 原始输入(只关行缓冲 / 回显 / Ctrl+C 信号,保留输出处理,
|
|
168
|
+
`println` 不受影响);程序退出自动恢复终端设置(幂等,覆盖所有执行出口)。
|
|
169
|
+
- **终端缓存兼容**:激活监听时清空控制台输入残留(Windows `FlushConsoleInputBuffer` /
|
|
170
|
+
Unix 非阻塞排空);Windows 启用前先以 `GetConsoleMode` 校验 stdin 为控制台,
|
|
171
|
+
管道 / 重定向的判定与非 Windows 一致。
|
|
172
|
+
- **顺序输出(禁止一股脑输出)**:真实终端下激活监听时,原生引擎先把已缓冲输出
|
|
173
|
+
落到终端,之后逐条直写 stdout 实时可见 —— "提示 → 等按键 → 反馈"顺序正确;
|
|
174
|
+
非终端环境(管道 / 重定向 / 测试捕获)保持"缓冲 + 结束回传"不变。
|
|
175
|
+
- **非终端环境优雅失败**:`key_hit` 恒 `0`,`get_key` 恒 `-1`,不阻塞、不报错。
|
|
176
|
+
- **`codecin/lib/key.cin` 标准库**:`enum Key` 键码常量(`K_UP`..`K_F10`、
|
|
177
|
+
`K_ESC` `K_ENTER` `K_TAB` `K_BACKSPACE`)+ `k_ctrl`(Ctrl 组合键码)+
|
|
178
|
+
`k_is_special`(扩展键码判定)+ `key_wait`(10ms 轮询阻塞等一键)。
|
|
179
|
+
- 三个内建同时注册进 Python 编译器 `HOST_BUILTINS` 表;纯解释路径
|
|
180
|
+
(`--no-native`)调用报 `require the native Go runtime`(与全部宿主 API 一致),
|
|
181
|
+
`--sandbox` 同样拦截;`cpu.py` 宿主能力判定由编号区间改为
|
|
182
|
+
`>= AUDIOPLAY` 下界,未来新增宿主 SYS 不再漏判。
|
|
183
|
+
|
|
184
|
+
### 文档 (Docs)
|
|
185
|
+
|
|
186
|
+
- `docs/CIN_GUIDE.md`:新增「enum 枚举」「范围 for」「转义序列」小节,`switch` 增加多值/范围 case,
|
|
187
|
+
`for` 说明 init 赋值,`print`/`println` 改为多参数签名;宿主能力新增 6 个分组的 API 表
|
|
188
|
+
(路径与文件系统 / 时间与系统 / 网络 / 编码与哈希 / 桌面集成,含各平台机制对照表 / Android-Termux 扩展)、
|
|
189
|
+
限制与常见错误同步。
|
|
190
|
+
- `docs/language/`:`types.md`(新增 enum 类型)、`control-flow.md`(范围 for、case 范围)、
|
|
191
|
+
`lexical.md`(转义表、关键字、续行运算符 `..`)、`strings.md`、`builtins.md`、`host-abilities.md`
|
|
192
|
+
(网络 / 编码 / 桌面 / Android 分组与能力矩阵)、`errors.md`、`arrays.md`、`variables.md`、`index.md` 同步。
|
|
193
|
+
- `docs/beginner/`:`cheatsheet.md`、`ch11-io-host.md`(新增网络、编码与哈希、桌面集成、路径与文件管理、
|
|
194
|
+
时间与系统、Android/Termux 扩展小节)、`ch04-conditions.md`、`ch05-loops.md`、`index.md` 同步。
|
|
195
|
+
- `misc/vim/syntax/cin.vim`:`enum` 加入类型高亮,36 个新宿主内建加入 `cinBuiltin`。
|
|
196
|
+
- `docs/CIN_GUIDE.md`:新增「宿主能力: 键盘输入监听(非阻塞轮询)」分节
|
|
197
|
+
(键码约定表 + 游戏循环示例 + 平台实现与输出行为说明),官方标准库清单收录 `key.cin`。
|
|
198
|
+
- 修正 `input()` 的过时描述(`docs/language/builtins.md`、`beginner/ch02-variables.md`、
|
|
199
|
+
`ch11-io-host.md`、`ch12-debug.md`、`cheatsheet.md`、`projects.md`):
|
|
200
|
+
自本版起 `input()` 已真实现(读一行标准输入,非法行 / EOF 返回 `0`),
|
|
201
|
+
相关章节与避坑清单同步为实际行为;`cheatsheet.md` 的 `sqrt(-1)` 条目
|
|
202
|
+
同步为"两条路径一致返回 NaN"。
|
|
203
|
+
- 本版宿主 API 均为 **Go 原生引擎实现**:纯解释路径(`--no-native`)调用会报
|
|
204
|
+
`host builtins (GUI/audio/system/Termux) require the native Go runtime`;
|
|
205
|
+
它们具备真实文件与网络权限,请只运行可信脚本。
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
8
209
|
## [5.5.3] - 2026-09-26
|
|
9
210
|
|
|
10
211
|
以「消除静默算错地址」为主线的一次修复发布:两个会**读错/写错内存且不报错**的
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecin
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.7.4
|
|
4
4
|
Summary: Code CIN - 高级语言与运行时 (CIN/PL/ASM 工具链 + Go 原生 VM + ARM64/RISC-V/FP/Vector 指令集)
|
|
5
5
|
Author: ByUsi Studio, 北啊呢
|
|
6
6
|
License-Expression: MIT
|
|
@@ -417,7 +417,8 @@ flowchart TD
|
|
|
417
417
|
**安装(pip)**
|
|
418
418
|
|
|
419
419
|
```bash
|
|
420
|
-
pip install codecin # 内置标准库
|
|
420
|
+
pip install codecin # 安装 (内置标准库 codecin/lib/*.cin 随包分发)
|
|
421
|
+
pip install -U codecin # 升级到最新版 (不要锁定版本号)
|
|
421
422
|
codecin --version
|
|
422
423
|
```
|
|
423
424
|
|
|
@@ -9,18 +9,40 @@
|
|
|
9
9
|
`codecin/native/aot` 共用同一份文件 (``stub_main.go.txt``), 避免两处漂移。
|
|
10
10
|
临时包建在 `codecin-native` 模块内的 ``.aotbuild-*`` 目录: Go 工具链会忽略
|
|
11
11
|
以 '.' 开头的目录, 因此既不影响 `go build ./...`, 也不需要 replace 指令。
|
|
12
|
+
|
|
13
|
+
临时目录的生命周期 (加固, 见 docs/SUGGESTIONS_NEXT.md §3.3):
|
|
14
|
+
* 每次 :func:`build` 之前先调用 :func:`sweep_stale_build_dirs` 清理**陈旧**残留,
|
|
15
|
+
避免受限环境 (沙箱 / 只读 ACL) 下删不掉的目录无限累积;
|
|
16
|
+
* ``finally`` 里的 ``rmtree`` 失败**不再静默**: 有 logger 就 ``warning``,
|
|
17
|
+
没有 logger 时退回模块级 stdlib logger (``codecin.aot``), 绝不吞掉;
|
|
18
|
+
* 只清理 ``codecin/native/`` **本目录下**、名字带 ``.aotbuild-`` /
|
|
19
|
+
``.aotprobe-`` 前缀、且**不是符号链接**的目录, 绝不递归删除别处。
|
|
12
20
|
"""
|
|
13
21
|
|
|
22
|
+
import logging
|
|
14
23
|
import os
|
|
15
24
|
import platform
|
|
16
25
|
import secrets
|
|
17
26
|
import shutil
|
|
18
27
|
import subprocess
|
|
19
|
-
|
|
28
|
+
import time
|
|
29
|
+
from typing import List, NamedTuple, Optional
|
|
20
30
|
|
|
21
31
|
#: 仓库内 codecin-native 模块目录 (含 go.mod)。
|
|
22
32
|
_NATIVE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'native')
|
|
23
33
|
|
|
34
|
+
#: 模块级 logger: 调用方没有传 logger 时的兜底 (绝不能因为"没人接"就静默)。
|
|
35
|
+
#: 它是 ``codecin`` logger 的子 logger, 因此 CLI 构造的 Logger (RichHandler) 会
|
|
36
|
+
#: 照常渲染它; 完全没配置 logging 时 stdlib 的 lastResort 也会打到 stderr。
|
|
37
|
+
_LOGGER = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
#: AOT 临时目录名前缀 (``.aotprobe-`` 是历史遗留, 一并纳入守卫)。
|
|
40
|
+
TEMP_DIR_PREFIXES = ('.aotbuild-', '.aotprobe-')
|
|
41
|
+
|
|
42
|
+
#: 陈旧残留的默认容忍时长 (6 小时): 远大于任何一次 go build, 因此不会误删
|
|
43
|
+
#: 并发构建中的临时目录。
|
|
44
|
+
DEFAULT_STALE_AGE_SECONDS = 6 * 3600
|
|
45
|
+
|
|
24
46
|
#: 生成的 main.go 模板 (与 Go 侧共用)。
|
|
25
47
|
STUB_PATH = os.path.join(_NATIVE_DIR, 'aot', 'stub_main.go.txt')
|
|
26
48
|
|
|
@@ -130,6 +152,156 @@ def _run_go_build(cmd, mod, env, logger):
|
|
|
130
152
|
return proc, detail
|
|
131
153
|
|
|
132
154
|
|
|
155
|
+
class SweepResult(NamedTuple):
|
|
156
|
+
""":func:`sweep_stale_build_dirs` 的结果 (两个绝对路径列表)。"""
|
|
157
|
+
|
|
158
|
+
#: 成功删除的目录
|
|
159
|
+
removed: List[str]
|
|
160
|
+
#: 判定为残留但删除/读取失败的目录 (调用方应提示用户手动处理)
|
|
161
|
+
failed: List[str]
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _warn(logger, message: str) -> None:
|
|
165
|
+
"""记一条 warning; 没有 logger 时退回模块级 stdlib logger (绝不静默)。
|
|
166
|
+
|
|
167
|
+
日志本身出问题不能掩盖构建结果 (本函数常在 ``finally`` 里被调用),
|
|
168
|
+
因此这里再包一层兜底。
|
|
169
|
+
"""
|
|
170
|
+
try:
|
|
171
|
+
if logger is not None:
|
|
172
|
+
logger.warning(message)
|
|
173
|
+
else:
|
|
174
|
+
_LOGGER.warning(message)
|
|
175
|
+
except Exception: # noqa: BLE001 - 日志失败不得影响构建
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _debug(logger, message: str) -> None:
|
|
180
|
+
"""记一条 debug (同样带兜底)。"""
|
|
181
|
+
try:
|
|
182
|
+
if logger is not None:
|
|
183
|
+
logger.debug(message)
|
|
184
|
+
else:
|
|
185
|
+
_LOGGER.debug(message)
|
|
186
|
+
except Exception: # noqa: BLE001
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _manual_removal_hint(path: str) -> str:
|
|
191
|
+
"""给用户的「怎么手动删掉它」提示 (Windows + POSIX 各一条)。"""
|
|
192
|
+
return (f'请手动删除该目录 —— Windows PowerShell: '
|
|
193
|
+
f'Remove-Item -LiteralPath "{path}" -Recurse -Force ; '
|
|
194
|
+
f'Linux/macOS: rm -rf "{path}"')
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _is_temp_dir_name(name: str) -> bool:
|
|
198
|
+
"""名字是否是本模块自己产生的临时目录名 (只认前缀, 不做任何路径解析)。
|
|
199
|
+
|
|
200
|
+
显式拒绝 ``.``/``..``、含路径分隔符的名字以及绝对路径 —— 这些都不可能是
|
|
201
|
+
``secrets.token_hex`` 的产物, 一旦出现就说明有人往我们手里塞了别的东西。
|
|
202
|
+
"""
|
|
203
|
+
if not name or name in ('.', '..'):
|
|
204
|
+
return False
|
|
205
|
+
if name != os.path.basename(name): # 含 / 或 \ -> 不是直接子项
|
|
206
|
+
return False
|
|
207
|
+
if os.sep in name or (os.altsep and os.altsep in name):
|
|
208
|
+
return False
|
|
209
|
+
if os.path.isabs(name):
|
|
210
|
+
return False
|
|
211
|
+
return name.startswith(TEMP_DIR_PREFIXES)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def sweep_stale_build_dirs(max_age_seconds: float = DEFAULT_STALE_AGE_SECONDS,
|
|
215
|
+
logger=None) -> SweepResult:
|
|
216
|
+
"""删除 ``codecin/native/`` 下**陈旧**的 AOT 临时构建目录。
|
|
217
|
+
|
|
218
|
+
只处理 ``codecin/native/`` 的**直接子目录** (不递归), 名字必须带
|
|
219
|
+
``.aotbuild-`` / ``.aotprobe-`` 前缀, 且 mtime 早于 ``max_age_seconds``;
|
|
220
|
+
符号链接与普通文件一律跳过。任何失败都不会抛异常, 只记录日志并把路径
|
|
221
|
+
放进 :attr:`SweepResult.failed`。
|
|
222
|
+
|
|
223
|
+
:param max_age_seconds: 容忍时长 (默认 6 小时)。``0`` 表示删除所有匹配目录
|
|
224
|
+
—— 并发构建时不要这么做。
|
|
225
|
+
:param logger: 可选 logger; 缺省时用模块级 stdlib logger。
|
|
226
|
+
:return: 被删除的路径与清理失败的路径
|
|
227
|
+
"""
|
|
228
|
+
removed: List[str] = []
|
|
229
|
+
failed: List[str] = []
|
|
230
|
+
|
|
231
|
+
root = os.path.abspath(_NATIVE_DIR)
|
|
232
|
+
try:
|
|
233
|
+
names = os.listdir(root)
|
|
234
|
+
except OSError as e: # 目录不存在 / 不可读: 尽力而为
|
|
235
|
+
_warn(logger, f'AOT: 无法扫描临时目录所在位置 {root} ({e}); 跳过清理')
|
|
236
|
+
return SweepResult(removed, failed)
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
max_age = max(0.0, float(max_age_seconds))
|
|
240
|
+
except (TypeError, ValueError):
|
|
241
|
+
max_age = float(DEFAULT_STALE_AGE_SECONDS)
|
|
242
|
+
|
|
243
|
+
now = time.time()
|
|
244
|
+
for name in names:
|
|
245
|
+
if not _is_temp_dir_name(name):
|
|
246
|
+
continue
|
|
247
|
+
path = os.path.join(root, name)
|
|
248
|
+
# 双保险: 解析后必须仍然是 root 的直接子项 (防 '..' / 绝对路径混入)
|
|
249
|
+
if os.path.dirname(os.path.abspath(path)) != root:
|
|
250
|
+
_warn(logger, f'AOT: 跳过可疑的临时目录路径: {path}')
|
|
251
|
+
continue
|
|
252
|
+
if os.path.islink(path) or not os.path.isdir(path):
|
|
253
|
+
_debug(logger, f'AOT: 跳过非普通目录的临时项: {path}')
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
age = now - os.path.getmtime(path)
|
|
258
|
+
except OSError as e:
|
|
259
|
+
failed.append(path)
|
|
260
|
+
_warn(logger, f'AOT: 无法读取残留临时目录的时间戳: {path} ({e}); '
|
|
261
|
+
f'{_manual_removal_hint(path)}')
|
|
262
|
+
continue
|
|
263
|
+
|
|
264
|
+
if age < max_age:
|
|
265
|
+
_debug(logger, f'AOT: 临时目录仍然新鲜, 保留: {path} '
|
|
266
|
+
f'({age / 3600:.1f} 小时前)')
|
|
267
|
+
continue
|
|
268
|
+
|
|
269
|
+
try:
|
|
270
|
+
shutil.rmtree(path)
|
|
271
|
+
except OSError as e:
|
|
272
|
+
failed.append(path)
|
|
273
|
+
_warn(logger, f'AOT: 残留临时目录清理失败: {path} '
|
|
274
|
+
f'({age / 3600:.1f} 小时前, {e}); '
|
|
275
|
+
f'{_manual_removal_hint(path)}')
|
|
276
|
+
continue
|
|
277
|
+
|
|
278
|
+
removed.append(path)
|
|
279
|
+
_debug(logger, f'AOT: 已清理陈旧临时目录: {path} '
|
|
280
|
+
f'({age / 3600:.1f} 小时前)')
|
|
281
|
+
|
|
282
|
+
if removed:
|
|
283
|
+
_debug(logger, f'AOT: 陈旧临时目录清理完成, 共 {len(removed)} 个')
|
|
284
|
+
return SweepResult(removed, failed)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _remove_temp_dir(tmp: str, logger) -> None:
|
|
288
|
+
"""删除本次构建的临时目录; 失败时告警而不是静默吞掉 (§3.3)。
|
|
289
|
+
|
|
290
|
+
``shutil.rmtree(ignore_errors=True)`` 会让「删不掉」完全静默, 于是残留
|
|
291
|
+
在受限环境 (沙箱 / 异常 ACL) 里不断累积, 最终让任何遍历仓库的工具报
|
|
292
|
+
Access is denied。这里改为: 失败 -> warning (含绝对路径与手动删除提示)。
|
|
293
|
+
"""
|
|
294
|
+
try:
|
|
295
|
+
shutil.rmtree(tmp)
|
|
296
|
+
except FileNotFoundError:
|
|
297
|
+
pass # 目录已不存在: 目的已达成
|
|
298
|
+
except Exception as e: # noqa: BLE001 - rmtree 正常只抛 OSError,
|
|
299
|
+
# 但这里处于 finally, 任何异常都不能掩盖构建结果, 一律降级为 warning。
|
|
300
|
+
_warn(logger, f'AOT: 临时构建目录清理失败, 残留目录: '
|
|
301
|
+
f'{os.path.abspath(tmp)} (原因: {e}); '
|
|
302
|
+
f'{_manual_removal_hint(tmp)}')
|
|
303
|
+
|
|
304
|
+
|
|
133
305
|
def build(bytecode: bytes,
|
|
134
306
|
mem_image: bytes,
|
|
135
307
|
out: str,
|
|
@@ -150,6 +322,14 @@ def build(bytecode: bytes,
|
|
|
150
322
|
goos, goarch = parse_target(target) if target else parse_target(host_target())
|
|
151
323
|
mod = module_dir()
|
|
152
324
|
|
|
325
|
+
# 先清掉上一次留下的陈旧残留 (默认 > 6 小时), 再建新目录: 受限环境下
|
|
326
|
+
# rmtree 可能失败, 没有这一步残留就会无限累积。
|
|
327
|
+
# 清理是尽力而为 —— 任何异常都必须吞掉, 绝不能因此让构建失败。
|
|
328
|
+
try:
|
|
329
|
+
sweep_stale_build_dirs(logger=logger)
|
|
330
|
+
except Exception as e: # noqa: BLE001
|
|
331
|
+
_debug(logger, f'AOT: 陈旧临时目录清理跳过 ({e})')
|
|
332
|
+
|
|
153
333
|
# 注意: 这里用 os.makedirs 而不是 tempfile.mkdtemp —— mkdtemp 会创建 0700
|
|
154
334
|
# 目录, 在受限环境 (沙箱 / 部分 CI 安全策略) 下随后向其中写文件会被拒绝。
|
|
155
335
|
tmp = os.path.join(mod, '.aotbuild-' + secrets.token_hex(6))
|
|
@@ -199,7 +379,7 @@ def build(bytecode: bytes,
|
|
|
199
379
|
if logger:
|
|
200
380
|
logger.info(f'AOT 临时目录保留: {tmp}')
|
|
201
381
|
else:
|
|
202
|
-
|
|
382
|
+
_remove_temp_dir(tmp, logger)
|
|
203
383
|
|
|
204
384
|
|
|
205
385
|
def program_dependencies(program_file: str) -> List[str]:
|
|
@@ -36,12 +36,12 @@ def _eval_expr(text: str, symbols: Dict[str, int]) -> Optional[int]:
|
|
|
36
36
|
raw = match.group(0).replace('_', '')
|
|
37
37
|
if len(raw) > 1 and raw[:2].lower() in ('0x', '0b', '0o'):
|
|
38
38
|
raw = '0' + raw[1].lower() + raw[2:]
|
|
39
|
-
|
|
40
|
-
return str(int(raw, 0))
|
|
41
|
-
except ValueError:
|
|
42
|
-
return '0'
|
|
39
|
+
return str(int(raw, 0)) # 非法字面量 (如 0x_) 抛 ValueError -> 整体求值失败
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
try:
|
|
42
|
+
text = _RE_EXPR_NUM.sub(repl_num, text)
|
|
43
|
+
except ValueError:
|
|
44
|
+
return None
|
|
45
45
|
|
|
46
46
|
def repl_name(match):
|
|
47
47
|
name = match.group(0)
|