codecin 5.8.0__tar.gz → 5.8.2__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.8.0 → codecin-5.8.2}/CHANGELOG.md +64 -0
- {codecin-5.8.0/codecin.egg-info → codecin-5.8.2}/PKG-INFO +1 -1
- {codecin-5.8.0 → codecin-5.8.2}/codecin/__init__.py +1 -1
- {codecin-5.8.0 → codecin-5.8.2}/codecin/cin.py +31 -4
- {codecin-5.8.0 → codecin-5.8.2}/codecin/cli.py +57 -3
- {codecin-5.8.0 → codecin-5.8.2}/codecin/cpu.py +50 -6
- {codecin-5.8.0 → codecin-5.8.2}/codecin/isa.py +4 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/gostd.cin +12 -3
- {codecin-5.8.0 → codecin-5.8.2}/codecin/memory.py +6 -1
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/compiler/codegen.go +31 -2
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/compiler/syscalls.go +3 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/isa_gen.go +3 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/version_gen.go +1 -1
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/vm.go +65 -14
- {codecin-5.8.0 → codecin-5.8.2/codecin.egg-info}/PKG-INFO +1 -1
- {codecin-5.8.0 → codecin-5.8.2}/codecin.egg-info/SOURCES.txt +1 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/args_demo.cin +4 -4
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_cin_host.py +12 -2
- codecin-5.8.2/tests/test_issue_regress.py +263 -0
- {codecin-5.8.0 → codecin-5.8.2}/LICENSE +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/MANIFEST.in +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/README.md +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/aot.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/assembler.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/cache.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/config.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/console.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/crom.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/debugger.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/disasm.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/environment.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/errors.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/jit.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/array.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/bigint.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/bits.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/bitset.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/codec.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/combin.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/conv.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/cppstd.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/cstd.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/csv.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/dp.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/fmt.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/frac.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/graph.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/gui.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/hash.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/heap.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/io.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/json.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/key.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/math.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/matrix.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/path.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/queue.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/rand.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/set.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/sort.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/stat.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/str.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/termux.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/test.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/text.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/time.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/token.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/tree.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/unionfind.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/validate.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/lib/vec.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/logger.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/aot/aot.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/aot/stub_main.go.txt +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/build.ps1 +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/build.sh +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/compiler/compile_test.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/compiler/parser.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/compiler/tokenizer.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/compiler/types.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/args.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/audio.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/audio_other.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/audio_test.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/audio_windows.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/canvas.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/console_other.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/console_windows.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/crom.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/encode.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/engine_test.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/gui.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/gui_stub.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/gui_test.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/gui_windows.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/gui_x11.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/host_test.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/hostcrypto.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/hostdesktop.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/hostnet.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/keyboard.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/keyboard_other.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/keyboard_stub.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/keyboard_termios_darwin.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/keyboard_termios_linux.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/keyboard_test.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/keyboard_windows.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/keyboard_windows_test.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/mem_other.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/mem_windows.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/stdin.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/stdin_other.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/stdin_windows.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/system.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/engine/termux.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/go.mod +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/ir/ir.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native/main.go +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/native.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/registers.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/stats.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/stdlib.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin/version.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin.egg-info/dependency_links.txt +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin.egg-info/entry_points.txt +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin.egg-info/requires.txt +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/codecin.egg-info/top_level.txt +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/asm_constants.asm +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/bitwise_builtins.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/control_flow.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/enum_range_for.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/gui_demo.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/literals_types.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/local_audio.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/modules_demo.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/stdlib_demo.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/examples/system_interaction.cin +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/misc/vim/ftdetect/codecin.vim +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/misc/vim/syntax/cin.vim +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/misc/vim/syntax/codecinasm.vim +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/misc/vscode-cin/.vscodeignore +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/misc/vscode-cin/README.md +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/misc/vscode-cin/build.bat +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/misc/vscode-cin/language-configuration.json +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/misc/vscode-cin/package.json +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/misc/vscode-cin/snippets/cin.json +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/misc/vscode-cin/syntaxes/cin.tmLanguage.json +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/pyproject.toml +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/script/bump_version.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/script/check_paths.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/script/gen_isa_docs.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/script/gen_native_isa.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/setup.cfg +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/setup.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_aggregate_and_const.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_all_libs_together.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_aot.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_assembler_ext.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_cache.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_cin_host_ext.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_cin_new_features.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_cin_syntax.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_cin_syntax_ext.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_cin_system.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_cli.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_compiler_errors.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_cpu_interpreted.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_debugger.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_environment.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_examples.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_features_ab.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_features_crom_mmu.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_features_import.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_features_importloc.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_features_mmu.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_features_remote.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_gui.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_index_subscript_type.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_isa_dispatch.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_isa_single_source.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_keyboard.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_bigint.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_bitset.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_codec.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_combin.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_compat.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_csv.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_dp.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_fmt.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_frac.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_graph.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_heap.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_path.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_set.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_text.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_token.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_tree.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_lib_unionfind.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_libs.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_libs_ext.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_literals_and_bom.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_memory_protection.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_native_hardening.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_native_lib_lookup.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_nested_subscript_codegen.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_no_go_cli.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_p0_fixes.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_packaging.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_paths_consistency.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_struct_value_semantics.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_switch_semantics.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_three_paths.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_version.py +0 -0
- {codecin-5.8.0 → codecin-5.8.2}/tests/test_workflows.py +0 -0
|
@@ -5,6 +5,70 @@
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## [5.8.2] - 2026-10-04
|
|
9
|
+
|
|
10
|
+
一次以「真实压测程序暴露的错误信息与栈安全」为主线的修复发布。核心结论:
|
|
11
|
+
5.5.3 的浮点下标修复在三条执行路径均有效(`a[(n * p) / 100]` 一类变量参与
|
|
12
|
+
的浮点表达式同样覆盖);用户在 Termux 上报告的 `address ffffffffffffc6f8
|
|
13
|
+
out of bounds` 实为**局部大数组把栈推成负地址**——`int lat[10000]` 需要
|
|
14
|
+
80 KB 局部空间,超过默认 64 KB 内存后 SP 绕回,越界检查检查的是绕回后的
|
|
15
|
+
地址。为此新增带防护的帧分配系统调用(SYS 137),并全面改进运行时/编译期
|
|
16
|
+
错误信息、补齐 CLI 直传参数与高精度计时。
|
|
17
|
+
|
|
18
|
+
### 修复 (Fixed)
|
|
19
|
+
|
|
20
|
+
#### 局部大数组撑爆栈(静默生成负地址)
|
|
21
|
+
- **函数序言的帧分配改为带防护的系统调用**。此前 prologue 用裸 `ADDI sp, sp,
|
|
22
|
+
-frame_bytes` 递减栈指针,局部数组总大小超过可用内存时 SP 绕回为巨大无符号
|
|
23
|
+
值(如 0xFFFFFFFFFFFFC6F8),后续所有访问都指向"负地址",报错只看到一句
|
|
24
|
+
out of bounds,看不出真因。现在两侧编译器(`cin.py` / `codegen.go`)改为
|
|
25
|
+
`SYS ALLOCFRAME`:VM 侧用**有符号比较**在分配前检查栈余量(含 4 KB 警戒
|
|
26
|
+
线),不足时直接报
|
|
27
|
+
`Stack overflow: frame needs N bytes, stack headroom only M bytes (...)
|
|
28
|
+
Try --mem-size (default 65536) or smaller local arrays`,
|
|
29
|
+
不再让绕回地址流入访存路径。Python 解释器、JIT、Go 原生三条路径语义一致。
|
|
30
|
+
- **负地址越界提示兜底**:地址 >= 2^63 的越界报错(Python `memory.py` 与
|
|
31
|
+
Go `checkAddr`)追加
|
|
32
|
+
`(negative address: stack overflow or bad pointer?)`,把"看起来像随机大数"
|
|
33
|
+
的地址翻译成可排查的方向。
|
|
34
|
+
|
|
35
|
+
#### 错误信息补全(问题 2/4/5)
|
|
36
|
+
- **Heap exhausted 附用量与建议**:三处堆耗尽点(malloc / 字符串拼接 / 字符串
|
|
37
|
+
操作)在 Python 与 Go 两侧逐字一致地报
|
|
38
|
+
`Heap exhausted: need N bytes, free M bytes (heap 0x...0x...).
|
|
39
|
+
Try --mem-size (default 65536) or reduce allocations`。
|
|
40
|
+
- **非常量数组长度的编译期报错附 hint**:
|
|
41
|
+
`array lengths must be compile-time constants; declare it as
|
|
42
|
+
\`const int NAME = ...\`; variable-length arrays are not supported —
|
|
43
|
+
use a fixed-size array plus a count variable`。
|
|
44
|
+
- **`float(x)` / `int(x)` 报错附替代写法**:提示使用 `to_float(x)` / `to_int(x)`
|
|
45
|
+
或依赖混合表达式的隐式提升。
|
|
46
|
+
|
|
47
|
+
#### 兼容层与文档
|
|
48
|
+
- `gostd.cin` 头注释明确列出当前覆盖的包(strings / strconv / math / slices /
|
|
49
|
+
os.Args)与**未覆盖项**(time、os 文件读写、net/http、sync),并指向对应
|
|
50
|
+
内建(`time_ms/time_us/time_ns`、`file_*` 函数族),避免"Go 标准库兼容层"
|
|
51
|
+
的命名让人误以为常用包会逐步全覆盖。
|
|
52
|
+
|
|
53
|
+
### 新增 (Added)
|
|
54
|
+
|
|
55
|
+
#### CLI 程序名后直传参数
|
|
56
|
+
- `codecin 压测.cin https://example.com 24` 现在可用:程序文件之后的裸参数
|
|
57
|
+
原样传给 CIN 程序(`arg_count()` / `arg(i)`),已知 CLI 选项放在程序之前或
|
|
58
|
+
之后均可识别;形如选项的直传参数给出 stderr 提示并用 `--` 显式分隔。
|
|
59
|
+
`--` 分隔符行为不变且优先级最高,`--help` 新增 Program arguments 说明段。
|
|
60
|
+
|
|
61
|
+
#### 高精度计时 `time_us()` / `time_ns()`(SYS 138/139)
|
|
62
|
+
- 微秒 / 纳秒级单调计时,补齐 `time_ms()` 只能测毫秒的短板(压测与微基准
|
|
63
|
+
基础设施)。两侧编译器同步登记,解释器 / JIT / 原生三条路径均可用;
|
|
64
|
+
与 ALLOCFRAME 同属核心 VM 机制,不触发沙箱门禁与原生路径限定。
|
|
65
|
+
|
|
66
|
+
#### 显式类型转换 `to_int()` / `to_float()`
|
|
67
|
+
- 数值类型(int/float/bool)间的显式转换,语义与隐式转换一致(float→int 向零
|
|
68
|
+
截断)。非数值参数在编译期报错并说明。
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
8
72
|
## [5.8.0] - 2026-10-04
|
|
9
73
|
|
|
10
74
|
一次「图形、媒体与跨平台兼容」发布:新增 **GUI 窗口**(Windows Win32 /
|
|
@@ -210,6 +210,8 @@ HOST_BUILTINS = {
|
|
|
210
210
|
'chdir': (Syscall.CHDIR, 1, 'int'),
|
|
211
211
|
# 时间与系统信息
|
|
212
212
|
'time_ms': (Syscall.TIMEMS, 0, 'int'),
|
|
213
|
+
'time_us': (Syscall.TIMEUS, 0, 'int'),
|
|
214
|
+
'time_ns': (Syscall.TIMENS, 0, 'int'),
|
|
213
215
|
'sleep_ms': (Syscall.SLEEPMS, 1, 'int'),
|
|
214
216
|
'cpu_count': (Syscall.CPUCOUNT, 0, 'int'),
|
|
215
217
|
'arch_name': (Syscall.ARCHNAME, 0, 'string'),
|
|
@@ -953,7 +955,11 @@ class Parser:
|
|
|
953
955
|
return ('int', self.enums[node[1]])
|
|
954
956
|
raise CompilerError(
|
|
955
957
|
f"Constant expression uses non-constant name '{node[1]}' "
|
|
956
|
-
f"at {self._loc()}"
|
|
958
|
+
f"at {self._loc()} "
|
|
959
|
+
"(hint: array lengths must be compile-time constants; "
|
|
960
|
+
f"declare it as `const int {node[1]} = ...`; "
|
|
961
|
+
"variable-length arrays are not supported — use a fixed-size "
|
|
962
|
+
"array plus a count variable)")
|
|
957
963
|
if kind == 'neg':
|
|
958
964
|
ctype, value = self._const_value_ast(node[1])
|
|
959
965
|
if ctype == 'string':
|
|
@@ -1996,12 +2002,15 @@ class CodeGen:
|
|
|
1996
2002
|
self.locals[pname] = (ptype, poff, False)
|
|
1997
2003
|
|
|
1998
2004
|
self.label(f.name)
|
|
1999
|
-
# prologue: 保存调用方 FP, 建立帧指针,
|
|
2005
|
+
# prologue: 保存调用方 FP, 建立帧指针, 分配局部空间。
|
|
2006
|
+
# 帧分配走 SYS ALLOCFRAME (带栈溢出防护): 此前直接 ADDI SP 减帧长,
|
|
2007
|
+
# 局部大数组会把 SP 推到负地址 —— 轻则报晦涩的越界地址
|
|
2008
|
+
# (如 address ffff...c6f8), 重则静默覆盖全局/堆内存。
|
|
2000
2009
|
self.emit('PUSH', self.reg(29))
|
|
2001
2010
|
self.emit('MOV', self.reg(29), self.reg(32)) # FP = SP
|
|
2002
2011
|
if self.frame_bytes:
|
|
2003
|
-
self.emit('
|
|
2004
|
-
self.emit('
|
|
2012
|
+
self.emit('MOV', self.reg(0), self.imm(self.frame_bytes))
|
|
2013
|
+
self.emit('SYS', self.imm(Syscall.ALLOCFRAME))
|
|
2005
2014
|
|
|
2006
2015
|
# struct 局部变量: 堆分配对象 (槽里存对象指针, 与全局 struct 同一模型)
|
|
2007
2016
|
for _name, (t, loff, _is_block) in list(self.locals.items()):
|
|
@@ -3543,6 +3552,18 @@ class CodeGen:
|
|
|
3543
3552
|
'rtrim': Syscall.RTRIM}[name]
|
|
3544
3553
|
self.emit('SYS', self.imm(sys_id))
|
|
3545
3554
|
return 'string'
|
|
3555
|
+
if name in ('to_int', 'to_float'):
|
|
3556
|
+
# 显式类型转换: to_int(x) 向零截断 (与 int x = 1.9 一致),
|
|
3557
|
+
# to_float(x) 整数提升为浮点。参数类型静态已知, 非数值报编译错。
|
|
3558
|
+
t = self.gen_value(args[0])
|
|
3559
|
+
target = 'int' if name == 'to_int' else 'float'
|
|
3560
|
+
if t not in ('int', 'float', 'bool'):
|
|
3561
|
+
raise CompilerError(
|
|
3562
|
+
f"{name}() expects a numeric argument, got {t} "
|
|
3563
|
+
f"at {self._loc()}"
|
|
3564
|
+
+ (f" (hint: use atoi() to parse a string)" if t == 'string' else ""))
|
|
3565
|
+
self._convert(t, target)
|
|
3566
|
+
return target
|
|
3546
3567
|
if name == 'atoi':
|
|
3547
3568
|
self.gen_value(args[0])
|
|
3548
3569
|
self.emit('SYS', self.imm(Syscall.ATOI))
|
|
@@ -3562,6 +3583,12 @@ class CodeGen:
|
|
|
3562
3583
|
return 'int'
|
|
3563
3584
|
|
|
3564
3585
|
# 用户函数 (已在 _gen_call 开头优先分派; 走到这里说明名字未知)
|
|
3586
|
+
if name in ('int', 'float'):
|
|
3587
|
+
# 常见迁移习惯: C/Python 风格的显式转换函数。给出可操作的替代写法。
|
|
3588
|
+
raise CompilerError(
|
|
3589
|
+
f"Unknown function: {name} (hint: use "
|
|
3590
|
+
f"to_{name}(x) for explicit conversion, or rely on implicit "
|
|
3591
|
+
"promotion in mixed int/float expressions)")
|
|
3565
3592
|
raise CompilerError(f"Unknown function: {name}")
|
|
3566
3593
|
|
|
3567
3594
|
@staticmethod
|
|
@@ -17,7 +17,11 @@ from .console import Colors, Console, Panel
|
|
|
17
17
|
HELP_INTRO = f"""{Colors.colorize(f'Code CIN v{__version__}', Colors.CYAN, True)}
|
|
18
18
|
|
|
19
19
|
{Colors.colorize('Usage:', Colors.YELLOW)}
|
|
20
|
-
codecin <program.[cin|pl|asm|bin]> [
|
|
20
|
+
codecin [options] <program.[cin|pl|asm|bin]> [args...]
|
|
21
|
+
|
|
22
|
+
{Colors.colorize('Program arguments:', Colors.YELLOW)}
|
|
23
|
+
program 之后的裸参数原样传给 CIN 程序 (arg_count()/arg(i) 读取);
|
|
24
|
+
CLI 选项放在 program 之前或之后均可识别, 形如选项的参数请用 `--` 显式分隔。
|
|
21
25
|
|
|
22
26
|
{Colors.colorize('Supported formats:', Colors.YELLOW)}
|
|
23
27
|
.cin CIN 高级语言 (函数/struct/数组/浮点/字符串)
|
|
@@ -219,6 +223,43 @@ def _run_aot_build(ns: argparse.Namespace, console, program_file: str,
|
|
|
219
223
|
return 0
|
|
220
224
|
|
|
221
225
|
|
|
226
|
+
def _extract_program_args(args: List[str], parser: argparse.ArgumentParser):
|
|
227
|
+
"""把程序文件之后的裸参数透传给 CIN 程序 (arg_count()/arg(i))。
|
|
228
|
+
|
|
229
|
+
规则 (向后兼容): 第一个非选项 token 是程序文件; 此后遇到已知 CLI 选项
|
|
230
|
+
(及其取值) 仍归 CLI 解析, 其余裸 token 归程序参数。`--` 分隔的参数已在
|
|
231
|
+
main 里摘出, 优先级更高。返回 (cli_args, program_args)。
|
|
232
|
+
"""
|
|
233
|
+
takes_value = {}
|
|
234
|
+
for act in parser._actions: # noqa: SLF001 - argparse 无公开枚举接口
|
|
235
|
+
for s in getattr(act, 'option_strings', []):
|
|
236
|
+
takes_value[s] = getattr(act, 'nargs', None) != 0
|
|
237
|
+
cli_args: List[str] = []
|
|
238
|
+
prog_args: List[str] = []
|
|
239
|
+
seen_prog = False
|
|
240
|
+
i, n = 0, len(args)
|
|
241
|
+
while i < n:
|
|
242
|
+
tok = args[i]
|
|
243
|
+
# --opt=value 形式按其选项名识别
|
|
244
|
+
optname = tok.split('=', 1)[0] if tok.startswith('--') else tok
|
|
245
|
+
if optname in takes_value:
|
|
246
|
+
cli_args.append(tok)
|
|
247
|
+
if takes_value[optname] and '=' not in tok:
|
|
248
|
+
i += 1
|
|
249
|
+
if i < n:
|
|
250
|
+
cli_args.append(args[i])
|
|
251
|
+
i += 1
|
|
252
|
+
continue
|
|
253
|
+
if not seen_prog:
|
|
254
|
+
cli_args.append(tok)
|
|
255
|
+
seen_prog = True
|
|
256
|
+
i += 1
|
|
257
|
+
continue
|
|
258
|
+
prog_args.append(tok)
|
|
259
|
+
i += 1
|
|
260
|
+
return cli_args, prog_args
|
|
261
|
+
|
|
262
|
+
|
|
222
263
|
def main(argv: Optional[List[str]] = None) -> int:
|
|
223
264
|
args = list(sys.argv[1:] if argv is None else argv)
|
|
224
265
|
|
|
@@ -246,8 +287,20 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
246
287
|
|
|
247
288
|
console = Console()
|
|
248
289
|
parser = build_parser()
|
|
290
|
+
|
|
291
|
+
# 程序文件之后的裸参数透传给 CIN 程序 (问题反馈 #3): 已知 CLI 选项
|
|
292
|
+
# (含取值) 仍归 argparse, 其余 token 归 arg_count()/arg(i)。
|
|
293
|
+
cli_args, direct_args = _extract_program_args(args, parser)
|
|
294
|
+
if direct_args:
|
|
295
|
+
for tok in direct_args:
|
|
296
|
+
if tok.startswith('-') and tok not in ('-',) \
|
|
297
|
+
and not tok.lstrip('-').split('=')[0].isdigit():
|
|
298
|
+
sys.stderr.write(
|
|
299
|
+
f"note: {tok!r} 不是 CLI 选项, 已作为程序参数传递 "
|
|
300
|
+
f"(arg_count/arg); 如需强制传参请用 `-- {tok}`\n")
|
|
301
|
+
|
|
249
302
|
try:
|
|
250
|
-
ns = parser.parse_args(
|
|
303
|
+
ns = parser.parse_args(cli_args)
|
|
251
304
|
except SystemExit as e:
|
|
252
305
|
# argparse 错误 (未知选项/非法数值): 已打印 usage, 返回其退出码
|
|
253
306
|
return int(e.code) if e.code is not None else 2
|
|
@@ -278,7 +331,8 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
278
331
|
|
|
279
332
|
config = Config()
|
|
280
333
|
_apply_namespace(config, ns)
|
|
281
|
-
|
|
334
|
+
# 直传参数与 `--` 后参数合并 (直传在前, 位置顺序与命令行一致)
|
|
335
|
+
config.program_args = direct_args + program_args
|
|
282
336
|
config.validate()
|
|
283
337
|
|
|
284
338
|
program_file = ns.program
|
|
@@ -23,6 +23,12 @@ Instruction = Tuple[str, List[Operand]]
|
|
|
23
23
|
MASK64 = 0xFFFFFFFFFFFFFFFF
|
|
24
24
|
MASK32 = 0xFFFFFFFF
|
|
25
25
|
|
|
26
|
+
# 核心 VM 机制系统调用: 不触碰宿主状态, 沙箱模式同样放行 (与 SYS < AUDIOPLAY
|
|
27
|
+
# 的数学/字符串类内建同级)。文本语义必须与 Go 引擎 vm.go 保持一致。
|
|
28
|
+
_CORE_SYS_CALLS = frozenset({
|
|
29
|
+
Syscall.ALLOCFRAME, Syscall.TIMEUS, Syscall.TIMENS,
|
|
30
|
+
})
|
|
31
|
+
|
|
26
32
|
|
|
27
33
|
def _f_to_bits(f: float) -> int:
|
|
28
34
|
return struct.unpack('<Q', struct.pack('<d', f))[0]
|
|
@@ -85,7 +91,8 @@ class CPU:
|
|
|
85
91
|
self.pstate: Dict[str, bool] = {'N': False, 'Z': False, 'C': False, 'V': False}
|
|
86
92
|
self.pc = 0
|
|
87
93
|
self.sp = (config.mem_size - Constants.STACK_SLOT) & ~0x7
|
|
88
|
-
self.
|
|
94
|
+
self.heap_base = config.mem_size // 2
|
|
95
|
+
self.heap_ptr = self.heap_base
|
|
89
96
|
|
|
90
97
|
self.logger.dump("CPU 初始化", {
|
|
91
98
|
'memory': f"0x{config.mem_size:x} bytes",
|
|
@@ -1153,7 +1160,11 @@ class CPU:
|
|
|
1153
1160
|
ptr = self.heap_ptr
|
|
1154
1161
|
self.heap_ptr += size
|
|
1155
1162
|
if self.heap_ptr > self.sp:
|
|
1156
|
-
|
|
1163
|
+
free = max(self.sp - ptr, 0)
|
|
1164
|
+
raise ExecutionError(
|
|
1165
|
+
f"Heap exhausted (string operation): need {size} bytes,"
|
|
1166
|
+
f" free {free} bytes (heap 0x{self.heap_base:x}..0x{self.sp:x})."
|
|
1167
|
+
" Try --mem-size (default 65536) or reduce allocations")
|
|
1157
1168
|
self.memory.write_block(ptr, data)
|
|
1158
1169
|
return ptr
|
|
1159
1170
|
|
|
@@ -1170,7 +1181,9 @@ class CPU:
|
|
|
1170
1181
|
call_id = args[0][1]
|
|
1171
1182
|
# 沙箱模式: 拦截全部宿主能力 (音频/画布/文件/进程/环境/网络/Termux),
|
|
1172
1183
|
# 只放行数学/字符串/内存/输出类内建。原生路径在 _try_native_run 已强制回退。
|
|
1173
|
-
|
|
1184
|
+
# 核心机制系统调用 (_CORE_SYS_CALLS) 不触碰宿主状态, 沙箱内照常放行。
|
|
1185
|
+
if (self.config.sandbox_mode and call_id >= Syscall.AUDIOPLAY
|
|
1186
|
+
and call_id not in _CORE_SYS_CALLS):
|
|
1174
1187
|
raise ExecutionError(
|
|
1175
1188
|
"Host capability disabled in sandbox mode (SYS "
|
|
1176
1189
|
f"{call_id}: {Syscall(call_id).name if call_id in Syscall._value2member_map_ else 'UNKNOWN'})")
|
|
@@ -1183,7 +1196,30 @@ class CPU:
|
|
|
1183
1196
|
self.logger.trace(f" SYS #{call_id} ({name}) "
|
|
1184
1197
|
f"x0=0x{x0:x} x1=0x{x1:x} x2=0x{x2:x}")
|
|
1185
1198
|
|
|
1186
|
-
if call_id == Syscall.
|
|
1199
|
+
if call_id == Syscall.ALLOCFRAME:
|
|
1200
|
+
# 函数序言的帧分配 (核心机制, 沙箱放行): 带溢出防护的 SP 递减。
|
|
1201
|
+
# 此前 prologue 直接 ADDI SP, SP, -frame_bytes, 局部大数组会把 SP
|
|
1202
|
+
# 推到负地址 —— 轻则报晦涩的 "address ffff... out of bounds",
|
|
1203
|
+
# 重则静默覆盖全局/堆内存。消息文本必须与 Go 引擎 vm.go 一致。
|
|
1204
|
+
fb = x0
|
|
1205
|
+
guard = self.heap_ptr + 4096
|
|
1206
|
+
# 按有符号比较: SP 绕回 (负地址) 必须判定为溢出, 不能先按
|
|
1207
|
+
# 2^64 取模再比大小 (绕回值会显得巨大而漏判)。
|
|
1208
|
+
new_sp = self.sp - fb
|
|
1209
|
+
if new_sp < guard:
|
|
1210
|
+
avail = max(self.sp - guard, 0)
|
|
1211
|
+
raise ExecutionError(
|
|
1212
|
+
f"Stack overflow: frame needs {fb} bytes, stack headroom "
|
|
1213
|
+
f"only {avail} bytes (SP 0x{self.sp:x}, guard "
|
|
1214
|
+
f"0x{guard:x}, memory {self.config.mem_size} bytes). "
|
|
1215
|
+
"Try --mem-size (default 65536) or smaller local arrays")
|
|
1216
|
+
self.sp = new_sp & MASK64
|
|
1217
|
+
self._set_reg(0, self.sp)
|
|
1218
|
+
elif call_id in (Syscall.TIMEUS, Syscall.TIMENS):
|
|
1219
|
+
# 高精度计时: Unix 纪元微秒/纳秒 (与 Go time.Now() 同基, 非单调钟)
|
|
1220
|
+
ns = time.time_ns()
|
|
1221
|
+
self._set_reg(0, ns // 1000 if call_id == Syscall.TIMEUS else ns)
|
|
1222
|
+
elif call_id == Syscall.ABS:
|
|
1187
1223
|
self._set_reg(0, abs(x0 if x0 < (1 << 63) else x0 - (1 << 64)))
|
|
1188
1224
|
elif call_id in (Syscall.SQRT,):
|
|
1189
1225
|
# 负数定义域: 与 Go 路径一致返回 NaN (不抛 CPython 内部异常)
|
|
@@ -1258,7 +1294,11 @@ class CPU:
|
|
|
1258
1294
|
ptr = self.heap_ptr
|
|
1259
1295
|
self.heap_ptr += size
|
|
1260
1296
|
if self.heap_ptr > self.sp:
|
|
1261
|
-
|
|
1297
|
+
free = max(self.sp - ptr, 0)
|
|
1298
|
+
raise ExecutionError(
|
|
1299
|
+
f"Heap exhausted: need {size} bytes, free {free} bytes"
|
|
1300
|
+
f" (heap 0x{self.heap_base:x}..0x{self.sp:x})."
|
|
1301
|
+
" Try --mem-size (default 65536) or reduce allocations")
|
|
1262
1302
|
self._set_reg(0, ptr)
|
|
1263
1303
|
elif call_id == Syscall.PRINT_FLOAT:
|
|
1264
1304
|
self._emit_text(_format_float(_bits_to_f(x0)))
|
|
@@ -1282,7 +1322,11 @@ class CPU:
|
|
|
1282
1322
|
ptr = self.heap_ptr
|
|
1283
1323
|
self.heap_ptr += size
|
|
1284
1324
|
if self.heap_ptr > self.sp:
|
|
1285
|
-
|
|
1325
|
+
free = max(self.sp - ptr, 0)
|
|
1326
|
+
raise ExecutionError(
|
|
1327
|
+
f"Heap exhausted (string concat): need {size} bytes,"
|
|
1328
|
+
f" free {free} bytes (heap 0x{self.heap_base:x}..0x{self.sp:x})."
|
|
1329
|
+
" Try --mem-size (default 65536) or reduce allocations")
|
|
1286
1330
|
self.memory.write_block(ptr, data)
|
|
1287
1331
|
self._set_reg(0, ptr)
|
|
1288
1332
|
elif call_id == Syscall.BOOL_STR:
|
|
@@ -275,6 +275,10 @@ class Syscall(IntEnum):
|
|
|
275
275
|
AUDIOPAUSE = 134 # audio_pause() -> 0 成功 / -1 (无播放或已暂停)
|
|
276
276
|
AUDIORESUME = 135 # audio_resume() -> 0 成功 / -1 (无暂停可恢复)
|
|
277
277
|
AUDIOLEVEL = 136 # audio_level() -> 当前音量 0..100 (audio_volume 设置值)
|
|
278
|
+
# ---- 核心 VM 机制 (非宿主能力: 解释器/沙箱/原生三路径都可用) ----
|
|
279
|
+
ALLOCFRAME = 137 # alloc_frame(x0=字节数): SP -= x0; 低于堆警戒线则中止
|
|
280
|
+
TIMEUS = 138 # time_us() -> 单调微秒计时 (高精度基准测试用)
|
|
281
|
+
TIMENS = 139 # time_ns() -> 单调纳秒计时 (time_us 的高分辨率版本)
|
|
278
282
|
|
|
279
283
|
|
|
280
284
|
class Cond:
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
// Code CIN 官方标准库: Go 标准库兼容层 (gostd)
|
|
2
2
|
// 用法: import "gostd.cin"
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// 覆盖范围 (纯 CIN 实现, 与 Go 标准库命名的对应关系):
|
|
5
|
+
// strings —— Contains/Index/重复/大小写/裁剪/前后缀/替换等常用函数
|
|
6
|
+
// strconv —— Itoa/Atoi/ParseBool/FormatFloat(经 ftoa) 等转换
|
|
7
|
+
// math —— Sqrt/Abs/Floor/Ceil/Round/Pow 等包装
|
|
8
|
+
// slices —— Contains/Index/Sort(经排序内建)/Reverse 等常用操作
|
|
9
|
+
// os —— 仅 os.Args (go_os_args_len / go_os_args_get)
|
|
10
|
+
// 未覆盖 (避免误解): time、os 文件读写、net/http、sync 等包暂无兼容层 ——
|
|
11
|
+
// 计时请用内建 time_ms/time_us/time_ns, 文件读写用内建 file_* 函数族。
|
|
12
|
+
//
|
|
13
|
+
// 以 pkg.Func 命名习惯提供上述包的常用函数 (go_strings_contains 对应
|
|
14
|
+
// strings.Contains)。命名里的包段与 Go 文档一一对应, 便于从 Go 移植或
|
|
15
|
+
// 查阅语义。
|
|
7
16
|
// 注意: go_os_args_len / go_os_args_get 依赖命令行参数内建 (arg_count /
|
|
8
17
|
// arg, SYS 129..130), 需要原生路径 (CLI `--` 传参或 AOT 产物 os.Args)。
|
|
9
18
|
|
|
@@ -179,9 +179,14 @@ class FastMemory:
|
|
|
179
179
|
|
|
180
180
|
def _check_bounds(self, addr: int, size: int = 1) -> None:
|
|
181
181
|
if not 0 <= addr <= self._size - size:
|
|
182
|
+
hint = ""
|
|
183
|
+
if addr >= (1 << 63):
|
|
184
|
+
# 按位模 2^64 后为负数: 典型成因是栈溢出 (SP 被推到负地址)
|
|
185
|
+
# 或野指针。可读性提示, 与 Go 引擎 vm.go 的文案保持一致。
|
|
186
|
+
hint = " (negative address: stack overflow or bad pointer?)"
|
|
182
187
|
raise MemoryAccessError(
|
|
183
188
|
f"Address 0x{addr:x} out of bounds (memory size 0x{self._size:x}, "
|
|
184
|
-
f"access width {size})")
|
|
189
|
+
f"access width {size}){hint}")
|
|
185
190
|
|
|
186
191
|
def _check_protection(self, addr: int, access: str) -> None:
|
|
187
192
|
if not self.check_access(addr, access):
|
|
@@ -79,6 +79,8 @@ var hostBuiltins = map[string]hostBuiltin{
|
|
|
79
79
|
"chdir": {SysCHDIR, 1, kInt},
|
|
80
80
|
// 时间与系统信息
|
|
81
81
|
"time_ms": {SysTIMEMS, 0, kInt},
|
|
82
|
+
"time_us": {SysTIMEUS, 0, kInt},
|
|
83
|
+
"time_ns": {SysTIMENS, 0, kInt},
|
|
82
84
|
"sleep_ms": {SysSLEEPMS, 1, kInt},
|
|
83
85
|
"cpu_count": {SysCPUCOUNT, 0, kInt},
|
|
84
86
|
"arch_name": {SysARCHNAME, 0, kString},
|
|
@@ -342,9 +344,11 @@ func (c *compiler) genFunctionBody(f *FuncDef) {
|
|
|
342
344
|
c.label(f.name)
|
|
343
345
|
c.emit("PUSH", c.reg(29))
|
|
344
346
|
c.emit("MOV", c.reg(29), c.reg(32))
|
|
347
|
+
// 帧分配走 SYS ALLOCFRAME (带栈溢出防护), 与 Python 编译器逐字节一致:
|
|
348
|
+
// 此前直接 ADDI SP 减帧长, 局部大数组会把 SP 推到负地址 (越界/静默破坏内存)。
|
|
345
349
|
if c.frameBytes != 0 {
|
|
346
|
-
c.emit("
|
|
347
|
-
c.emit("
|
|
350
|
+
c.emit("MOV", c.reg(0), c.imm(int64(c.frameBytes)))
|
|
351
|
+
c.emit("SYS", c.imm(SysALLOCFRAME))
|
|
348
352
|
}
|
|
349
353
|
|
|
350
354
|
// struct 局部变量: 堆分配对象
|
|
@@ -1923,6 +1927,25 @@ func (c *compiler) genCall(name string, args []*Node) *Type {
|
|
|
1923
1927
|
c.emit("SYS", c.imm(SysATOI))
|
|
1924
1928
|
return scalarT(kInt)
|
|
1925
1929
|
}
|
|
1930
|
+
if name == "to_int" || name == "to_float" {
|
|
1931
|
+
// 显式类型转换: to_int 向零截断 (与 int x = 1.9 一致), to_float 提升。
|
|
1932
|
+
// 与 Python 编译器 (cin.py) 的同名内建语义逐一致。
|
|
1933
|
+
argT := c.genValue(args[0])
|
|
1934
|
+
target := scalarT(kInt)
|
|
1935
|
+
if name == "to_float" {
|
|
1936
|
+
target = scalarT(kFloat)
|
|
1937
|
+
}
|
|
1938
|
+
if argT == nil || (argT.Kind != kInt && argT.Kind != kFloat && argT.Kind != kBool) {
|
|
1939
|
+
got := "void"
|
|
1940
|
+
if argT != nil {
|
|
1941
|
+
got = typeName(argT)
|
|
1942
|
+
}
|
|
1943
|
+
c.failf("%s() expects a numeric argument, got %s", name, got)
|
|
1944
|
+
return nil
|
|
1945
|
+
}
|
|
1946
|
+
c.convert(argT, target)
|
|
1947
|
+
return target
|
|
1948
|
+
}
|
|
1926
1949
|
|
|
1927
1950
|
// 宿主能力 (表驱动): 音频 / 画布 / 系统交互 / Termux API
|
|
1928
1951
|
if hb, ok := hostBuiltins[name]; ok {
|
|
@@ -1946,6 +1969,12 @@ func (c *compiler) genCall(name string, args []*Node) *Type {
|
|
|
1946
1969
|
// 用户函数
|
|
1947
1970
|
fdef := c.functions[name]
|
|
1948
1971
|
if fdef == nil {
|
|
1972
|
+
if name == "int" || name == "float" {
|
|
1973
|
+
c.failf("Unknown function: %s (hint: use to_%s(x) for explicit "+
|
|
1974
|
+
"conversion, or rely on implicit promotion in mixed "+
|
|
1975
|
+
"int/float expressions)", name, name)
|
|
1976
|
+
return nil
|
|
1977
|
+
}
|
|
1949
1978
|
c.failf("Unknown function: %s", name)
|
|
1950
1979
|
return nil
|
|
1951
1980
|
}
|
|
@@ -50,6 +50,7 @@ type vmState struct {
|
|
|
50
50
|
sp uint64
|
|
51
51
|
pc int
|
|
52
52
|
heapPtr uint64
|
|
53
|
+
heapBase uint64 // 堆起始 (heapPtr 初值; 报错信息用)
|
|
53
54
|
steps uint64
|
|
54
55
|
flags struct{ N, Z, C, V bool }
|
|
55
56
|
out strings.Builder
|
|
@@ -199,15 +200,16 @@ func Run(bc []byte, mem []byte, entry, sp, heapBase int64, inData []byte,
|
|
|
199
200
|
ent = int(entry)
|
|
200
201
|
}
|
|
201
202
|
vm := &vmState{
|
|
202
|
-
prog:
|
|
203
|
-
entry:
|
|
204
|
-
mem:
|
|
205
|
-
sp:
|
|
206
|
-
pc:
|
|
207
|
-
heapPtr:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
203
|
+
prog: prog,
|
|
204
|
+
entry: ent,
|
|
205
|
+
mem: mem,
|
|
206
|
+
sp: uint64(sp),
|
|
207
|
+
pc: ent,
|
|
208
|
+
heapPtr: uint64(heapBase),
|
|
209
|
+
heapBase: uint64(heapBase),
|
|
210
|
+
rng: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
211
|
+
inData: inData,
|
|
212
|
+
args: currentProgramArgs(),
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
// 键盘监听可能切换终端 raw mode, 任何出口都必须恢复 (幂等)
|
|
@@ -280,7 +282,7 @@ func opcodeSupported(op uint8) bool {
|
|
|
280
282
|
}
|
|
281
283
|
|
|
282
284
|
func syscallSupported(id uint64) bool {
|
|
283
|
-
return id <=
|
|
285
|
+
return id <= sysTIMENS
|
|
284
286
|
}
|
|
285
287
|
|
|
286
288
|
func (vm *vmState) reg(n int) uint64 {
|
|
@@ -342,7 +344,13 @@ func (vm *vmState) val(op operand) (uint64, bool) {
|
|
|
342
344
|
|
|
343
345
|
func (vm *vmState) checkAddr(addr uint64, width int) string {
|
|
344
346
|
if uint64(len(vm.mem)) < uint64(width) || addr > uint64(len(vm.mem))-uint64(width) {
|
|
345
|
-
|
|
347
|
+
hint := ""
|
|
348
|
+
if addr >= 1<<63 {
|
|
349
|
+
// 按位模 2^64 后为负数: 典型成因是栈溢出 (SP 被推到负地址)
|
|
350
|
+
// 或野指针。可读性提示, 与 Python 侧 memory.py 文案一致。
|
|
351
|
+
hint = " (negative address: stack overflow or bad pointer?)"
|
|
352
|
+
}
|
|
353
|
+
return "Address 0x" + strconv.FormatUint(addr, 16) + " out of bounds" + hint
|
|
346
354
|
}
|
|
347
355
|
return ""
|
|
348
356
|
}
|
|
@@ -976,7 +984,14 @@ func (vm *vmState) doSyscall(id uint64) string {
|
|
|
976
984
|
ptr := vm.heapPtr
|
|
977
985
|
vm.heapPtr += size
|
|
978
986
|
if vm.heapPtr > vm.sp {
|
|
979
|
-
|
|
987
|
+
free := uint64(0)
|
|
988
|
+
if vm.sp > ptr {
|
|
989
|
+
free = vm.sp - ptr
|
|
990
|
+
}
|
|
991
|
+
// 文本与 Python 解释器 (cpu.py) 保持一致
|
|
992
|
+
return fmt.Sprintf("Heap exhausted: need %d bytes, free %d bytes "+
|
|
993
|
+
"(heap 0x%x..0x%x). Try --mem-size (default 65536) or "+
|
|
994
|
+
"reduce allocations", size, free, vm.heapBase, vm.sp)
|
|
980
995
|
}
|
|
981
996
|
vm.setReg(0, ptr)
|
|
982
997
|
case sysPRINTFLO:
|
|
@@ -1005,7 +1020,14 @@ func (vm *vmState) doSyscall(id uint64) string {
|
|
|
1005
1020
|
ptr := vm.heapPtr
|
|
1006
1021
|
vm.heapPtr += size
|
|
1007
1022
|
if vm.heapPtr > vm.sp {
|
|
1008
|
-
|
|
1023
|
+
free := uint64(0)
|
|
1024
|
+
if vm.sp > ptr {
|
|
1025
|
+
free = vm.sp - ptr
|
|
1026
|
+
}
|
|
1027
|
+
return fmt.Sprintf("Heap exhausted (string concat): need %d bytes, "+
|
|
1028
|
+
"free %d bytes (heap 0x%x..0x%x). Try --mem-size "+
|
|
1029
|
+
"(default 65536) or reduce allocations",
|
|
1030
|
+
size, free, vm.heapBase, vm.sp)
|
|
1009
1031
|
}
|
|
1010
1032
|
if e := vm.checkAddr(ptr, len(data)); e != "" {
|
|
1011
1033
|
return e
|
|
@@ -1313,6 +1335,28 @@ func (vm *vmState) doSyscall(id uint64) string {
|
|
|
1313
1335
|
vm.setReg(0, vm.argAt(x0))
|
|
1314
1336
|
case sysREADLINE:
|
|
1315
1337
|
vm.setReg(0, vm.readLineStr())
|
|
1338
|
+
// 核心 VM 机制 (非宿主能力, 与 Python 解释器 cpu.py 语义逐一致)
|
|
1339
|
+
case sysALLOCFRAME:
|
|
1340
|
+
fb := x0
|
|
1341
|
+
guard := vm.heapPtr + 4096
|
|
1342
|
+
// 有符号比较: SP 绕回 (负地址) 必须判溢出, 不能先取模再比大小
|
|
1343
|
+
newSP := int64(vm.sp) - int64(fb)
|
|
1344
|
+
if newSP < int64(guard) {
|
|
1345
|
+
avail := int64(vm.sp) - int64(guard)
|
|
1346
|
+
if avail < 0 {
|
|
1347
|
+
avail = 0
|
|
1348
|
+
}
|
|
1349
|
+
return fmt.Sprintf("Stack overflow: frame needs %d bytes, stack "+
|
|
1350
|
+
"headroom only %d bytes (SP 0x%x, guard 0x%x, memory %d "+
|
|
1351
|
+
"bytes). Try --mem-size (default 65536) or smaller local "+
|
|
1352
|
+
"arrays", fb, avail, vm.sp, guard, len(vm.mem))
|
|
1353
|
+
}
|
|
1354
|
+
vm.sp = uint64(newSP)
|
|
1355
|
+
vm.setReg(0, vm.sp)
|
|
1356
|
+
case sysTIMEUS:
|
|
1357
|
+
vm.setReg(0, uint64(time.Now().UnixNano()/1000))
|
|
1358
|
+
case sysTIMENS:
|
|
1359
|
+
vm.setReg(0, uint64(time.Now().UnixNano()))
|
|
1316
1360
|
default:
|
|
1317
1361
|
return "Unknown SYS call id"
|
|
1318
1362
|
}
|
|
@@ -1326,7 +1370,14 @@ func (vm *vmState) heapDupString(s string) (uint64, string) {
|
|
|
1326
1370
|
ptr := vm.heapPtr
|
|
1327
1371
|
vm.heapPtr += size
|
|
1328
1372
|
if vm.heapPtr > vm.sp {
|
|
1329
|
-
|
|
1373
|
+
free := uint64(0)
|
|
1374
|
+
if vm.sp > ptr {
|
|
1375
|
+
free = vm.sp - ptr
|
|
1376
|
+
}
|
|
1377
|
+
return 0, fmt.Sprintf("Heap exhausted (string operation): need %d "+
|
|
1378
|
+
"bytes, free %d bytes (heap 0x%x..0x%x). Try --mem-size "+
|
|
1379
|
+
"(default 65536) or reduce allocations",
|
|
1380
|
+
size, free, vm.heapBase, vm.sp)
|
|
1330
1381
|
}
|
|
1331
1382
|
if e := vm.checkAddr(ptr, len(data)); e != "" {
|
|
1332
1383
|
return 0, e
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Code CIN 命令行参数与行输入演示 (SYS 129..131, 跨平台一致)
|
|
2
|
-
// 运行: codecin examples/args_demo.cin
|
|
3
|
-
// codecin examples/args_demo.cin -- alice 42
|
|
4
|
-
//
|
|
5
|
-
//
|
|
2
|
+
// 运行: codecin examples/args_demo.cin alice 42 (程序名后直传)
|
|
3
|
+
// codecin examples/args_demo.cin -- alice 42 (`--` 显式分隔, 兼容旧法)
|
|
4
|
+
// 两种方式等价; 含 `--` 前缀形如选项的参数必须走 `--`。AOT 产物直接取进程
|
|
5
|
+
// os.Args[1:]。另见 stdlib: gostd.cin 的 go_os_args_len() / go_os_args_get(i)。
|
|
6
6
|
|
|
7
7
|
function main() -> int {
|
|
8
8
|
int n = arg_count()
|