codecin 6.0.0__tar.gz → 6.0.1__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.
Files changed (219) hide show
  1. {codecin-6.0.0 → codecin-6.0.1}/CHANGELOG.md +21 -0
  2. {codecin-6.0.0/codecin.egg-info → codecin-6.0.1}/PKG-INFO +3 -2
  3. {codecin-6.0.0 → codecin-6.0.1}/README.md +2 -1
  4. {codecin-6.0.0 → codecin-6.0.1}/codecin/__init__.py +1 -1
  5. {codecin-6.0.0 → codecin-6.0.1}/codecin/crom.py +171 -20
  6. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/version_gen.go +1 -1
  7. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/vm.go +14 -0
  8. {codecin-6.0.0 → codecin-6.0.1/codecin.egg-info}/PKG-INFO +3 -2
  9. {codecin-6.0.0 → codecin-6.0.1}/codecin.egg-info/SOURCES.txt +1 -0
  10. codecin-6.0.1/tests/test_bin_compat.py +195 -0
  11. {codecin-6.0.0 → codecin-6.0.1}/LICENSE +0 -0
  12. {codecin-6.0.0 → codecin-6.0.1}/MANIFEST.in +0 -0
  13. {codecin-6.0.0 → codecin-6.0.1}/codecin/aot.py +0 -0
  14. {codecin-6.0.0 → codecin-6.0.1}/codecin/assembler.py +0 -0
  15. {codecin-6.0.0 → codecin-6.0.1}/codecin/cin.py +0 -0
  16. {codecin-6.0.0 → codecin-6.0.1}/codecin/cli.py +0 -0
  17. {codecin-6.0.0 → codecin-6.0.1}/codecin/config.py +0 -0
  18. {codecin-6.0.0 → codecin-6.0.1}/codecin/console.py +0 -0
  19. {codecin-6.0.0 → codecin-6.0.1}/codecin/cpu.py +0 -0
  20. {codecin-6.0.0 → codecin-6.0.1}/codecin/disasm.py +0 -0
  21. {codecin-6.0.0 → codecin-6.0.1}/codecin/environment.py +0 -0
  22. {codecin-6.0.0 → codecin-6.0.1}/codecin/errors.py +0 -0
  23. {codecin-6.0.0 → codecin-6.0.1}/codecin/isa.py +0 -0
  24. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/array.cin +0 -0
  25. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/bigint.cin +0 -0
  26. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/bits.cin +0 -0
  27. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/bitset.cin +0 -0
  28. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/codec.cin +0 -0
  29. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/combin.cin +0 -0
  30. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/conv.cin +0 -0
  31. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/cppstd.cin +0 -0
  32. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/cstd.cin +0 -0
  33. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/csv.cin +0 -0
  34. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/dp.cin +0 -0
  35. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/ffi.cin +0 -0
  36. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/fmt.cin +0 -0
  37. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/frac.cin +0 -0
  38. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/gostd.cin +0 -0
  39. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/graph.cin +0 -0
  40. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/gui.cin +0 -0
  41. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/hash.cin +0 -0
  42. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/heap.cin +0 -0
  43. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/io.cin +0 -0
  44. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/json.cin +0 -0
  45. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/key.cin +0 -0
  46. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/math.cin +0 -0
  47. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/matrix.cin +0 -0
  48. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/net.cin +0 -0
  49. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/path.cin +0 -0
  50. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/queue.cin +0 -0
  51. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/rand.cin +0 -0
  52. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/set.cin +0 -0
  53. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/sort.cin +0 -0
  54. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/stat.cin +0 -0
  55. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/str.cin +0 -0
  56. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/termux.cin +0 -0
  57. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/test.cin +0 -0
  58. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/text.cin +0 -0
  59. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/time.cin +0 -0
  60. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/token.cin +0 -0
  61. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/tree.cin +0 -0
  62. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/unionfind.cin +0 -0
  63. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/validate.cin +0 -0
  64. {codecin-6.0.0 → codecin-6.0.1}/codecin/lib/vec.cin +0 -0
  65. {codecin-6.0.0 → codecin-6.0.1}/codecin/logger.py +0 -0
  66. {codecin-6.0.0 → codecin-6.0.1}/codecin/memory.py +0 -0
  67. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/aot/aot.go +0 -0
  68. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/aot/stub_main.go.txt +0 -0
  69. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/build.ps1 +0 -0
  70. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/build.sh +0 -0
  71. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/compiler/codegen.go +0 -0
  72. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/compiler/compile_test.go +0 -0
  73. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/compiler/parser.go +0 -0
  74. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/compiler/syscalls.go +0 -0
  75. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/compiler/tokenizer.go +0 -0
  76. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/compiler/types.go +0 -0
  77. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/args.go +0 -0
  78. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/audio.go +0 -0
  79. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/audio_other.go +0 -0
  80. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/audio_test.go +0 -0
  81. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/audio_windows.go +0 -0
  82. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/canvas.go +0 -0
  83. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/console_other.go +0 -0
  84. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/console_windows.go +0 -0
  85. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/crom.go +0 -0
  86. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/encode.go +0 -0
  87. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/engine_test.go +0 -0
  88. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/ffi_nocgo.go +0 -0
  89. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/ffi_registry.go +0 -0
  90. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/ffi_unix.go +0 -0
  91. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/ffi_windows.go +0 -0
  92. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/gui.go +0 -0
  93. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/gui_stub.go +0 -0
  94. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/gui_test.go +0 -0
  95. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/gui_windows.go +0 -0
  96. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/gui_x11.go +0 -0
  97. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/host_test.go +0 -0
  98. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/hostcrypto.go +0 -0
  99. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/hostdesktop.go +0 -0
  100. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/hostnet.go +0 -0
  101. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/isa_gen.go +0 -0
  102. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/keyboard.go +0 -0
  103. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/keyboard_other.go +0 -0
  104. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/keyboard_stub.go +0 -0
  105. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/keyboard_termios_darwin.go +0 -0
  106. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/keyboard_termios_linux.go +0 -0
  107. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/keyboard_test.go +0 -0
  108. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/keyboard_windows.go +0 -0
  109. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/keyboard_windows_test.go +0 -0
  110. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/mem_other.go +0 -0
  111. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/mem_windows.go +0 -0
  112. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/native.go +0 -0
  113. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/netsock.go +0 -0
  114. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/ops_ext.go +0 -0
  115. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/stdin.go +0 -0
  116. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/stdin_other.go +0 -0
  117. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/stdin_windows.go +0 -0
  118. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/system.go +0 -0
  119. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/engine/termux.go +0 -0
  120. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/go.mod +0 -0
  121. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/ir/ir.go +0 -0
  122. {codecin-6.0.0 → codecin-6.0.1}/codecin/native/main.go +0 -0
  123. {codecin-6.0.0 → codecin-6.0.1}/codecin/native.py +0 -0
  124. {codecin-6.0.0 → codecin-6.0.1}/codecin/native_codegen.py +0 -0
  125. {codecin-6.0.0 → codecin-6.0.1}/codecin/object.py +0 -0
  126. {codecin-6.0.0 → codecin-6.0.1}/codecin/optimizer.py +0 -0
  127. {codecin-6.0.0 → codecin-6.0.1}/codecin/registers.py +0 -0
  128. {codecin-6.0.0 → codecin-6.0.1}/codecin/stats.py +0 -0
  129. {codecin-6.0.0 → codecin-6.0.1}/codecin/stdlib.py +0 -0
  130. {codecin-6.0.0 → codecin-6.0.1}/codecin/version.py +0 -0
  131. {codecin-6.0.0 → codecin-6.0.1}/codecin.egg-info/dependency_links.txt +0 -0
  132. {codecin-6.0.0 → codecin-6.0.1}/codecin.egg-info/entry_points.txt +0 -0
  133. {codecin-6.0.0 → codecin-6.0.1}/codecin.egg-info/requires.txt +0 -0
  134. {codecin-6.0.0 → codecin-6.0.1}/codecin.egg-info/top_level.txt +0 -0
  135. {codecin-6.0.0 → codecin-6.0.1}/examples/args_demo.cin +0 -0
  136. {codecin-6.0.0 → codecin-6.0.1}/examples/asm_constants.asm +0 -0
  137. {codecin-6.0.0 → codecin-6.0.1}/examples/bitwise_builtins.cin +0 -0
  138. {codecin-6.0.0 → codecin-6.0.1}/examples/control_flow.cin +0 -0
  139. {codecin-6.0.0 → codecin-6.0.1}/examples/enum_range_for.cin +0 -0
  140. {codecin-6.0.0 → codecin-6.0.1}/examples/gui_demo.cin +0 -0
  141. {codecin-6.0.0 → codecin-6.0.1}/examples/literals_types.cin +0 -0
  142. {codecin-6.0.0 → codecin-6.0.1}/examples/local_audio.cin +0 -0
  143. {codecin-6.0.0 → codecin-6.0.1}/examples/modules_demo.cin +0 -0
  144. {codecin-6.0.0 → codecin-6.0.1}/examples/stdlib_demo.cin +0 -0
  145. {codecin-6.0.0 → codecin-6.0.1}/examples/system_interaction.cin +0 -0
  146. {codecin-6.0.0 → codecin-6.0.1}/misc/vim/ftdetect/codecin.vim +0 -0
  147. {codecin-6.0.0 → codecin-6.0.1}/misc/vim/syntax/cin.vim +0 -0
  148. {codecin-6.0.0 → codecin-6.0.1}/misc/vim/syntax/codecinasm.vim +0 -0
  149. {codecin-6.0.0 → codecin-6.0.1}/misc/vscode-cin/.vscodeignore +0 -0
  150. {codecin-6.0.0 → codecin-6.0.1}/misc/vscode-cin/README.md +0 -0
  151. {codecin-6.0.0 → codecin-6.0.1}/misc/vscode-cin/build.bat +0 -0
  152. {codecin-6.0.0 → codecin-6.0.1}/misc/vscode-cin/language-configuration.json +0 -0
  153. {codecin-6.0.0 → codecin-6.0.1}/misc/vscode-cin/package.json +0 -0
  154. {codecin-6.0.0 → codecin-6.0.1}/misc/vscode-cin/snippets/cin.json +0 -0
  155. {codecin-6.0.0 → codecin-6.0.1}/misc/vscode-cin/syntaxes/cin.tmLanguage.json +0 -0
  156. {codecin-6.0.0 → codecin-6.0.1}/pyproject.toml +0 -0
  157. {codecin-6.0.0 → codecin-6.0.1}/script/bump_version.py +0 -0
  158. {codecin-6.0.0 → codecin-6.0.1}/script/check_paths.py +0 -0
  159. {codecin-6.0.0 → codecin-6.0.1}/script/gen_isa_docs.py +0 -0
  160. {codecin-6.0.0 → codecin-6.0.1}/script/gen_native_isa.py +0 -0
  161. {codecin-6.0.0 → codecin-6.0.1}/setup.cfg +0 -0
  162. {codecin-6.0.0 → codecin-6.0.1}/setup.py +0 -0
  163. {codecin-6.0.0 → codecin-6.0.1}/tests/test_aggregate_and_const.py +0 -0
  164. {codecin-6.0.0 → codecin-6.0.1}/tests/test_all_libs_together.py +0 -0
  165. {codecin-6.0.0 → codecin-6.0.1}/tests/test_aot.py +0 -0
  166. {codecin-6.0.0 → codecin-6.0.1}/tests/test_assembler_ext.py +0 -0
  167. {codecin-6.0.0 → codecin-6.0.1}/tests/test_cin_host.py +0 -0
  168. {codecin-6.0.0 → codecin-6.0.1}/tests/test_cin_host_ext.py +0 -0
  169. {codecin-6.0.0 → codecin-6.0.1}/tests/test_cin_new_features.py +0 -0
  170. {codecin-6.0.0 → codecin-6.0.1}/tests/test_cin_syntax.py +0 -0
  171. {codecin-6.0.0 → codecin-6.0.1}/tests/test_cin_syntax_ext.py +0 -0
  172. {codecin-6.0.0 → codecin-6.0.1}/tests/test_cin_system.py +0 -0
  173. {codecin-6.0.0 → codecin-6.0.1}/tests/test_cli.py +0 -0
  174. {codecin-6.0.0 → codecin-6.0.1}/tests/test_compiler_errors.py +0 -0
  175. {codecin-6.0.0 → codecin-6.0.1}/tests/test_environment.py +0 -0
  176. {codecin-6.0.0 → codecin-6.0.1}/tests/test_examples.py +0 -0
  177. {codecin-6.0.0 → codecin-6.0.1}/tests/test_features_ab.py +0 -0
  178. {codecin-6.0.0 → codecin-6.0.1}/tests/test_features_import.py +0 -0
  179. {codecin-6.0.0 → codecin-6.0.1}/tests/test_features_importloc.py +0 -0
  180. {codecin-6.0.0 → codecin-6.0.1}/tests/test_ffi_net.py +0 -0
  181. {codecin-6.0.0 → codecin-6.0.1}/tests/test_gui.py +0 -0
  182. {codecin-6.0.0 → codecin-6.0.1}/tests/test_index_subscript_type.py +0 -0
  183. {codecin-6.0.0 → codecin-6.0.1}/tests/test_isa_single_source.py +0 -0
  184. {codecin-6.0.0 → codecin-6.0.1}/tests/test_issue_regress.py +0 -0
  185. {codecin-6.0.0 → codecin-6.0.1}/tests/test_keyboard.py +0 -0
  186. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_bigint.py +0 -0
  187. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_bitset.py +0 -0
  188. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_codec.py +0 -0
  189. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_combin.py +0 -0
  190. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_compat.py +0 -0
  191. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_csv.py +0 -0
  192. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_dp.py +0 -0
  193. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_fmt.py +0 -0
  194. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_frac.py +0 -0
  195. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_graph.py +0 -0
  196. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_heap.py +0 -0
  197. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_path.py +0 -0
  198. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_set.py +0 -0
  199. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_text.py +0 -0
  200. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_token.py +0 -0
  201. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_tree.py +0 -0
  202. {codecin-6.0.0 → codecin-6.0.1}/tests/test_lib_unionfind.py +0 -0
  203. {codecin-6.0.0 → codecin-6.0.1}/tests/test_libs.py +0 -0
  204. {codecin-6.0.0 → codecin-6.0.1}/tests/test_libs_ext.py +0 -0
  205. {codecin-6.0.0 → codecin-6.0.1}/tests/test_literals_and_bom.py +0 -0
  206. {codecin-6.0.0 → codecin-6.0.1}/tests/test_native_codegen.py +0 -0
  207. {codecin-6.0.0 → codecin-6.0.1}/tests/test_native_hardening.py +0 -0
  208. {codecin-6.0.0 → codecin-6.0.1}/tests/test_native_lib_lookup.py +0 -0
  209. {codecin-6.0.0 → codecin-6.0.1}/tests/test_nested_subscript_codegen.py +0 -0
  210. {codecin-6.0.0 → codecin-6.0.1}/tests/test_no_go_cli.py +0 -0
  211. {codecin-6.0.0 → codecin-6.0.1}/tests/test_object_link.py +0 -0
  212. {codecin-6.0.0 → codecin-6.0.1}/tests/test_p0_fixes.py +0 -0
  213. {codecin-6.0.0 → codecin-6.0.1}/tests/test_packaging.py +0 -0
  214. {codecin-6.0.0 → codecin-6.0.1}/tests/test_paths_consistency.py +0 -0
  215. {codecin-6.0.0 → codecin-6.0.1}/tests/test_sparse_memory.py +0 -0
  216. {codecin-6.0.0 → codecin-6.0.1}/tests/test_struct_value_semantics.py +0 -0
  217. {codecin-6.0.0 → codecin-6.0.1}/tests/test_switch_semantics.py +0 -0
  218. {codecin-6.0.0 → codecin-6.0.1}/tests/test_version.py +0 -0
  219. {codecin-6.0.0 → codecin-6.0.1}/tests/test_workflows.py +0 -0
