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/native/go.mod
ADDED
codecin/native/ir/ir.go
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Package ir 定义 Code CIN 字节码中间表示 (IR)。
|
|
2
|
+
//
|
|
3
|
+
// 该 IR 与 Python codecin 侧 CINCompiler 产出的结构完全等价:
|
|
4
|
+
//
|
|
5
|
+
// 指令 = (opcode 名, 操作数列表); 操作数用 Kind 区分 (reg/imm/mem/label/cond/float/str)。
|
|
6
|
+
//
|
|
7
|
+
// 编译器 (compiler 包) 产出 *Program, engine 包将其编码为 UCBC 字节码后交由 VM 执行。
|
|
8
|
+
package ir
|
|
9
|
+
|
|
10
|
+
// Operand 表示一条指令的操作数。
|
|
11
|
+
type Operand struct {
|
|
12
|
+
Kind string // reg / imm / mem / label / cond / float / str / vec / veclane
|
|
13
|
+
V int64 // reg 号 / imm 值 / mem base(-1=绝对地址) / str 地址 / vec 号
|
|
14
|
+
Off int64 // mem 偏移 / veclane
|
|
15
|
+
Name string // label 名 / cond 名
|
|
16
|
+
F float64 // float 值
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Instr 是一条 IR 指令。
|
|
20
|
+
type Instr struct {
|
|
21
|
+
Op string
|
|
22
|
+
Args []Operand
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// DataWrite 是一次数据段写入 (地址 + 字节)。
|
|
26
|
+
type DataWrite struct {
|
|
27
|
+
Addr int
|
|
28
|
+
Data []byte
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Program 是编译产物: 指令流 + 标签 + 数据段写入。
|
|
32
|
+
type Program struct {
|
|
33
|
+
Instructions []Instr
|
|
34
|
+
Labels map[string]int
|
|
35
|
+
DataLabels map[string]int
|
|
36
|
+
DataWrites []DataWrite
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 操作数构造辅助 (与编译器共享书写习惯)。
|
|
40
|
+
func Reg(n int) Operand { return Operand{Kind: "reg", V: int64(n)} }
|
|
41
|
+
func Imm(v int64) Operand { return Operand{Kind: "imm", V: v} }
|
|
42
|
+
func Mem(base, off int) Operand {
|
|
43
|
+
return Operand{Kind: "mem", V: int64(base), Off: int64(off)}
|
|
44
|
+
}
|
|
45
|
+
func Label(name string) Operand { return Operand{Kind: "label", Name: name} }
|
|
46
|
+
func Cond(name string) Operand { return Operand{Kind: "cond", Name: name} }
|
|
47
|
+
func Float(f float64) Operand { return Operand{Kind: "float", F: f} }
|
|
48
|
+
func Str(addr int) Operand { return Operand{Kind: "str", V: int64(addr)} }
|
codecin/native/main.go
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
#include <stdlib.h>
|
|
5
|
+
*/
|
|
6
|
+
import "C"
|
|
7
|
+
|
|
8
|
+
import (
|
|
9
|
+
"encoding/binary"
|
|
10
|
+
"fmt"
|
|
11
|
+
"sync"
|
|
12
|
+
"unsafe"
|
|
13
|
+
|
|
14
|
+
"codecin-native/engine"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
// 结果结构布局 (与 codecin/native.py _parse_result 严格一致):
|
|
18
|
+
// status u8 + 3B pad
|
|
19
|
+
// pc u64 sp u64 heap_ptr u64 steps u64
|
|
20
|
+
// regs 33×u64
|
|
21
|
+
// vec 32×4×f64
|
|
22
|
+
// mem_len u64 | mem ...
|
|
23
|
+
// out_len u64 | out ...
|
|
24
|
+
// err_len u16 | err ...
|
|
25
|
+
|
|
26
|
+
// buildVersion 由构建时注入: -ldflags "-X main.buildVersion=<版本>"
|
|
27
|
+
// (install.sh / install.ps1 / build.ps1 会传入 codecin 包版本)。
|
|
28
|
+
var buildVersion = "dev"
|
|
29
|
+
|
|
30
|
+
// marshalResult 把执行结果序列化为 ABI 缓冲。
|
|
31
|
+
// mem 为执行后的内存镜像; panic 降级路径传 nil (调用方按 mem_len=0 处理)。
|
|
32
|
+
func marshalResult(res *engine.Result, mem []byte,
|
|
33
|
+
sp0, heap0 int64) unsafe.Pointer {
|
|
34
|
+
|
|
35
|
+
const regBytes = 33 * 8
|
|
36
|
+
const vecBytes = 32 * 4 * 8
|
|
37
|
+
|
|
38
|
+
status := engine.StatusError
|
|
39
|
+
pc := 0
|
|
40
|
+
sp := uint64(sp0)
|
|
41
|
+
heap := uint64(heap0)
|
|
42
|
+
var steps uint64
|
|
43
|
+
var regs [33]uint64
|
|
44
|
+
out := []byte{}
|
|
45
|
+
errb := []byte{}
|
|
46
|
+
if res != nil {
|
|
47
|
+
status = res.Status
|
|
48
|
+
pc = res.Pc
|
|
49
|
+
sp = res.Sp
|
|
50
|
+
heap = res.HeapPtr
|
|
51
|
+
steps = res.Steps
|
|
52
|
+
regs = res.Regs
|
|
53
|
+
out = []byte(res.Output)
|
|
54
|
+
errb = []byte(res.ErrMsg)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
total := 36 + regBytes + vecBytes + 8 + len(mem) + 8 + len(out) + 2 + len(errb)
|
|
58
|
+
buf := make([]byte, total)
|
|
59
|
+
|
|
60
|
+
buf[0] = byte(status)
|
|
61
|
+
pos := 4
|
|
62
|
+
binary.LittleEndian.PutUint64(buf[pos:], uint64(pc))
|
|
63
|
+
binary.LittleEndian.PutUint64(buf[pos+8:], sp)
|
|
64
|
+
binary.LittleEndian.PutUint64(buf[pos+16:], heap)
|
|
65
|
+
binary.LittleEndian.PutUint64(buf[pos+24:], steps)
|
|
66
|
+
pos = 36 // 状态头 36 字节: status+pad(4) + 4×u64(32)
|
|
67
|
+
for i := 0; i < 33; i++ {
|
|
68
|
+
binary.LittleEndian.PutUint64(buf[pos+i*8:], regs[i])
|
|
69
|
+
}
|
|
70
|
+
pos += regBytes
|
|
71
|
+
pos += vecBytes // 向量区保持 0
|
|
72
|
+
binary.LittleEndian.PutUint64(buf[pos:], uint64(len(mem)))
|
|
73
|
+
pos += 8
|
|
74
|
+
copy(buf[pos:], mem)
|
|
75
|
+
pos += len(mem)
|
|
76
|
+
binary.LittleEndian.PutUint64(buf[pos:], uint64(len(out)))
|
|
77
|
+
pos += 8
|
|
78
|
+
copy(buf[pos:], out)
|
|
79
|
+
pos += len(out)
|
|
80
|
+
binary.LittleEndian.PutUint16(buf[pos:], uint16(len(errb)))
|
|
81
|
+
pos += 2
|
|
82
|
+
copy(buf[pos:], errb)
|
|
83
|
+
|
|
84
|
+
cbuf := C.malloc(C.size_t(total))
|
|
85
|
+
if cbuf == nil {
|
|
86
|
+
return nil
|
|
87
|
+
}
|
|
88
|
+
copy(unsafe.Slice((*byte)(cbuf), total), buf)
|
|
89
|
+
return cbuf
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// errResult 是 panic / 参数非法时的降级结果 (仍返回合法缓冲, 供 Python 解析)。
|
|
93
|
+
func errResult(msg string, sp0, heap0 int64) unsafe.Pointer {
|
|
94
|
+
return marshalResult(&engine.Result{
|
|
95
|
+
Status: engine.StatusError,
|
|
96
|
+
ErrMsg: msg,
|
|
97
|
+
Sp: uint64(sp0),
|
|
98
|
+
HeapPtr: uint64(heap0),
|
|
99
|
+
}, nil, sp0, heap0)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
//export codecin_run
|
|
103
|
+
func codecin_run(bcPtr unsafe.Pointer, bcLen C.int,
|
|
104
|
+
memPtr unsafe.Pointer, memLen C.int,
|
|
105
|
+
entry C.longlong, sp C.longlong, heapBase C.longlong,
|
|
106
|
+
inPtr unsafe.Pointer, inLen C.int,
|
|
107
|
+
maxSteps C.longlong) (ret unsafe.Pointer) {
|
|
108
|
+
|
|
109
|
+
// panic 不得跨越 CGO 边界: Go 运行时会把未捕获 panic 当作致命错误直接
|
|
110
|
+
// 终止宿主进程 (Python 无法拦截)。这里降级为"一次调用返回错误结果"。
|
|
111
|
+
defer func() {
|
|
112
|
+
if r := recover(); r != nil {
|
|
113
|
+
ret = errResult(fmt.Sprintf("native panic: %v", r),
|
|
114
|
+
int64(sp), int64(heapBase))
|
|
115
|
+
}
|
|
116
|
+
}()
|
|
117
|
+
|
|
118
|
+
if bcLen < 0 || memLen < 0 || inLen < 0 {
|
|
119
|
+
return errResult("native: negative buffer length",
|
|
120
|
+
int64(sp), int64(heapBase))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
var bc, mem []byte
|
|
124
|
+
if bcPtr != nil && bcLen > 0 {
|
|
125
|
+
bc = C.GoBytes(bcPtr, bcLen)
|
|
126
|
+
}
|
|
127
|
+
if memPtr != nil && memLen > 0 {
|
|
128
|
+
mem = C.GoBytes(memPtr, memLen)
|
|
129
|
+
}
|
|
130
|
+
in := []byte{}
|
|
131
|
+
if inPtr != nil && inLen > 0 {
|
|
132
|
+
in = C.GoBytes(inPtr, inLen)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
res := engine.Run(bc, mem, int64(entry), int64(sp), int64(heapBase),
|
|
136
|
+
in, int64(maxSteps))
|
|
137
|
+
return marshalResult(res, mem, int64(sp), int64(heapBase))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
//export codecin_free
|
|
141
|
+
func codecin_free(ptr unsafe.Pointer) {
|
|
142
|
+
if ptr != nil {
|
|
143
|
+
C.free(ptr)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//export codecin_crom_pack
|
|
148
|
+
func codecin_crom_pack(dataPtr unsafe.Pointer, dataLen C.int, compress C.int,
|
|
149
|
+
outLen *C.int) (ret unsafe.Pointer) {
|
|
150
|
+
defer func() {
|
|
151
|
+
if r := recover(); r != nil {
|
|
152
|
+
ret = nil
|
|
153
|
+
}
|
|
154
|
+
}()
|
|
155
|
+
if outLen == nil || dataLen < 0 || dataPtr == nil {
|
|
156
|
+
return nil
|
|
157
|
+
}
|
|
158
|
+
data := C.GoBytes(dataPtr, dataLen)
|
|
159
|
+
packed := engine.CromPack(data, compress != 0)
|
|
160
|
+
if packed == nil {
|
|
161
|
+
return nil
|
|
162
|
+
}
|
|
163
|
+
cbuf := C.malloc(C.size_t(len(packed)))
|
|
164
|
+
if cbuf == nil {
|
|
165
|
+
return nil
|
|
166
|
+
}
|
|
167
|
+
copy(unsafe.Slice((*byte)(cbuf), len(packed)), packed)
|
|
168
|
+
*outLen = C.int(len(packed))
|
|
169
|
+
return cbuf
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//export codecin_crom_unpack
|
|
173
|
+
func codecin_crom_unpack(dataPtr unsafe.Pointer, dataLen C.int,
|
|
174
|
+
memLen *C.int, flags *C.int) (ret unsafe.Pointer) {
|
|
175
|
+
defer func() {
|
|
176
|
+
if r := recover(); r != nil {
|
|
177
|
+
ret = nil
|
|
178
|
+
}
|
|
179
|
+
}()
|
|
180
|
+
if memLen == nil || flags == nil || dataLen < 0 || dataPtr == nil {
|
|
181
|
+
return nil
|
|
182
|
+
}
|
|
183
|
+
data := C.GoBytes(dataPtr, dataLen)
|
|
184
|
+
raw, flg, ok := engine.CromUnpack(data)
|
|
185
|
+
if !ok {
|
|
186
|
+
return nil
|
|
187
|
+
}
|
|
188
|
+
cbuf := C.malloc(C.size_t(len(raw)))
|
|
189
|
+
if cbuf == nil {
|
|
190
|
+
return nil
|
|
191
|
+
}
|
|
192
|
+
copy(unsafe.Slice((*byte)(cbuf), len(raw)), raw)
|
|
193
|
+
*memLen = C.int(len(raw))
|
|
194
|
+
*flags = C.int(flg)
|
|
195
|
+
return cbuf
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// buildVersion 为空时使用生成器注入的 engine.BuildVersion
|
|
199
|
+
// (单一事实来源: codecin/__init__.py 的 __version__)。
|
|
200
|
+
func versionString() string {
|
|
201
|
+
v := buildVersion
|
|
202
|
+
if v == "" || v == "dev" {
|
|
203
|
+
v = engine.BuildVersion
|
|
204
|
+
}
|
|
205
|
+
return "codecin-native " + v + " (Go)"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
var (
|
|
209
|
+
versionOnce sync.Once
|
|
210
|
+
versionCStr *C.char
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
// codecin_version 返回静态版本字符串。
|
|
214
|
+
// 旧实现每次调用都 C.CString, 而 Python 侧以 c_char_p 取走后永不 free → 每次调用泄漏。
|
|
215
|
+
// 返回的指针为进程级常量, 调用方不要 free。
|
|
216
|
+
//
|
|
217
|
+
//export codecin_version
|
|
218
|
+
func codecin_version() *C.char {
|
|
219
|
+
versionOnce.Do(func() {
|
|
220
|
+
versionCStr = C.CString(versionString())
|
|
221
|
+
})
|
|
222
|
+
return versionCStr
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
func main() {}
|
codecin/native.py
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
"""Go 原生库桥接 (c-shared)。
|
|
2
|
+
|
|
3
|
+
通过 ctypes 加载 Go 编译的共享库 (Windows: codecin_native.dll,
|
|
4
|
+
Linux/Termux: libcodecin_native.so, macOS: libcodecin_native.dylib),
|
|
5
|
+
提供:
|
|
6
|
+
1. 原生字节码 VM (codecin_run) -- 整程序高速执行
|
|
7
|
+
2. CROM 压缩/解压 (codecin_crom_pack / codecin_crom_unpack)
|
|
8
|
+
|
|
9
|
+
库不存在或加载失败时所有接口返回 None, 调用方自动回退纯 Python。
|
|
10
|
+
|
|
11
|
+
字节码格式 (UCBC):
|
|
12
|
+
头部: magic[4]='UCBC' version u8 entry u32 instr_count u32
|
|
13
|
+
指令: opcode u8 argc u8
|
|
14
|
+
操作数: kind u8 value i64 extra i64 (小端)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import ctypes
|
|
18
|
+
import os
|
|
19
|
+
import platform
|
|
20
|
+
import struct
|
|
21
|
+
import sys
|
|
22
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
23
|
+
|
|
24
|
+
from .isa import (
|
|
25
|
+
BC_MAGIC,
|
|
26
|
+
BC_VERSION,
|
|
27
|
+
KIND_COND,
|
|
28
|
+
KIND_FLOAT,
|
|
29
|
+
KIND_IMM,
|
|
30
|
+
KIND_MEM,
|
|
31
|
+
KIND_REG,
|
|
32
|
+
KIND_STR,
|
|
33
|
+
KIND_VEC,
|
|
34
|
+
KIND_VECLANE,
|
|
35
|
+
Cond,
|
|
36
|
+
Constants,
|
|
37
|
+
Opcode,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
Operand = Tuple[Any, ...]
|
|
41
|
+
Instruction = Tuple[str, List[Operand]]
|
|
42
|
+
|
|
43
|
+
# ==================== 字节码编解码 ====================
|
|
44
|
+
|
|
45
|
+
_KIND_MAP = {
|
|
46
|
+
'reg': KIND_REG, 'imm': KIND_IMM, 'vec': KIND_VEC,
|
|
47
|
+
'veclane': KIND_VECLANE, 'mem': KIND_MEM, 'cond': KIND_COND,
|
|
48
|
+
'float': KIND_FLOAT, 'str': KIND_STR,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def encode_operand(op: Operand) -> Tuple[int, int, int]:
|
|
53
|
+
kind = _KIND_MAP.get(op[0])
|
|
54
|
+
if kind is None:
|
|
55
|
+
if op[0] == 'label':
|
|
56
|
+
return KIND_IMM, 0, 0 # 未解析标签 (不应出现)
|
|
57
|
+
raise ValueError(f"Cannot encode operand: {op}")
|
|
58
|
+
if kind == KIND_REG:
|
|
59
|
+
return kind, int(op[1]), 0
|
|
60
|
+
if kind == KIND_IMM:
|
|
61
|
+
return kind, int(op[1]) & 0xFFFFFFFFFFFFFFFF, 0
|
|
62
|
+
if kind == KIND_VEC:
|
|
63
|
+
return kind, int(op[1]), 0
|
|
64
|
+
if kind == KIND_VECLANE:
|
|
65
|
+
return kind, int(op[1]), int(op[2])
|
|
66
|
+
if kind == KIND_MEM:
|
|
67
|
+
base = op[1] if len(op) > 1 else -1
|
|
68
|
+
off = op[2] if len(op) > 2 else 0
|
|
69
|
+
return kind, int(base), int(off) & 0xFFFFFFFFFFFFFFFF
|
|
70
|
+
if kind == KIND_COND:
|
|
71
|
+
return kind, Cond.code(op[1]), 0
|
|
72
|
+
if kind == KIND_FLOAT:
|
|
73
|
+
return kind, struct.unpack('<Q', struct.pack('<d', float(op[1])))[0], 0
|
|
74
|
+
if kind == KIND_STR:
|
|
75
|
+
return kind, int(op[1]), 0
|
|
76
|
+
raise ValueError(f"Cannot encode operand: {op}")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _i64(v: int) -> int:
|
|
80
|
+
v &= 0xFFFFFFFFFFFFFFFF
|
|
81
|
+
return v if v < (1 << 63) else v - (1 << 64)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def decode_operand(kind: int, value: int, extra: int) -> Operand:
|
|
85
|
+
if kind == KIND_REG:
|
|
86
|
+
return ('reg', value)
|
|
87
|
+
if kind == KIND_IMM:
|
|
88
|
+
return ('imm', value)
|
|
89
|
+
if kind == KIND_VEC:
|
|
90
|
+
return ('vec', value)
|
|
91
|
+
if kind == KIND_VECLANE:
|
|
92
|
+
return ('veclane', value, extra)
|
|
93
|
+
if kind == KIND_MEM:
|
|
94
|
+
return ('mem', value, extra)
|
|
95
|
+
if kind == KIND_COND:
|
|
96
|
+
return ('cond', Cond.NAMES[value & 15])
|
|
97
|
+
if kind == KIND_FLOAT:
|
|
98
|
+
return ('float', struct.unpack('<d', struct.pack('<q', _i64(value)))[0])
|
|
99
|
+
if kind == KIND_STR:
|
|
100
|
+
return ('str', value)
|
|
101
|
+
raise ValueError(f"Unknown operand kind: {kind}")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def encode_program(instructions: List[Instruction], entry: int = 0,
|
|
105
|
+
labels: Optional[Dict[str, int]] = None) -> bytes:
|
|
106
|
+
"""编码程序为字节码。labels 用于将 ('label', name) 操作数解析为立即数。"""
|
|
107
|
+
out = bytearray()
|
|
108
|
+
out += BC_MAGIC
|
|
109
|
+
out += struct.pack('<B', BC_VERSION)
|
|
110
|
+
out += struct.pack('<I', entry & 0xFFFFFFFF)
|
|
111
|
+
out += struct.pack('<I', len(instructions) & 0xFFFFFFFF)
|
|
112
|
+
labels = labels or {}
|
|
113
|
+
for opcode, args in instructions:
|
|
114
|
+
op_enum = Constants.OPCODE_NAME_TO_ENUM.get(opcode)
|
|
115
|
+
if op_enum is None:
|
|
116
|
+
raise ValueError(f"Unknown opcode: {opcode}")
|
|
117
|
+
out += struct.pack('<BB', op_enum.value, len(args))
|
|
118
|
+
for arg in args:
|
|
119
|
+
if arg[0] == 'label':
|
|
120
|
+
if arg[1] not in labels:
|
|
121
|
+
raise ValueError(f"Undefined label: {arg[1]}")
|
|
122
|
+
arg = ('imm', labels[arg[1]])
|
|
123
|
+
kind, value, extra = encode_operand(arg)
|
|
124
|
+
out += struct.pack('<Bqq', kind, _i64(value), _i64(extra))
|
|
125
|
+
return bytes(out)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def decode_program(data: bytes) -> Tuple[List[Instruction], int]:
|
|
129
|
+
if data[:4] != BC_MAGIC:
|
|
130
|
+
raise ValueError("Bad bytecode magic")
|
|
131
|
+
version = data[4]
|
|
132
|
+
if version != BC_VERSION:
|
|
133
|
+
raise ValueError(f"Unsupported bytecode version: {version}")
|
|
134
|
+
entry = struct.unpack('<I', data[5:9])[0]
|
|
135
|
+
count = struct.unpack('<I', data[9:13])[0]
|
|
136
|
+
pos = 13
|
|
137
|
+
instructions: List[Instruction] = []
|
|
138
|
+
for _ in range(count):
|
|
139
|
+
op_val, argc = struct.unpack('<BB', data[pos:pos + 2])
|
|
140
|
+
pos += 2
|
|
141
|
+
opcode = Opcode(op_val).name
|
|
142
|
+
args: List[Operand] = []
|
|
143
|
+
for _ in range(argc):
|
|
144
|
+
kind, value, extra = struct.unpack('<Bqq', data[pos:pos + 17])
|
|
145
|
+
pos += 17
|
|
146
|
+
args.append(decode_operand(kind, value, extra))
|
|
147
|
+
instructions.append((opcode, args))
|
|
148
|
+
return instructions, entry
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ==================== 原生库加载 ====================
|
|
152
|
+
|
|
153
|
+
_LIB_CACHE: Optional['NativeEngine'] = False # type: ignore
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _lib_candidates() -> List[str]:
|
|
157
|
+
here = os.path.dirname(os.path.abspath(__file__))
|
|
158
|
+
system = platform.system()
|
|
159
|
+
if system == 'Windows':
|
|
160
|
+
names = ['codecin_native.dll']
|
|
161
|
+
elif system == 'Darwin':
|
|
162
|
+
names = ['libcodecin_native.dylib', 'codecin_native.dylib']
|
|
163
|
+
else:
|
|
164
|
+
names = ['libcodecin_native.so', 'codecin_native.so']
|
|
165
|
+
candidates = [os.path.join(here, 'native', n) for n in names]
|
|
166
|
+
candidates += [os.path.join(here, n) for n in names]
|
|
167
|
+
env = os.environ.get('CODECIN_NATIVE_LIB')
|
|
168
|
+
if env:
|
|
169
|
+
candidates.insert(0, env)
|
|
170
|
+
if getattr(sys, 'frozen', False):
|
|
171
|
+
# PyInstaller 打包布局: 二进制位于 exe 目录或 _internal 下
|
|
172
|
+
exe_dir = os.path.dirname(os.path.abspath(sys.executable))
|
|
173
|
+
meipass = getattr(sys, '_MEIPASS', exe_dir)
|
|
174
|
+
candidates += [os.path.join(meipass, n) for n in names]
|
|
175
|
+
candidates += [os.path.join(exe_dir, n) for n in names]
|
|
176
|
+
return candidates
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class NativeEngine:
|
|
180
|
+
def __init__(self, lib: ctypes.CDLL):
|
|
181
|
+
self.lib = lib
|
|
182
|
+
self._configure()
|
|
183
|
+
|
|
184
|
+
def _configure(self) -> None:
|
|
185
|
+
lib = self.lib
|
|
186
|
+
lib.codecin_run.argtypes = [
|
|
187
|
+
ctypes.c_void_p, ctypes.c_int, # bytecode, len
|
|
188
|
+
ctypes.c_void_p, ctypes.c_int, # mem, len
|
|
189
|
+
ctypes.c_longlong, ctypes.c_longlong, ctypes.c_longlong, # entry, sp, heap
|
|
190
|
+
ctypes.c_void_p, ctypes.c_int, # input, len
|
|
191
|
+
ctypes.c_longlong, # max steps
|
|
192
|
+
]
|
|
193
|
+
lib.codecin_run.restype = ctypes.c_void_p
|
|
194
|
+
lib.codecin_free.argtypes = [ctypes.c_void_p]
|
|
195
|
+
lib.codecin_free.restype = None
|
|
196
|
+
lib.codecin_crom_pack.argtypes = [
|
|
197
|
+
ctypes.c_void_p, ctypes.c_int, ctypes.c_int,
|
|
198
|
+
ctypes.POINTER(ctypes.c_int),
|
|
199
|
+
]
|
|
200
|
+
lib.codecin_crom_pack.restype = ctypes.c_void_p
|
|
201
|
+
lib.codecin_crom_unpack.argtypes = [
|
|
202
|
+
ctypes.c_void_p, ctypes.c_int,
|
|
203
|
+
ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int),
|
|
204
|
+
]
|
|
205
|
+
lib.codecin_crom_unpack.restype = ctypes.c_void_p
|
|
206
|
+
lib.codecin_version.argtypes = []
|
|
207
|
+
lib.codecin_version.restype = ctypes.c_char_p
|
|
208
|
+
|
|
209
|
+
def version(self) -> str:
|
|
210
|
+
try:
|
|
211
|
+
return self.lib.codecin_version().decode()
|
|
212
|
+
except Exception:
|
|
213
|
+
return "unknown"
|
|
214
|
+
|
|
215
|
+
# ---------------- 原生 VM ----------------
|
|
216
|
+
|
|
217
|
+
def run(self, bytecode: bytes, mem: bytes, entry: int, sp: int,
|
|
218
|
+
heap_base: int, input_data: bytes, max_steps: int) -> Optional[Dict[str, Any]]:
|
|
219
|
+
bc_buf = ctypes.create_string_buffer(bytecode)
|
|
220
|
+
mem_buf = ctypes.create_string_buffer(bytes(mem), len(mem))
|
|
221
|
+
in_buf = ctypes.create_string_buffer(input_data) if input_data else None
|
|
222
|
+
|
|
223
|
+
ptr = self.lib.codecin_run(
|
|
224
|
+
ctypes.cast(bc_buf, ctypes.c_void_p), len(bytecode),
|
|
225
|
+
ctypes.cast(mem_buf, ctypes.c_void_p), len(mem),
|
|
226
|
+
entry, sp, heap_base,
|
|
227
|
+
ctypes.cast(in_buf, ctypes.c_void_p) if in_buf else None,
|
|
228
|
+
len(input_data),
|
|
229
|
+
max_steps,
|
|
230
|
+
)
|
|
231
|
+
if not ptr:
|
|
232
|
+
return None
|
|
233
|
+
try:
|
|
234
|
+
return self._parse_result(ptr)
|
|
235
|
+
finally:
|
|
236
|
+
self.lib.codecin_free(ptr)
|
|
237
|
+
|
|
238
|
+
def _parse_result(self, ptr: int) -> Dict[str, Any]:
|
|
239
|
+
base = ptr
|
|
240
|
+
view = ctypes.string_at(base, 64)
|
|
241
|
+
status = view[0]
|
|
242
|
+
pc, sp, heap_ptr, steps = struct.unpack_from('<QQQQ', view, 4)
|
|
243
|
+
off = 4 + 32
|
|
244
|
+
regs_raw = ctypes.string_at(base + off, 33 * 8)
|
|
245
|
+
off += 33 * 8
|
|
246
|
+
vec_raw = ctypes.string_at(base + off, 32 * 4 * 8)
|
|
247
|
+
off += 32 * 4 * 8
|
|
248
|
+
mem_len = struct.unpack_from('<Q', ctypes.string_at(base + off, 8), 0)[0]
|
|
249
|
+
off += 8
|
|
250
|
+
mem_data = bytes(ctypes.string_at(base + off, mem_len)) if mem_len else b''
|
|
251
|
+
off += mem_len
|
|
252
|
+
out_len = struct.unpack_from('<Q', ctypes.string_at(base + off, 8), 0)[0]
|
|
253
|
+
off += 8
|
|
254
|
+
out_data = bytes(ctypes.string_at(base + off, out_len)) if out_len else b''
|
|
255
|
+
off += out_len
|
|
256
|
+
err_len = struct.unpack_from('<H', ctypes.string_at(base + off, 2), 0)[0]
|
|
257
|
+
off += 2
|
|
258
|
+
err_msg = ctypes.string_at(base + off, err_len).decode('utf-8', 'replace') if err_len else ''
|
|
259
|
+
|
|
260
|
+
regs = list(struct.unpack('<33Q', regs_raw))
|
|
261
|
+
vec_flat = struct.unpack(f'<{32 * 4}d', vec_raw)
|
|
262
|
+
vec_regs = [list(vec_flat[i * 4:(i + 1) * 4]) for i in range(32)]
|
|
263
|
+
|
|
264
|
+
error = None
|
|
265
|
+
if status == 2:
|
|
266
|
+
error = 'unsupported'
|
|
267
|
+
elif status == 3:
|
|
268
|
+
error = err_msg or 'runtime error'
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
'status': status,
|
|
272
|
+
'halted': status == 0,
|
|
273
|
+
'pc': pc,
|
|
274
|
+
'sp': sp,
|
|
275
|
+
'heap_ptr': heap_ptr,
|
|
276
|
+
'steps': steps,
|
|
277
|
+
'regs': regs,
|
|
278
|
+
'vec_regs': vec_regs,
|
|
279
|
+
'mem': mem_data,
|
|
280
|
+
'output': out_data.decode('utf-8', 'replace'),
|
|
281
|
+
'error': error,
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
# ---------------- CROM ----------------
|
|
285
|
+
|
|
286
|
+
def crom_pack(self, mem: bytes, compress: bool) -> Optional[bytes]:
|
|
287
|
+
buf = ctypes.create_string_buffer(bytes(mem), len(mem))
|
|
288
|
+
out_len = ctypes.c_int(0)
|
|
289
|
+
ptr = self.lib.codecin_crom_pack(
|
|
290
|
+
ctypes.cast(buf, ctypes.c_void_p), len(mem),
|
|
291
|
+
1 if compress else 0, ctypes.byref(out_len))
|
|
292
|
+
if not ptr:
|
|
293
|
+
return None
|
|
294
|
+
try:
|
|
295
|
+
return bytes(ctypes.string_at(ptr, out_len.value))
|
|
296
|
+
finally:
|
|
297
|
+
self.lib.codecin_free(ptr)
|
|
298
|
+
|
|
299
|
+
def crom_unpack(self, data: bytes) -> Optional[bytes]:
|
|
300
|
+
buf = ctypes.create_string_buffer(data, len(data))
|
|
301
|
+
mem_len = ctypes.c_int(0)
|
|
302
|
+
flags = ctypes.c_int(0)
|
|
303
|
+
ptr = self.lib.codecin_crom_unpack(
|
|
304
|
+
ctypes.cast(buf, ctypes.c_void_p), len(data),
|
|
305
|
+
ctypes.byref(mem_len), ctypes.byref(flags))
|
|
306
|
+
if not ptr:
|
|
307
|
+
return None
|
|
308
|
+
try:
|
|
309
|
+
return bytes(ctypes.string_at(ptr, mem_len.value))
|
|
310
|
+
finally:
|
|
311
|
+
self.lib.codecin_free(ptr)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def get_engine(logger=None) -> Optional[NativeEngine]:
|
|
315
|
+
"""查找并加载原生库; 失败返回 None (纯 Python 回退)。"""
|
|
316
|
+
global _LIB_CACHE
|
|
317
|
+
if _LIB_CACHE is not False:
|
|
318
|
+
return _LIB_CACHE
|
|
319
|
+
|
|
320
|
+
engine = None
|
|
321
|
+
for path in _lib_candidates():
|
|
322
|
+
if not os.path.exists(path):
|
|
323
|
+
continue
|
|
324
|
+
try:
|
|
325
|
+
lib = ctypes.CDLL(path)
|
|
326
|
+
engine = NativeEngine(lib)
|
|
327
|
+
if logger:
|
|
328
|
+
logger.debug(f"Loaded native library: {path} ({engine.version()})")
|
|
329
|
+
break
|
|
330
|
+
except OSError as e:
|
|
331
|
+
if logger:
|
|
332
|
+
logger.debug(f"Failed to load native library {path}: {e}")
|
|
333
|
+
engine = None
|
|
334
|
+
|
|
335
|
+
_LIB_CACHE = engine
|
|
336
|
+
return engine
|