codecin 5.4.2__py3-none-any.whl
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/__init__.py +27 -0
- codecin/aot.py +229 -0
- codecin/assembler.py +548 -0
- codecin/cache.py +71 -0
- codecin/cin.py +2558 -0
- codecin/cli.py +282 -0
- codecin/config.py +58 -0
- codecin/console.py +183 -0
- codecin/cpu.py +1654 -0
- codecin/crom.py +222 -0
- codecin/debugger.py +659 -0
- codecin/disasm.py +86 -0
- codecin/errors.py +58 -0
- codecin/isa.py +457 -0
- codecin/jit.py +270 -0
- codecin/libcodecin_native.so +0 -0
- codecin/logger.py +145 -0
- codecin/memory.py +348 -0
- codecin/native/aot/aot.go +70 -0
- codecin/native/aot/build.go +219 -0
- codecin/native/aot/stub_main.go.txt +22 -0
- codecin/native/build.ps1 +33 -0
- codecin/native/build.sh +48 -0
- codecin/native/cmd/codecin/main.go +287 -0
- codecin/native/compiler/codegen.go +1938 -0
- codecin/native/compiler/compile_test.go +164 -0
- codecin/native/compiler/parser.go +1071 -0
- codecin/native/compiler/syscalls.go +86 -0
- codecin/native/compiler/tokenizer.go +336 -0
- codecin/native/compiler/types.go +213 -0
- codecin/native/engine/audio.go +152 -0
- codecin/native/engine/audio_other.go +46 -0
- codecin/native/engine/audio_windows.go +43 -0
- codecin/native/engine/canvas.go +271 -0
- codecin/native/engine/crom.go +86 -0
- codecin/native/engine/encode.go +93 -0
- codecin/native/engine/engine_test.go +198 -0
- codecin/native/engine/isa_gen.go +445 -0
- codecin/native/engine/system.go +190 -0
- codecin/native/engine/termux.go +113 -0
- codecin/native/engine/version_gen.go +7 -0
- codecin/native/engine/vm.go +1157 -0
- codecin/native/go.mod +3 -0
- codecin/native/ir/ir.go +48 -0
- codecin/native/main.go +225 -0
- codecin/native.py +336 -0
- codecin/registers.py +130 -0
- codecin/stats.py +235 -0
- codecin-5.4.2.dist-info/METADATA +985 -0
- codecin-5.4.2.dist-info/RECORD +54 -0
- codecin-5.4.2.dist-info/WHEEL +5 -0
- codecin-5.4.2.dist-info/entry_points.txt +2 -0
- codecin-5.4.2.dist-info/licenses/LICENSE +7 -0
- codecin-5.4.2.dist-info/top_level.txt +1 -0
codecin/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
__version__ = "5.4.2"
|
|
2
|
+
__author__ = "ByUsi Studio"
|
|
3
|
+
|
|
4
|
+
from .config import Config
|
|
5
|
+
from .cpu import CPU
|
|
6
|
+
from .errors import (
|
|
7
|
+
AssemblerError,
|
|
8
|
+
CompilerError,
|
|
9
|
+
CPUSimulatorError,
|
|
10
|
+
ExecutionError,
|
|
11
|
+
MemoryAccessError,
|
|
12
|
+
)
|
|
13
|
+
from .isa import Constants, Opcode, Syscall
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"__version__",
|
|
17
|
+
"Opcode",
|
|
18
|
+
"Constants",
|
|
19
|
+
"Syscall",
|
|
20
|
+
"CPUSimulatorError",
|
|
21
|
+
"AssemblerError",
|
|
22
|
+
"CompilerError",
|
|
23
|
+
"ExecutionError",
|
|
24
|
+
"MemoryAccessError",
|
|
25
|
+
"Config",
|
|
26
|
+
"CPU",
|
|
27
|
+
]
|
codecin/aot.py
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""AOT 构建: 把 CIN 程序编译成**独立静态可执行文件** (Windows / Linux / macOS)。
|
|
2
|
+
|
|
3
|
+
产物特点:
|
|
4
|
+
* 内嵌 UCBC 字节码与初始内存镜像, 由内置 Go VM 执行;
|
|
5
|
+
* `CGO_ENABLED=0` 静态链接 —— 不依赖 Python、Go 工具链、libc 或任何动态库;
|
|
6
|
+
* 可用 `--target` 交叉编译 (windows/amd64、linux/arm64、darwin/arm64 ...)。
|
|
7
|
+
|
|
8
|
+
实现说明: 本模块只做"编排", 生成的 main.go 模板与 Go 侧
|
|
9
|
+
`codecin/native/aot` 共用同一份文件 (``stub_main.go.txt``), 避免两处漂移。
|
|
10
|
+
临时包建在 `codecin-native` 模块内的 ``.aotbuild-*`` 目录: Go 工具链会忽略
|
|
11
|
+
以 '.' 开头的目录, 因此既不影响 `go build ./...`, 也不需要 replace 指令。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import platform
|
|
16
|
+
import secrets
|
|
17
|
+
import shutil
|
|
18
|
+
import subprocess
|
|
19
|
+
from typing import List, Optional
|
|
20
|
+
|
|
21
|
+
#: 仓库内 codecin-native 模块目录 (含 go.mod)。
|
|
22
|
+
_NATIVE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'native')
|
|
23
|
+
|
|
24
|
+
#: 生成的 main.go 模板 (与 Go 侧共用)。
|
|
25
|
+
STUB_PATH = os.path.join(_NATIVE_DIR, 'aot', 'stub_main.go.txt')
|
|
26
|
+
|
|
27
|
+
#: 默认内存大小, 与解释器 / Go CLI 一致。
|
|
28
|
+
DEFAULT_MEM_SIZE = 65536
|
|
29
|
+
|
|
30
|
+
#: 常用交叉编译目标 (go 本身支持更多组合)。
|
|
31
|
+
SUPPORTED_TARGETS = [
|
|
32
|
+
'windows/amd64', 'windows/arm64',
|
|
33
|
+
'linux/amd64', 'linux/arm64',
|
|
34
|
+
'darwin/amd64', 'darwin/arm64',
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
#: go 的 GOOS 取值与 Python platform.system() 的对应
|
|
38
|
+
_GOOS_BY_SYSTEM = {'Windows': 'windows', 'Linux': 'linux', 'Darwin': 'darwin'}
|
|
39
|
+
_GOARCH_BY_MACHINE = {
|
|
40
|
+
'x86_64': 'amd64', 'AMD64': 'amd64', 'amd64': 'amd64',
|
|
41
|
+
'aarch64': 'arm64', 'arm64': 'arm64', 'ARM64': 'arm64',
|
|
42
|
+
'i386': '386', 'i686': '386',
|
|
43
|
+
'armv7l': 'arm', 'armv6l': 'arm',
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AotError(Exception):
|
|
48
|
+
"""AOT 构建失败。"""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def host_target() -> str:
|
|
52
|
+
"""返回当前平台的 ``os/arch``。"""
|
|
53
|
+
goos = _GOOS_BY_SYSTEM.get(platform.system(), platform.system().lower())
|
|
54
|
+
machine = platform.machine()
|
|
55
|
+
goarch = _GOARCH_BY_MACHINE.get(machine, machine.lower())
|
|
56
|
+
return f'{goos}/{goarch}'
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def parse_target(target: str) -> tuple:
|
|
60
|
+
"""把 ``os/arch`` 解析为 (goos, goarch); 非法时抛 AotError。"""
|
|
61
|
+
parts = target.split('/')
|
|
62
|
+
if len(parts) != 2 or not parts[0] or not parts[1]:
|
|
63
|
+
raise AotError(
|
|
64
|
+
f"--target 格式应为 os/arch (例如 linux/amd64); 支持: "
|
|
65
|
+
f"{', '.join(SUPPORTED_TARGETS)}")
|
|
66
|
+
return parts[0], parts[1]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def exe_suffix(goos: str) -> str:
|
|
70
|
+
"""目标平台的可执行文件后缀。"""
|
|
71
|
+
return '.exe' if goos == 'windows' else ''
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def module_dir() -> str:
|
|
75
|
+
"""返回 codecin-native 模块目录 (含 go.mod)。"""
|
|
76
|
+
if os.path.isfile(os.path.join(_NATIVE_DIR, 'go.mod')):
|
|
77
|
+
return _NATIVE_DIR
|
|
78
|
+
raise AotError(
|
|
79
|
+
f'找不到 Go 模块目录 {_NATIVE_DIR} (AOT 构建需要仓库中的 Go 源码)')
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def stub_source() -> str:
|
|
83
|
+
"""读取生成的 main.go 模板。"""
|
|
84
|
+
with open(STUB_PATH, encoding='utf-8') as f:
|
|
85
|
+
return f.read()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _cache_fallback_dir(mod_dir: str) -> str:
|
|
89
|
+
"""仓库内的构建缓存目录 (install 脚本同款; 已被 .gitignore 忽略)。"""
|
|
90
|
+
root = os.path.dirname(os.path.dirname(os.path.abspath(mod_dir)))
|
|
91
|
+
return os.path.join(root, '.gocache')
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _looks_like_cache_denied(text: str) -> bool:
|
|
95
|
+
low = text.lower()
|
|
96
|
+
if 'go-build' not in low and 'gocache' not in low and 'cache' not in low:
|
|
97
|
+
return False
|
|
98
|
+
return ('access is denied' in low or 'permission denied' in low
|
|
99
|
+
or 'operation not permitted' in low)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _run_go_build(cmd, mod, env, logger):
|
|
103
|
+
"""执行 go build; 默认构建缓存不可写时自动回退到仓库内缓存重试一次。
|
|
104
|
+
|
|
105
|
+
受限环境 (沙箱 / 只读 HOME / 部分 CI) 下 Go 默认的
|
|
106
|
+
``$LOCALAPPDATA/go-build`` 或 ``~/.cache/go-build`` 可能不可写,
|
|
107
|
+
此时直接用用户环境会得到一个难以理解的 "Access is denied"。
|
|
108
|
+
"""
|
|
109
|
+
proc = subprocess.run(cmd, cwd=mod, env=env, capture_output=True,
|
|
110
|
+
text=True, encoding='utf-8', errors='replace',
|
|
111
|
+
timeout=1800)
|
|
112
|
+
detail = (proc.stderr or proc.stdout or '').strip()
|
|
113
|
+
if proc.returncode != 0 and _looks_like_cache_denied(detail) \
|
|
114
|
+
and 'GOCACHE' not in os.environ:
|
|
115
|
+
cache = _cache_fallback_dir(mod)
|
|
116
|
+
os.makedirs(cache, exist_ok=True)
|
|
117
|
+
if logger:
|
|
118
|
+
logger.info(f'AOT: 默认 Go 构建缓存不可写, 回退到 {cache} 重试')
|
|
119
|
+
retry_env = dict(env)
|
|
120
|
+
retry_env['GOCACHE'] = cache
|
|
121
|
+
proc = subprocess.run(cmd, cwd=mod, env=retry_env, capture_output=True,
|
|
122
|
+
text=True, encoding='utf-8', errors='replace',
|
|
123
|
+
timeout=1800)
|
|
124
|
+
detail = (proc.stderr or proc.stdout or '').strip()
|
|
125
|
+
return proc, detail
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def build(bytecode: bytes,
|
|
129
|
+
mem_image: bytes,
|
|
130
|
+
out: str,
|
|
131
|
+
target: Optional[str] = None,
|
|
132
|
+
keep_temp: bool = False,
|
|
133
|
+
logger=None) -> str:
|
|
134
|
+
"""构建独立静态可执行文件, 返回产物的绝对路径。
|
|
135
|
+
|
|
136
|
+
:param bytecode: UCBC 字节码 (codecin.native.encode_program 的产物)
|
|
137
|
+
:param mem_image: 初始内存镜像 (数据段已写入)
|
|
138
|
+
:param out: 输出路径
|
|
139
|
+
:param target: ``os/arch``, 默认当前平台
|
|
140
|
+
:param keep_temp: 保留临时构建目录 (排查失败用)
|
|
141
|
+
:param logger: 可选 logger (记录 go build 输出)
|
|
142
|
+
"""
|
|
143
|
+
if not bytecode:
|
|
144
|
+
raise AotError('字节码为空')
|
|
145
|
+
goos, goarch = parse_target(target) if target else parse_target(host_target())
|
|
146
|
+
mod = module_dir()
|
|
147
|
+
|
|
148
|
+
# 注意: 这里用 os.makedirs 而不是 tempfile.mkdtemp —— mkdtemp 会创建 0700
|
|
149
|
+
# 目录, 在受限环境 (沙箱 / 部分 CI 安全策略) 下随后向其中写文件会被拒绝。
|
|
150
|
+
tmp = os.path.join(mod, '.aotbuild-' + secrets.token_hex(6))
|
|
151
|
+
os.makedirs(tmp, exist_ok=False)
|
|
152
|
+
try:
|
|
153
|
+
with open(os.path.join(tmp, 'main.go'), 'w', encoding='utf-8',
|
|
154
|
+
newline='\n') as f:
|
|
155
|
+
f.write(stub_source())
|
|
156
|
+
with open(os.path.join(tmp, 'program.ucbc'), 'wb') as f:
|
|
157
|
+
f.write(bytecode)
|
|
158
|
+
with open(os.path.join(tmp, 'program.mem'), 'wb') as f:
|
|
159
|
+
f.write(mem_image)
|
|
160
|
+
|
|
161
|
+
out_abs = os.path.abspath(out)
|
|
162
|
+
os.makedirs(os.path.dirname(out_abs) or '.', exist_ok=True)
|
|
163
|
+
|
|
164
|
+
env = dict(os.environ)
|
|
165
|
+
env['CGO_ENABLED'] = '0' # 关键: 静态链接
|
|
166
|
+
env['GOOS'] = goos
|
|
167
|
+
env['GOARCH'] = goarch
|
|
168
|
+
# -trimpath 去掉本机路径; -s -w 去掉符号表
|
|
169
|
+
cmd = ['go', 'build', '-trimpath',
|
|
170
|
+
'-tags', 'netgo,osusergo',
|
|
171
|
+
'-ldflags', '-s -w',
|
|
172
|
+
'-o', out_abs,
|
|
173
|
+
'.' + os.sep + os.path.basename(tmp)]
|
|
174
|
+
if logger:
|
|
175
|
+
logger.debug(f'AOT build: {cmd} (cwd={mod}, GOOS={goos}, '
|
|
176
|
+
f'GOARCH={goarch}, CGO_ENABLED=0)')
|
|
177
|
+
try:
|
|
178
|
+
proc, detail = _run_go_build(cmd, mod, env, logger)
|
|
179
|
+
except FileNotFoundError as e:
|
|
180
|
+
raise AotError('未找到 go 命令; AOT 构建需要 Go 工具链 '
|
|
181
|
+
'(https://go.dev/dl/)') from e
|
|
182
|
+
except subprocess.TimeoutExpired as e:
|
|
183
|
+
raise AotError('go build 超时') from e
|
|
184
|
+
if proc.returncode != 0:
|
|
185
|
+
if _looks_like_cache_denied(detail) and 'GOCACHE' not in os.environ:
|
|
186
|
+
detail += ('\n提示: 默认 Go 构建缓存不可写且回退失败; '
|
|
187
|
+
'可显式设置 GOCACHE 指向可写目录后重试')
|
|
188
|
+
raise AotError(f'go build 失败 (目标 {goos}/{goarch}): {detail}')
|
|
189
|
+
if not os.path.isfile(out_abs):
|
|
190
|
+
raise AotError(f'go build 未产出文件: {out_abs}')
|
|
191
|
+
return out_abs
|
|
192
|
+
finally:
|
|
193
|
+
if keep_temp:
|
|
194
|
+
if logger:
|
|
195
|
+
logger.info(f'AOT 临时目录保留: {tmp}')
|
|
196
|
+
else:
|
|
197
|
+
shutil.rmtree(tmp, ignore_errors=True)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def build_program(program_file: str,
|
|
201
|
+
out: Optional[str] = None,
|
|
202
|
+
target: Optional[str] = None,
|
|
203
|
+
keep_temp: bool = False,
|
|
204
|
+
logger=None) -> str:
|
|
205
|
+
"""便捷入口: 直接由 .cin 源文件构建可执行文件。"""
|
|
206
|
+
from .cin import CINCompiler
|
|
207
|
+
from .native import encode_program
|
|
208
|
+
|
|
209
|
+
goos, _goarch = parse_target(target) if target else \
|
|
210
|
+
parse_target(host_target())
|
|
211
|
+
res = CINCompiler().compile(program_file)
|
|
212
|
+
labels = dict(res.labels)
|
|
213
|
+
labels.update(res.data_labels)
|
|
214
|
+
bytecode = encode_program(res.instructions,
|
|
215
|
+
getattr(res, 'entry_pc', 0) or 0, labels)
|
|
216
|
+
mem = bytearray(DEFAULT_MEM_SIZE)
|
|
217
|
+
for addr, data in res.data_writes:
|
|
218
|
+
end = addr + len(data)
|
|
219
|
+
if addr >= 0 and end <= len(mem):
|
|
220
|
+
mem[addr:end] = data
|
|
221
|
+
if not out:
|
|
222
|
+
out = os.path.splitext(program_file)[0] + exe_suffix(goos)
|
|
223
|
+
return build(bytes(bytecode), bytes(mem), out, target=target,
|
|
224
|
+
keep_temp=keep_temp, logger=logger)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def supported_targets() -> List[str]:
|
|
228
|
+
"""文档/帮助用: 常用目标列表。"""
|
|
229
|
+
return list(SUPPORTED_TARGETS)
|