@@ -5,6 +5,27 @@
5
5
 
6
6
  ---
7
7
 
8
+ ## [6.0.1] - 2026-10-18
9
+
10
+ ### 修复 (Fixed)
11
+
12
+ - **旧版 BIN v2 在 v5.9.0+ 内存布局下首条指令即误报栈溢出**:v2 头部只记录
13
+ `mem_size`/`sp`、不记录 `heap_base`,旧运行时固定按 `heap_base = mem_size // 2`
14
+ 布局。用 v6.0.0 默认 1 GiB 加载旧版 64 KiB BIN 时,SP(≈`0xfff0`)远低于新
15
+ `heap_base`(`0x20000000`),首条 `PUSH`/`SYS ALLOCFRAME` 报
16
+ `Stack overflow (collides with heap)`,看起来像真实栈溢出。现在 `load_bin()`
17
+ 在**加载阶段**检测到 v2 布局与当前 `--mem-size` 不一致时直接报
18
+ `CPUSimulatorError`(CLI 显示 `Load Error`),列出文件头/当前布局并提示
19
+ “用当前版本重新编译”或“使用 `--mem-size <文件头 mem_size>`”;原生引擎也会在
20
+ 执行前拒绝 `SP < heap_base` 的非法初始状态(`steps=0`)。
21
+ - BIN v2/v3 加载增加头部元数据校验:`mem_size` / `SP` / `heap_base` 越界、
22
+ 段地址越过声明内存、文件截断等损坏输入均在加载阶段给出明确错误,而不是
23
+ 执行到首条指令后才失败。
24
+ - 文档补充“格式可读 ≠ 内存布局兼容”:BIN v2 需匹配 `--mem-size`,v5.9.0 之前
25
+ 的旧 BIN 建议重新编译(见 `docs/runtime/formats.md`)。
26
+
27
+ ---
28
+
8
29
  ## [6.0.0] - 2026-10-17
9
30
 
10
31
  ### 新增 (Added)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecin
3
- Version: 6.0.0
3
+ Version: 6.0.1
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
@@ -311,7 +311,8 @@ FADD, FSUB, FMUL, FDIV, FCMP, FCVT, FABS, FNEG, LDRS, STRS, VADD, VSUB, VMUL, VD
311
311
 
312
312
  CROM v4 采用**段式内存镜像**: 只保存已分配的 4 KiB 页, 镜像体积与实际使用的内存成正比
313
313
  (默认 1 GiB 逻辑空间也只会写出真正写入过的页); 旧版 v3 整块镜像仍可读取 (仅兼容)。
314
- `.bin` 字节码格式同步升级为 **BIN v3** (同样段式, 旧 v2 仅兼容读取)。
314
+ `.bin` 字节码格式同步升级为 **BIN v3** (同样段式, 旧 v2 仅兼容读取; v2 不记录 `heap_base`,
315
+ 需用 `--mem-size` 指定与文件头一致的内存布局, 否则加载阶段会报错并提示处理方式)。
315
316
 
316
317
  ```mermaid
317
318
  block-beta
@@ -293,7 +293,8 @@ FADD, FSUB, FMUL, FDIV, FCMP, FCVT, FABS, FNEG, LDRS, STRS, VADD, VSUB, VMUL, VD
293
293
 
294
294
  CROM v4 采用**段式内存镜像**: 只保存已分配的 4 KiB 页, 镜像体积与实际使用的内存成正比
295
295
  (默认 1 GiB 逻辑空间也只会写出真正写入过的页); 旧版 v3 整块镜像仍可读取 (仅兼容)。
296
- `.bin` 字节码格式同步升级为 **BIN v3** (同样段式, 旧 v2 仅兼容读取)。
296
+ `.bin` 字节码格式同步升级为 **BIN v3** (同样段式, 旧 v2 仅兼容读取; v2 不记录 `heap_base`,
297
+ 需用 `--mem-size` 指定与文件头一致的内存布局, 否则加载阶段会报错并提示处理方式)。
297
298
 
298
299
  ```mermaid
299
300
  block-beta
@@ -1,4 +1,4 @@
1
- __version__ = "6.0.0"
1
+ __version__ = "6.0.1"
2
2
  __author__ = "ByUsi Studio"
3
3
 
4
4
  from .config import Config
@@ -7,6 +7,7 @@ v5.9.0 起程序内存默认 1 GiB, 不再整块存储:
7
7
  CROM 优先调用 Go 原生库 (codecin.native) 进行压缩/解压, 无原生库时回退 zlib。
8
8
  """
9
9
 
10
+ import os
10
11
  import struct
11
12
  import zlib
12
13
  from typing import TYPE_CHECKING, List, Optional, Tuple
@@ -185,6 +186,120 @@ def _load_crom_v3(memory: 'FastMemory', data: bytes,
185
186
 
186
187
  BIN_V3_HEADER = 5 + 1 + 8 + 4 + 8 + 8 + 4 + 4 + 8 # 50
187
188
 
189
+ #: v2 容器头长度: MAGIC 5 + ver 1 + mem_size u32 + entry u32 + sp u64 +
190
+ #: bc_len u32 + 保留 8B。
191
+ BIN_V2_HEADER = 34
192
+ #: 与 Go 引擎 vm.push() / SYS ALLOCFRAME 的栈-堆隔离区一致。SP 必须高于
193
+ #: heap_base + 该值, 否则首条 PUSH/ALLOCFRAME 就会被判为栈堆碰撞。
194
+ BIN_STACK_GUARD = 4096
195
+ #: v5.9.0 之前的默认内存 (64 KiB): 旧 BIN v2 的迁移提示用。
196
+ LEGACY_BIN_MEM_SIZE = 64 * 1024
197
+
198
+
199
+ def _bin_error_name(path: Optional[str]) -> str:
200
+ return f" '{os.path.basename(path)}'" if path else ""
201
+
202
+
203
+ def _bin_actions(path: Optional[str], mem_size: int) -> List[str]:
204
+ """旧版 BIN 布局不兼容时的可执行建议 (两行)。"""
205
+ target = os.path.basename(path) if path else "<程序>.bin"
206
+ return [
207
+ "请任选其一:",
208
+ " 1) 用当前版本重新编译源码: "
209
+ "codecin <源码>.cin --compile -o <新文件>.bin",
210
+ f" 2) 用与字节码一致的内存布局运行: "
211
+ f"codecin {target} --mem-size {mem_size}",
212
+ ]
213
+
214
+
215
+ def _bin_header_layout_error(version: int, mem_size: int, sp: int,
216
+ heap_base: int, reason: str) -> CPUSimulatorError:
217
+ """头部自相矛盾 (SP/heap/mem_size 越界): 在加载阶段直接拒绝。"""
218
+ return CPUSimulatorError(
219
+ f"BIN v{version} 头部记录的栈/堆布局非法: {reason}。\n"
220
+ f" mem_size={mem_size} bytes (0x{mem_size:x}), sp=0x{sp:x}, "
221
+ f"heap_base=0x{heap_base:x}\n"
222
+ " 该文件可能已损坏, 或由不兼容的工具链生成; "
223
+ "请用当前版本重新编译源码后重试。")
224
+
225
+
226
+ def _legacy_bin_layout_error(path: Optional[str], mem_size: int, sp: int,
227
+ heap_base: int, runtime_mem: int,
228
+ runtime_heap: int) -> CPUSimulatorError:
229
+ """v2 文件记录的布局与当前 --mem-size 布局不兼容。
230
+
231
+ 典型场景: v5.9.0 之前默认 64 KiB 内存编译的 BIN (SP≈0xfff0) 被 v5.9.0+
232
+ 的默认 1 GiB 布局加载, heap_base 变成 0x20000000, SP 低于堆警戒线。
233
+ 必须在这里拦住: 否则执行首条 PUSH/ALLOCFRAME 才会报
234
+ "Stack overflow (collides with heap)", 很容易被误解为真实栈溢出。
235
+ """
236
+ lines = [
237
+ f"无法加载旧版 BIN v2 字节码{_bin_error_name(path)}: "
238
+ "其初始 SP 约定与当前内存布局不兼容。",
239
+ f" 文件头布局: mem_size={mem_size} bytes (0x{mem_size:x}), "
240
+ f"sp=0x{sp:x}, 期望 heap_base=0x{heap_base:x}",
241
+ f" 当前运行布局: mem_size={runtime_mem} bytes (0x{runtime_mem:x}), "
242
+ f"heap_base=0x{runtime_heap:x}",
243
+ " BIN v2 只记录 SP、不记录 heap_base;旧运行时固定按 "
244
+ "heap_base = mem_size // 2 计算堆基址。",
245
+ ]
246
+ if sp < runtime_heap + BIN_STACK_GUARD + Constants.STACK_SLOT:
247
+ lines.extend([
248
+ " 按当前布局执行时 SP 低于 heap_base + 4096 的栈保护线, "
249
+ "首条 PUSH/ALLOCFRAME 会被误报为",
250
+ ' "Stack overflow (collides with heap)" (并非真实栈写满)。',
251
+ ])
252
+ else:
253
+ lines.append(
254
+ f" 当前布局会把堆基址从 0x{heap_base:x} 抬高到 "
255
+ f"0x{runtime_heap:x}, 改变字节码编译时的栈/堆边界。")
256
+ if mem_size == LEGACY_BIN_MEM_SIZE:
257
+ lines.append(
258
+ " 该字节码可能由 v5.9.0 之前的旧版本 (默认 64 KB 内存) 编译; "
259
+ "格式可读不等于内存布局兼容。")
260
+ else:
261
+ lines.append(
262
+ " 该字节码的初始 SP 约定与当前 --mem-size 布局不匹配; "
263
+ "格式可读不等于内存布局兼容。")
264
+ lines.extend(_bin_actions(path, mem_size))
265
+ return CPUSimulatorError("\n".join(lines))
266
+
267
+
268
+ def _validate_bin_stack_layout(version: int, mem_size: int, sp: int,
269
+ heap_base: int) -> None:
270
+ """校验头部的 mem_size / SP / heap_base 是否自洽。
271
+
272
+ 这里只做与运行环境无关的检查; v2 与当前 --mem-size 的差异由
273
+ :func:`_legacy_bin_layout_error` 处理。
274
+ """
275
+ if mem_size < 256:
276
+ raise _bin_header_layout_error(
277
+ version, mem_size, sp, heap_base,
278
+ "mem_size 过小 (最小 256 bytes)")
279
+ if mem_size > (1 << 40):
280
+ raise _bin_header_layout_error(
281
+ version, mem_size, sp, heap_base,
282
+ "mem_size 超出引擎上限 (1 TiB)")
283
+ if heap_base > mem_size - Constants.STACK_SLOT:
284
+ raise _bin_header_layout_error(
285
+ version, mem_size, sp, heap_base,
286
+ "heap_base 超出内存范围")
287
+ if sp < Constants.STACK_SLOT or sp > mem_size - Constants.STACK_SLOT:
288
+ raise _bin_header_layout_error(
289
+ version, mem_size, sp, heap_base,
290
+ "SP 超出内存范围或未给栈槽位留出空间")
291
+ if sp < heap_base:
292
+ raise _bin_header_layout_error(
293
+ version, mem_size, sp, heap_base,
294
+ "SP 低于 heap_base")
295
+ # 只有内存本身足够大、能容纳 4 KiB 栈堆隔离区时才强制守卫线;
296
+ # 极小的 --mem-size 布局 (例如只跑 HALT 的 .bin) 不应仅因此被拒绝。
297
+ min_sp = heap_base + BIN_STACK_GUARD + Constants.STACK_SLOT
298
+ if min_sp <= mem_size - Constants.STACK_SLOT and sp < min_sp:
299
+ raise _bin_header_layout_error(
300
+ version, mem_size, sp, heap_base,
301
+ "SP 低于堆警戒线 (heap_base + 4096)")
302
+
188
303
 
189
304
  def save_bin(cpu: 'CPU', path: str, logger: Optional['Logger'] = None) -> None:
190
305
  from .native import encode_program
@@ -211,33 +326,46 @@ def save_bin(cpu: 'CPU', path: str, logger: Optional['Logger'] = None) -> None:
211
326
 
212
327
 
213
328
  def load_bin(cpu: 'CPU', path: str) -> None:
214
- from .native import decode_program
215
329
  with open(path, 'rb') as f:
216
330
  data = f.read()
217
331
 
218
- if data[:5] != Constants.MAGIC_NUMBER:
332
+ if len(data) < 6 or data[:5] != Constants.MAGIC_NUMBER:
219
333
  raise CPUSimulatorError("Invalid binary file (bad magic)")
220
334
  version = data[5]
221
335
  if version == Constants.BIN_VERSION:
222
- _load_bin_v3(cpu, data)
336
+ _load_bin_v3(cpu, data, path)
223
337
  return
224
338
  if version == Constants.BIN_VERSION_V2:
225
- _load_bin_v2(cpu, data)
339
+ _load_bin_v2(cpu, data, path)
226
340
  return
227
341
  raise CPUSimulatorError(f"Unsupported binary version: {version}")
228
342
 
229
343
 
230
- def _load_bin_v3(cpu: 'CPU', data: bytes) -> None:
344
+ def _load_bin_v3(cpu: 'CPU', data: bytes, path: Optional[str] = None) -> None:
231
345
  from .native import decode_program
346
+ if len(data) < BIN_V3_HEADER:
347
+ raise CPUSimulatorError("BIN v3 头部不完整 (文件损坏?)")
232
348
  mem_size = struct.unpack_from('<Q', data, 6)[0]
233
349
  entry = struct.unpack_from('<I', data, 14)[0]
234
350
  sp = struct.unpack_from('<Q', data, 18)[0]
235
351
  heap_base = struct.unpack_from('<Q', data, 26)[0]
236
352
  bc_len = struct.unpack_from('<I', data, 34)[0]
237
353
  seg_count = struct.unpack_from('<I', data, 38)[0]
354
+
355
+ _validate_bin_stack_layout(3, mem_size, sp, heap_base)
356
+
238
357
  segs = _unpack_segments(data[BIN_V3_HEADER:], seg_count)
239
- bytecode = data[BIN_V3_HEADER + len(_pack_segments(segs)):
240
- BIN_V3_HEADER + len(_pack_segments(segs)) + bc_len]
358
+ packed_segs = _pack_segments(segs)
359
+ bytecode_off = BIN_V3_HEADER + len(packed_segs)
360
+ if bytecode_off + bc_len > len(data):
361
+ raise CPUSimulatorError("BIN v3 字节码不完整 (文件损坏?)")
362
+ # 段表声明的内容不能越过头部声明的内存大小。
363
+ for addr, seg_data in segs:
364
+ if addr + len(seg_data) > mem_size:
365
+ raise CPUSimulatorError(
366
+ f"BIN v3 段地址越界: segment 0x{addr:x}+{len(seg_data)} "
367
+ f"超出头部 mem_size={mem_size} bytes (文件损坏?)")
368
+ bytecode = data[bytecode_off:bytecode_off + bc_len]
241
369
 
242
370
  if mem_size > len(cpu.memory):
243
371
  cpu.memory.resize(mem_size)
@@ -250,26 +378,49 @@ def _load_bin_v3(cpu: 'CPU', data: bytes) -> None:
250
378
  cpu.heap_ptr = heap_base
251
379
 
252
380
 
253
- def _load_bin_v2(cpu: 'CPU', data: bytes) -> None:
381
+ def _load_bin_v2(cpu: 'CPU', data: bytes, path: Optional[str] = None) -> None:
254
382
  """v2 遗留格式: 整块内存镜像。
255
383
 
256
384
  头部 34B: MAGIC 5 + ver 1 + mem_size u32 + entry u32 + sp u64 +
257
385
  bc_len u32 + 保留 8B, 之后是镜像与字节码。
386
+
387
+ v2 不记录 heap_base: 旧运行时固定按 ``heap_base = mem_size // 2`` 布局。
388
+ 因此只有当当前 ``--mem-size`` 与头部声明完全一致时才能复现旧布局;
389
+ 不一致时在加载阶段给出明确错误 (而不是让首条 PUSH 报假栈溢出)。
258
390
  """
259
391
  from .native import decode_program
260
- pos = 6
261
- mem_size = struct.unpack_from('<I', data, pos)[0]
262
- entry = struct.unpack_from('<I', data, pos + 4)[0]
263
- sp = struct.unpack_from('<Q', data, pos + 8)[0]
264
- bc_len = struct.unpack_from('<I', data, pos + 16)[0]
265
- image_off = pos + 28
266
- mem_image = data[image_off:image_off + mem_size]
267
- bytecode = data[image_off + mem_size:image_off + mem_size + bc_len]
268
-
269
- if len(mem_image) > len(cpu.memory):
270
- cpu.memory.resize(len(mem_image))
271
- cpu.config.mem_size = len(mem_image)
392
+ if len(data) < BIN_V2_HEADER:
393
+ raise CPUSimulatorError("BIN v2 头部不完整 (文件损坏?)")
394
+ mem_size = struct.unpack_from('<I', data, 6)[0]
395
+ entry = struct.unpack_from('<I', data, 10)[0]
396
+ sp = struct.unpack_from('<Q', data, 14)[0]
397
+ bc_len = struct.unpack_from('<I', data, 22)[0]
398
+ expected_heap = mem_size // 2
399
+
400
+ _validate_bin_stack_layout(2, mem_size, sp, expected_heap)
401
+
402
+ runtime_mem = len(cpu.memory)
403
+ if runtime_mem > mem_size:
404
+ # 不再静默地把旧 BIN 放进更大的 1 GiB 布局: heap_base 会从
405
+ # mem_size//2 抬到 runtime_mem//2, 与文件里的 SP 冲突。
406
+ raise _legacy_bin_layout_error(
407
+ path, mem_size, sp, expected_heap, runtime_mem, cpu.heap_ptr)
408
+ if runtime_mem < mem_size:
409
+ cpu.memory.resize(mem_size)
410
+ cpu.config.mem_size = mem_size
411
+
412
+ image_end = BIN_V2_HEADER + mem_size
413
+ if image_end + bc_len > len(data):
414
+ raise CPUSimulatorError(
415
+ f"BIN v2 数据不完整 (需要 {image_end + bc_len} bytes, "
416
+ f"实际 {len(data)} bytes)")
417
+ mem_image = data[BIN_V2_HEADER:image_end]
418
+ bytecode = data[image_end:image_end + bc_len]
419
+
272
420
  cpu.memory.load_bytes(0, mem_image)
273
421
  cpu.instructions, cpu.entry_pc = decode_program(bytecode)
274
422
  cpu.pc = entry
275
423
  cpu.sp = sp
424
+ # 与旧运行时一致: 堆基址由内存大小决定, v2 文件没有该字段。
425
+ cpu.heap_base = expected_heap
426
+ cpu.heap_ptr = expected_heap
@@ -4,4 +4,4 @@
4
4
  package engine
5
5
 
6
6
  // BuildVersion 是 Code CIN 的版本号 (被 codecin_version() 与日志使用)。
7
- const BuildVersion = "6.0.0"
7
+ const BuildVersion = "6.0.1"
@@ -355,6 +355,20 @@ func RunV2(opts *RunOptions) *Result {
355
355
  return r
356
356
  }
357
357
 
358
+ // 初始 SP 低于堆基址是"旧版 BIN 配新内存布局"的典型症状: v5.9.0 之前
359
+ // 默认 64 KiB, SP≈0xfff0; v5.9.0+ 默认 1 GiB, heap_base=0x20000000。
360
+ // 在进入指令循环前直接拒绝, 避免执行首条 PUSH 才报
361
+ // "Stack overflow (collides with heap)" 而被误认为真实栈溢出。
362
+ if vm.sp < vm.heapBase {
363
+ return finish(StatusError, fmt.Sprintf(
364
+ "invalid initial stack layout: SP 0x%x is below heap base 0x%x "+
365
+ "(memory %d bytes); this usually means a legacy BIN v2 compiled "+
366
+ "for the old 64 KB default was loaded with the current "+
367
+ "1 GiB layout; recompile the source or use --mem-size "+
368
+ "matching the bytecode's layout",
369
+ vm.sp, vm.heapBase, memSize))
370
+ }
371
+
358
372
  for {
359
373
  if opts.MaxSteps > 0 && int64(vm.steps) >= opts.MaxSteps {
360
374
  // 步数用尽不等于正常停机: 旧实现返回 StatusDone, 与 HALT 无法区分,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codecin
3
- Version: 6.0.0
3
+ Version: 6.0.1
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
@@ -311,7 +311,8 @@ FADD, FSUB, FMUL, FDIV, FCMP, FCVT, FABS, FNEG, LDRS, STRS, VADD, VSUB, VMUL, VD
311
311
 
312
312
  CROM v4 采用**段式内存镜像**: 只保存已分配的 4 KiB 页, 镜像体积与实际使用的内存成正比
313
313
  (默认 1 GiB 逻辑空间也只会写出真正写入过的页); 旧版 v3 整块镜像仍可读取 (仅兼容)。
314
- `.bin` 字节码格式同步升级为 **BIN v3** (同样段式, 旧 v2 仅兼容读取)。
314
+ `.bin` 字节码格式同步升级为 **BIN v3** (同样段式, 旧 v2 仅兼容读取; v2 不记录 `heap_base`,
315
+ 需用 `--mem-size` 指定与文件头一致的内存布局, 否则加载阶段会报错并提示处理方式)。
315
316
 
316
317
  ```mermaid
317
318
  block-beta
@@ -161,6 +161,7 @@ tests/test_aggregate_and_const.py
161
161
  tests/test_all_libs_together.py
162
162
  tests/test_aot.py
163
163
  tests/test_assembler_ext.py
164
+ tests/test_bin_compat.py
164
165
  tests/test_cin_host.py
165
166
  tests/test_cin_host_ext.py
166
167
  tests/test_cin_new_features.py
@@ -0,0 +1,195 @@
1
+ """旧版 BIN v2 与新版内存布局的兼容性回归测试。
2
+
3
+ 背景 (2026-10-18 issue): v5.9.0 之前默认 64 KiB 内存编译的 BIN v2 只固化
4
+ SP、不记录 heap_base。用 v6.0.0 默认 1 GiB 布局运行时, heap_base 变成
5
+ 0x20000000, 而 SP 仍是 0xfff0 左右, 首条 PUSH/ALLOCFRAME 就会报
6
+ "Stack overflow (collides with heap)" —— 看起来像真实栈溢出, 实际是
7
+ 加载时布局不兼容。现在必须在加载阶段报出可操作的错误。
8
+ """
9
+
10
+ import os
11
+ import struct
12
+
13
+ import pytest
14
+
15
+ from codecin import Config
16
+ from codecin.cpu import CPU
17
+ from codecin.disasm import disassemble_file
18
+ from codecin.errors import CPUSimulatorError
19
+ from codecin.isa import Constants
20
+ from codecin.native import encode_program, get_engine
21
+
22
+ needs_native = pytest.mark.skipif(get_engine() is None,
23
+ reason='native library not built')
24
+
25
+
26
+ def _write(workdir, name, blob):
27
+ path = os.path.join(workdir, name)
28
+ with open(path, 'wb') as f:
29
+ f.write(blob)
30
+ return path
31
+
32
+
33
+ def _v2_blob(mem_size=64 * 1024, sp=None, bytecode=None):
34
+ """构造一个完整可解析的 BIN v2 (整块 64 KiB 镜像 + 单条 HALT)。"""
35
+ if sp is None:
36
+ sp = (mem_size - Constants.STACK_SLOT) & ~0x7
37
+ if bytecode is None:
38
+ bytecode = encode_program([('HALT', [])], 0)
39
+ header = (Constants.MAGIC_NUMBER
40
+ + bytes([Constants.BIN_VERSION_V2])
41
+ + struct.pack('<I', mem_size)
42
+ + struct.pack('<I', 0) # entry
43
+ + struct.pack('<Q', sp)
44
+ + struct.pack('<I', len(bytecode))
45
+ + b'\x00' * 8)
46
+ return header + bytes(mem_size) + bytecode
47
+
48
+
49
+ def _v3_blob(mem_size=64 * 1024, sp=None, heap_base=None, segs=b'',
50
+ seg_count=0, bytecode=None):
51
+ if sp is None:
52
+ sp = (mem_size - Constants.STACK_SLOT) & ~0x7
53
+ if heap_base is None:
54
+ heap_base = mem_size // 2
55
+ if bytecode is None:
56
+ bytecode = encode_program([('HALT', [])], 0)
57
+ header = (Constants.MAGIC_NUMBER
58
+ + bytes([Constants.BIN_VERSION])
59
+ + struct.pack('<Q', mem_size)
60
+ + struct.pack('<I', 0) # entry
61
+ + struct.pack('<Q', sp)
62
+ + struct.pack('<Q', heap_base)
63
+ + struct.pack('<I', len(bytecode))
64
+ + struct.pack('<I', seg_count)
65
+ + b'\x00' * 8)
66
+ return header + segs + bytecode
67
+
68
+
69
+ # ==================== BIN v2: 加载阶段检测布局不兼容 ====================
70
+
71
+ def test_v2_layout_mismatch_fails_at_load_with_actionable_message(workdir):
72
+ path = _write(workdir, 'legacy.bin', _v2_blob())
73
+ with pytest.raises(CPUSimulatorError) as ei:
74
+ CPU(Config(log_level='ERROR'), path)
75
+ msg = str(ei.value)
76
+ # 指向真正原因, 而不是执行期容易误解的栈溢出
77
+ assert 'BIN v2' in msg
78
+ assert '65536' in msg
79
+ assert '0x20000000' in msg
80
+ assert '64 KB' in msg
81
+ assert '格式可读不等于内存布局兼容' in msg
82
+ assert '--mem-size 65536' in msg
83
+ assert '重新编译' in msg
84
+ assert 'Stack overflow (collides with heap)' in msg
85
+
86
+
87
+ def test_v2_layout_mismatch_is_not_a_runtime_stack_error(workdir, capsys):
88
+ """CLI 必须在 Load Error 阶段退出, 不能进入执行后才报栈溢出。"""
89
+ from codecin import cli
90
+ path = _write(workdir, 'legacy_cli.bin', _v2_blob())
91
+ code = cli.main([path, '--log-level', 'ERROR'])
92
+ assert code == 1
93
+ out = capsys.readouterr().out
94
+ assert 'Load Error' in out
95
+ assert '--mem-size 65536' in out
96
+ assert 'Execution Error' not in out
97
+ assert 'Total Instructions' not in out
98
+
99
+
100
+ def test_v2_with_matching_mem_size_uses_old_layout(workdir):
101
+ path = _write(workdir, 'legacy_ok.bin', _v2_blob())
102
+ cpu = CPU(Config(log_level='ERROR', mem_size=64 * 1024), path)
103
+ assert cpu.sp == (64 * 1024 - 8) & ~0x7
104
+ assert cpu.heap_base == 32 * 1024
105
+ assert cpu.heap_ptr == 32 * 1024
106
+ assert [op for op, _ in cpu.instructions] == ['HALT']
107
+
108
+
109
+ @needs_native
110
+ def test_v2_with_matching_mem_size_runs(workdir):
111
+ path = _write(workdir, 'legacy_run.bin', _v2_blob())
112
+ cpu = CPU(Config(log_level='ERROR', mem_size=64 * 1024), path)
113
+ cpu.run()
114
+ assert cpu.execution_failed is False
115
+ assert cpu.pc == 1
116
+
117
+
118
+ @needs_native
119
+ def test_cli_runs_v2_bin_with_matching_mem_size(workdir):
120
+ from codecin import cli
121
+ path = _write(workdir, 'legacy_cli_ok.bin', _v2_blob())
122
+ assert cli.main([path, '--mem-size', '65536', '--log-level', 'ERROR']) == 0
123
+
124
+
125
+ def test_v2_with_a_different_but_non_colliding_size_is_still_rejected(workdir):
126
+ """BIN v2 的 heap_base / SP 约定随 mem_size 改变, 不能静默改变布局。"""
127
+ path = _write(workdir, 'legacy_72k.bin', _v2_blob())
128
+ with pytest.raises(CPUSimulatorError) as ei:
129
+ CPU(Config(log_level='ERROR', mem_size=72 * 1024), path)
130
+ msg = str(ei.value)
131
+ assert '--mem-size 65536' in msg
132
+ assert '改变字节码编译时的栈/堆边界' in msg
133
+
134
+
135
+ def test_v2_truncated_payload_is_reported_as_corruption(workdir):
136
+ blob = _v2_blob()
137
+ path = _write(workdir, 'truncated.bin', blob[:-4])
138
+ with pytest.raises(CPUSimulatorError) as ei:
139
+ CPU(Config(log_level='ERROR', mem_size=64 * 1024), path)
140
+ assert '数据不完整' in str(ei.value)
141
+
142
+
143
+ # ==================== BIN v3: 头部元数据校验 ====================
144
+
145
+ def test_v3_invalid_heap_layout_fails_at_load(workdir):
146
+ path = _write(workdir, 'bad_v3.bin',
147
+ _v3_blob(sp=64 * 1024 - 16, heap_base=64 * 1024 - 8))
148
+ with pytest.raises(CPUSimulatorError) as ei:
149
+ CPU(Config(log_level='ERROR', mem_size=64 * 1024), path)
150
+ msg = str(ei.value)
151
+ assert 'BIN v3' in msg
152
+ assert 'heap_base' in msg
153
+ assert 'SP 低于 heap_base' in msg
154
+
155
+
156
+ def test_v3_roundtrip_still_loads(workdir):
157
+ path = _write(workdir, 'good_v3.bin', _v3_blob())
158
+ cpu = CPU(Config(log_level='ERROR'), path)
159
+ assert cpu.sp == (64 * 1024 - 8) & ~0x7
160
+ assert cpu.heap_base == 32 * 1024
161
+ assert [op for op, _ in cpu.instructions] == ['HALT']
162
+
163
+
164
+ def test_v3_tiny_memory_without_stack_use_still_loads(workdir):
165
+ """极小内存布局装不下 4 KiB 守卫线, 但 HALT 程序原本就能跑, 不应误拒。"""
166
+ path = _write(workdir, 'tiny_v3.bin', _v3_blob(mem_size=4096))
167
+ cpu = CPU(Config(log_level='ERROR', mem_size=4096), path)
168
+ assert cpu.sp == 4088
169
+ assert cpu.heap_base == 2048
170
+ assert [op for op, _ in cpu.instructions] == ['HALT']
171
+
172
+
173
+ # ==================== 反汇编: 只解码, 不执行, 不受布局校验影响 ====================
174
+
175
+ def test_disasm_still_reads_legacy_v2(workdir):
176
+ path = _write(workdir, 'disasm_v2.bin', _v2_blob())
177
+ lines = disassemble_file(path)
178
+ assert 'CPUSA binary' in lines[0]
179
+ assert any('HALT' in line for line in lines)
180
+
181
+
182
+ # ==================== 原生引擎: 直接调用时也不允许 SP < heap_base ====================
183
+
184
+ @needs_native
185
+ def test_engine_rejects_sp_below_heap_base_before_execution():
186
+ eng = get_engine()
187
+ bc = encode_program([('PUSH', [('reg', 29)])], 0)
188
+ res = eng.run_v2(bc, [], 0,
189
+ 0xfff8, # 旧 64 KiB 布局的 SP
190
+ 0x20000000, # 新 1 GiB 布局的 heap_base
191
+ 1 << 30, b'', 1000)
192
+ assert res is not None and res['error']
193
+ assert res['steps'] == 0
194
+ assert 'initial stack layout' in res['error']
195
+ assert '--mem-size' in res['error']
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes