quickwin 1.3.0 → 2.0.0

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 (143) hide show
  1. package/README.en.md +1 -2
  2. package/README.md +19 -23
  3. package/examples/PdfCanvas.js +8 -37
  4. package/examples/PdfCanvas.tsx +11 -41
  5. package/examples/exec_server.js +20054 -1423
  6. package/examples/exec_server.tsx +489 -0
  7. package/examples/exec_server_worker.js +383 -33
  8. package/examples/exec_server_worker.ts +408 -30
  9. package/examples/pdf_preview2.js +25 -82
  10. package/examples/pdf_preview2.ts +27 -84
  11. package/examples/pdf_viewer.js +1318 -1178
  12. package/examples/pdf_viewer.tsx +16 -67
  13. package/examples/setres.js +22 -47
  14. package/examples/setres.ts +23 -57
  15. package/examples/test_gallery.js +1313 -1074
  16. package/examples/test_react_lazy.js +1284 -1056
  17. package/examples/test_react_listbox.js +1283 -1055
  18. package/examples/test_root_window.js +1279 -1051
  19. package/examples/test_tab.js +1284 -1056
  20. package/lib/abort.js +70 -0
  21. package/lib/abort.ts +69 -0
  22. package/lib/certs.js +214 -0
  23. package/lib/certs.ts +224 -0
  24. package/lib/exec-log.js +15 -0
  25. package/lib/exec-log.ts +30 -0
  26. package/lib/fetch.js +37 -4
  27. package/lib/fetch.ts +35 -4
  28. package/lib/ffi/bind.js +352 -0
  29. package/lib/ffi/bind.ts +442 -0
  30. package/lib/ffi/ctype.js +168 -0
  31. package/lib/ffi/ctype.ts +331 -0
  32. package/lib/ffi/index.js +6 -0
  33. package/lib/ffi/index.ts +6 -0
  34. package/lib/ffi/struct.js +412 -0
  35. package/lib/ffi/struct.ts +664 -0
  36. package/lib/http-common.js +43 -5
  37. package/lib/http-common.ts +48 -6
  38. package/lib/http-server.js +187 -29
  39. package/lib/http-server.ts +177 -26
  40. package/lib/react-qw/components/DateTimePicker.js +18 -37
  41. package/lib/react-qw/components/Input.js +3 -10
  42. package/lib/react-qw/components/Link.js +6 -19
  43. package/lib/react-qw/components/ListView.js +49 -154
  44. package/lib/react-qw/components/PathPicker.js +28 -88
  45. package/lib/react-qw/components/ScrollView.js +15 -8
  46. package/lib/react-qw/components/Slider.js +2 -1
  47. package/lib/react-qw/components/Tab.js +6 -22
  48. package/lib/react-qw/components/Tooltip.js +10 -21
  49. package/lib/react-qw/components/TreeView.js +8 -31
  50. package/lib/react-qw/index.js +4 -2
  51. package/lib/react-qw/index.ts +4 -2
  52. package/lib/react-qw/nmhdr.js +3 -24
  53. package/lib/react-qw/nmhdr.ts +5 -30
  54. package/lib/react-qw/props.js +7 -2
  55. package/lib/react-qw/props.ts +7 -3
  56. package/lib/react-qw/reconciler.js +23 -13
  57. package/lib/react-qw/reconciler.ts +22 -13
  58. package/lib/text-measure.js +18 -23
  59. package/lib/text-measure.ts +20 -25
  60. package/lib/websocket.js +12 -2
  61. package/lib/websocket.ts +15 -2
  62. package/lib/windows/comctl32.js +18 -0
  63. package/lib/windows/comctl32.ts +20 -0
  64. package/lib/windows/comdlg32.js +7 -0
  65. package/lib/windows/comdlg32.ts +9 -0
  66. package/lib/windows/gdi32.js +90 -0
  67. package/lib/windows/gdi32.ts +97 -0
  68. package/lib/windows/kernel32.js +66 -0
  69. package/lib/windows/kernel32.ts +76 -0
  70. package/lib/windows/ntdll.js +8 -0
  71. package/lib/windows/ntdll.ts +10 -0
  72. package/lib/windows/ole32.js +7 -0
  73. package/lib/windows/ole32.ts +9 -0
  74. package/lib/windows/shell32.js +11 -0
  75. package/lib/windows/shell32.ts +14 -0
  76. package/lib/windows/structs.js +384 -0
  77. package/lib/windows/structs.ts +422 -0
  78. package/lib/windows/user32.js +469 -0
  79. package/lib/windows/user32.ts +502 -0
  80. package/package.json +1 -1
  81. package/quickwin.d.ts +22 -37
  82. package/quickwin_const.d.ts +1 -0
  83. package/qwin-nowasm-x86.exe +0 -0
  84. package/qwin-nowasm.exe +0 -0
  85. package/qwin-x86.exe +0 -0
  86. package/qwin.exe +0 -0
  87. package/test/bench_ffi_callraw.js +236 -0
  88. package/test/bench_ffi_callraw.ts +236 -0
  89. package/test/bench_ffi_overhead.js +139 -0
  90. package/test/bench_ffi_overhead.ts +141 -0
  91. package/test/bench_sendmessage.js +104 -0
  92. package/test/bench_sendmessage.ts +100 -0
  93. package/test/bench_sendmessage_nonnull.js +187 -0
  94. package/test/bench_sendmessage_nonnull.ts +188 -0
  95. package/test/run.js +12 -4
  96. package/test/run.ts +13 -5
  97. package/test/test_basic_fetch.js +8 -0
  98. package/test/test_basic_fetch.ts +9 -0
  99. package/test/test_ffi.js +25 -43
  100. package/test/test_ffi.ts +33 -52
  101. package/test/test_ffi_abi.js +235 -0
  102. package/test/test_ffi_abi.ts +218 -0
  103. package/test/test_ffi_bind.js +538 -8
  104. package/test/test_ffi_bind.ts +559 -8
  105. package/test/test_ffi_struct.js +632 -88
  106. package/test/test_ffi_struct.ts +666 -91
  107. package/test/test_ffi_struct_layout.js +495 -0
  108. package/test/test_ffi_struct_layout.ts +543 -0
  109. package/test/test_ffi_user32.js +103 -0
  110. package/test/test_ffi_user32.ts +113 -0
  111. package/test/test_ffi_windows.js +101 -0
  112. package/test/test_ffi_windows.ts +113 -0
  113. package/test/test_helper.js +8 -1
  114. package/test/test_helper.ts +9 -1
  115. package/test/test_http_import.js +18 -0
  116. package/test/test_http_import.ts +19 -0
  117. package/test/test_http_server.js +73 -1
  118. package/test/test_http_server.ts +75 -1
  119. package/test/test_ipv6.js +2 -2
  120. package/test/test_ipv6.ts +2 -2
  121. package/test/test_net_fetch.js +70 -8
  122. package/test/test_net_fetch.ts +69 -7
  123. package/test/test_net_websocket.js +1 -1
  124. package/test/test_net_websocket.ts +1 -1
  125. package/test/test_react_complex.js +1294 -1066
  126. package/test/test_react_counter.js +1284 -1056
  127. package/test/test_react_flex.js +1304 -1069
  128. package/test/test_react_hidden.js +1287 -1059
  129. package/test/test_react_listview_link.js +1284 -1056
  130. package/test/test_react_notify.js +18534 -0
  131. package/test/test_react_render.js +1284 -1056
  132. package/test/test_wolfssl_read.js +3 -2
  133. package/test/test_wolfssl_read.ts +3 -1
  134. package/test/test_worker_net.js +35 -17
  135. package/test/test_worker_net.ts +27 -15
  136. package/tsconfig.json +1 -1
  137. package/examples/exec_server.ts +0 -80
  138. package/lib/ffi-bind.js +0 -85
  139. package/lib/ffi-bind.ts +0 -170
  140. package/lib/ffi-struct.js +0 -224
  141. package/lib/ffi-struct.ts +0 -239
  142. package/lib/win-common-structs.js +0 -39
  143. package/lib/win-common-structs.ts +0 -41
@@ -0,0 +1,331 @@
1
+ import { bufferPtr } from 'ffi'
2
+ import * as os from 'os'
3
+
4
+ // ============================================================
5
+ // 语法词汇 —— struct/bind 共用的 C 类型描述。标量直接写规范 token 字符串('i32'/'u32'),
6
+ // 指针写 '<>ptr'(裸地址)或 '<NAME>ptr'(带名);对象值一律带 '#' 声明键(见 §4)。
7
+ // 只描述「C 声明怎么写」(unit/length/encoding 等意图);内存布局与读写视图由
8
+ // struct.ts 的 computeStructLayout/lower 单独 lower 成 layout IR。
9
+ //
10
+ // 叶子模块:刻意不依赖 win/std/text-codec,可被 struct 直接引用,
11
+ // 避免「只用 struct」的调用方被动加载整个 bind 运行时。
12
+ // 四段按依赖序:§1 kind 词汇表 → §2 指针布局与品牌 → §3 token 词表与白名单(normToken) → §4 值词汇。
13
+ // ============================================================
14
+
15
+ // ============================================================
16
+ // §1 kind 词汇表 —— C_BASIC_TYPE 是唯一手写清单,下列集合全从它派生
17
+ // C_BasicType(12) bind 签名 token / lower 后 IR 共用
18
+ // C_Number(10) 结构体字段的数字档
19
+ // C_Integer(8) C_Number 去浮点 —— 位域存储单元只能是整数档
20
+ // Token(11+指针) C_BasicType_Token / Arg·Ret JsTypeOfToken 的操作面(见 §3)
21
+ // 排除项:'void' 无大小;裸 'ptr' 用户面禁写(须写 '<>ptr'),内部保留 ——
22
+ // 唯一随架构变宽的标量档。lower 后所有指针归一为 kind 'ptr',名字被擦掉。
23
+ // ============================================================
24
+ const C_BASIC_TYPE = [
25
+ 'void', 'u8', 'i8', 'u16', 'i16', 'u32', 'i32',
26
+ 'u64', 'i64', 'f32', 'f64', 'ptr',
27
+ ] as const
28
+
29
+ export type C_BasicType = (typeof C_BASIC_TYPE)[number]
30
+ export type C_BasicType_No_Void = Exclude<C_BasicType, 'void'>
31
+
32
+ // '!' 形随 token 走(normToken 只剥运行时不理解的 '@',运行时要执法的 '!' 保留——
33
+ // token 即契约,callPacked/closure 按尾缀做 0 检查,无独立 flag 可丢)。
34
+ export type C_BasicType_Token = Exclude<C_BasicType, 'ptr'> | `<${string}>ptr` | `<${string}>ptr!`
35
+ export type C_BasicType_Token_No_Void = Exclude<C_BasicType_Token, 'void'>
36
+
37
+ // 尾缀修饰:'!' = 非空担保(见 Arg/RetJsTypeOfToken 的 ! 分支与 callPacked 的运行时 0 检查),
38
+ // '@Name' = 枚举值域标注(类型层启用前先由 Norm 归一剥掉)。两者语义正交但作用面不相交
39
+ // (! 主品牌位、@ 主裸档整数位,@ 枚举不含 0 时已蕴含 !),故每 token 至多一个修饰。
40
+ // 组 1 恒为名字(归一名),组 2 为修饰;槽宽/ABI 只看归一形式。
41
+ const PTR_TOKEN_RE = /^<([^<>\s]*)>ptr(!|@[A-Za-z_]\w*)?$/
42
+
43
+ export function isCPtrToken(t: string): t is `<${string}>ptr` | `${string}ptr!` | `${string}ptr@${string}` { return PTR_TOKEN_RE.test(t) }
44
+ export function ptrName<const N extends string>(t: `<${N}>ptr` | `<${N}>ptr!`): N {
45
+ const m = PTR_TOKEN_RE.exec(t)
46
+ return (m?.[1] || "") as N
47
+ }
48
+ /**
49
+ * @description `<${string}>ptr` / `<${string}>ptr!` -> 'ptr'
50
+ */
51
+ export const cTokenToType = <const T extends string>(t: T): Exclude<T, `<${string}>ptr` | `<${string}>ptr!`> | 'ptr' => {
52
+ if (isCPtrToken(t)) return 'ptr'
53
+ else return t as Exclude<T, `<${string}>ptr` | `<${string}>ptr!`>
54
+ }
55
+
56
+ // C 的数字类型档:可作结构体字段 / bind 签名 token。排除项理由见 §1。
57
+ export type C_Number = Exclude<C_BasicType, 'void' | 'ptr'>
58
+
59
+ // 整数档:位域存储单元的合法类型(C 位域只能用整型)。
60
+ export type C_Integer = Exclude<C_Number, 'f32' | 'f64'>
61
+
62
+ export type Encoding = 'utf-8' | 'utf-16le'
63
+
64
+ type First =
65
+ | '_' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm'
66
+ | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z'
67
+ | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M'
68
+ | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z';
69
+
70
+ // ============================================================
71
+ // §4 值词汇(SimpleValue)—— 字段值/数组元素的全部合法形态:
72
+ // token 'u32'(规范 token 词汇;'void'/裸 'ptr'/拼错 token 运行时拒绝 —— typedef 别名已移除)
73
+ // 数组糖 'u32[4]'(仅 token 元素 —— 更复杂的元素写 '#' array 声明)
74
+ // 位域糖 'u32:3'(unit 位归一后须为整数档)
75
+ // 字符串糖 'u16[128]@utf-16le'(布局 unit[length] × 解释 @encoding 正交:
76
+ // size = typesize × length 由 unit/length 决定,encoding 只管怎么读写
77
+ // 这些字节 —— 错配如 'u8[128]@utf-16le' 也语义自洽。与键侧 alignas 同
78
+ // 用 '@' 但位置不同(键名尾 vs 值串尾),解析互不干扰。string 是唯一
79
+ // 没有 '#' 声明形式的值 —— 糖与 C_String 三元组双射,一种概念一种写法)
80
+ // '#' 声明 struct/union(平铺字段 + '#pack')、array(element+length)——
81
+ // 对象值的唯一判别键是 '#'(多字段/聚合元素无法用糖表达,故保留)
82
+ // '#' 必填(无「裸嵌套 map 默认 struct」):漏写 '#' 时 union 不会静默翻转成 struct,
83
+ // 类型层直接报错、运行时 throw。顶层 struct()/union() 例外 —— kind 由函数名表明,
84
+ // 参数是平铺字段表(不含 '#'),'#'/`#pack` 由构造器写入 __struct。
85
+ // ============================================================
86
+
87
+ // token 值面:规范 token(C_Number 档)∪ 指针形(含 '!' 非空尾缀)∪ '@枚举标注形'
88
+ // (枚举名限 keyof EnumMap —— 未知枚举名在定义点就 ∉ SimpleValue 被拒,与拼错 token
89
+ // 同一道闸)。'void' 除外 —— 无大小,不能作字段值。
90
+ export type SimpleToken =
91
+ | C_Number
92
+ | `${C_Number}@${keyof EnumMap}`
93
+ | `<${string}>ptr`
94
+ | `${string}ptr!`
95
+ | `<${string}>ptr@${keyof EnumMap}`
96
+
97
+ // 平铺字段表:struct()/union() 参数与 '#' 声明的字段部分(声明类型见下方 C_Struct/C_Union)。
98
+ // 键侧词汇:'k@N' = alignas N(键尾 @+数字,字段名不含 '@');'$x' = 匿名槽(只收聚合)。
99
+ // '#' 前缀键是指令位,与字段名(First 不含 '#')天然无撞名。
100
+ export type SimpleMember = {
101
+ [K: `${First}${string}`]: SimpleValue
102
+ [K: `$${string}`]: C_Struct | C_Union
103
+ }
104
+
105
+ // 用 interface(而非 `{'#': ...} & SimpleMember` 交叉别名):交叉 + 循环别名(SimpleMember
106
+ // ↔ SimpleValue ↔ 本类型)在展开时有解析顺序陷阱 —— 实测 C_Union 拿到的 SimpleMember
107
+ // 丢失索引签名(`'a' extends keyof C_Union` = false,字段字面量被 excess check 拒绝),
108
+ // 而 interface 成员惰性求值无此问题。索引签名与 SimpleMember 逐字同源,改一处须同步另一处。
109
+ export interface C_Struct {
110
+ '#': 'struct'
111
+ '#pack'?: number
112
+ [K: `${First}${string}`]: SimpleValue
113
+ [K: `$${string}`]: C_Struct | C_Union
114
+ }
115
+ export interface C_Union {
116
+ '#': 'union'
117
+ '#pack'?: number
118
+ [K: `${First}${string}`]: SimpleValue
119
+ [K: `$${string}`]: C_Struct | C_Union
120
+ }
121
+ export type C_Array = { '#': 'array', element: SimpleValue, length: number }
122
+
123
+ export type SimpleValue =
124
+ | SimpleToken
125
+ | `${SimpleToken}[${number}]`
126
+ | `${C_Number}:${number}` // 位域糖:仅裸数字档('@'/'!'/'<T>ptr' 尾缀在位域位词汇拒)
127
+ | `${'u8' | 'u16'}[${number}]@${Encoding}` // 字符串糖(unit/length 布局 + encoding 解释)
128
+ | C_Struct | C_Union | C_Array
129
+
130
+
131
+ export const PTR_SIZE = os.arch === 'x64' ? 8 : 4
132
+
133
+ export const SizeAlign: Record<C_Number, number> = {
134
+ u8: 1, i8: 1,
135
+ u16: 2, i16: 2,
136
+ u32: 4, i32: 4,
137
+ u64: 8, i64: 8,
138
+ f32: 4, f64: 8,
139
+ }
140
+
141
+ // brand 属性用普通字符串键(phantom——运行时就是裸 number,属性不存在):结构等价使
142
+ // quickwin.d.ts 的 gui.HWND 能直接书写 `number & { readonly ptrBrand: "HWND" }`,
143
+ // 与 Ptr<"HWND"> 双向可赋值、d.ts 零依赖 ctype;unique symbol 按声明位置定身份,
144
+ // 反而使跨包的重复副本互不兼容。同构碰撞的防线由 `number &` 交叉保证(裸 number
145
+ // 缺属性、纯对象缺 number,构造源只有 Ptr 本身或故意手写)。
146
+ // `''` 判在左侧(右侧实例化,不作 naked 分布):T=never 不再塌缩成 never(否则
147
+ // `Ptr<never>` 不可构造,decode(… | Ptr<N>) 在 N=never 时直接堵死),含 '' 的并集
148
+ // 不再拆分,宽 string 归一裸 number。字面品牌照旧走交叉 brand 分支。
149
+ // 空品牌塌缩成 number 是有意设计:FFI 的 `<>ptr` 是 uintptr 双态位(WPARAM/LPARAM/
150
+ // void*/LONG_PTR 在 C ABI 里既收地址又收整数),品牌化会把 sel、max、算术值这类
151
+ // 合法实参拦成伪错误——裸档误用靠 NULL 迁移后的调用点纪律兜底,不做 tsc 强制。
152
+ export type Ptr<T extends string> =
153
+ '' extends T ? number : number & { readonly ptrBrand: T }
154
+
155
+ /** NULL 品牌零:0 的品牌形态 —— 全 FFI 层唯一的空指针字面量(对应 C 的 NULL 宏,
156
+ * 运行时就是 0)。JS 的 null 不在指针值域:类型层禁收,运行时不做 null→0 转换
157
+ * (动态 null 在参数槽 / 写槽处 fail-loud 报错)。 */
158
+ export type NULL = 0 & { readonly ptrBrand: 'NULL' }
159
+ export const NULL = 0 as NULL
160
+
161
+ /** 唯一指针类型:参数位 / 返回位 / struct 字段 / closure 双侧通用。
162
+ * C→JS 0 保真(readScalar 不做 0→null);JS→C 空位写 NULL(写槽即 0)。
163
+ * 传参前由 tsc 收窄掉空位(truthy / === NULL / === 0)。 */
164
+ export type MaybePtr<T extends string> =
165
+ NULL | Ptr<T>
166
+
167
+ // 用户 token 词表 → JS 类型。刻意不含 'ptr':用户面禁写裸 ptr,Arg/Ret 的
168
+ // `K & keyof` 索引到 never 自塌拒绝;IR 层 kind 含 'ptr' 的消费者(Entry)
169
+ // 自行 `& { ptr: number }` 增量。
170
+ export type C_TypeJsTypeMap = {
171
+ u8: number; i8: number; u16: number; i16: number
172
+ u32: number; i32: number; i64: bigint, u64: bigint
173
+ f32: number; f64: number
174
+ }
175
+
176
+ export type TokenArgJsTypeMap = C_TypeJsTypeMap
177
+ export type TokenReturnJsTypeMap = C_TypeJsTypeMap & { void: void }
178
+
179
+ /** token 枚举标注('u32@Name' / '<>ptr@Name')的值域表 —— 全局 interface 声明合并:
180
+ * 预填由 tools/gen_enum_map.sh 从 quickwin_const.d.ts 提取(quickwin_enum_map.d.ts),
181
+ * 用户在自己的 .d.ts 里直接扩展(interface EnumMap { MyEnum: import('...').MyEnum })——
182
+ * 勿用 declare module:用户 import specifier 形态五花八门,模块匹配不上;全局合并对
183
+ * 「按枚举名索引」的 token 语义才成立。消费点(bind Arg/Ret、struct 字段)统一以
184
+ * `infer E extends keyof EnumMap` 收紧后直接索引本表 —— 值域类型不分参数/返回语境
185
+ * (拦异种枚举、域外字面量靠枚举类型自身的 nominal);未知枚举名由词汇/分支约束
186
+ * 挡在外面:struct 定义点 ∉ SimpleValue,bind 签名落链末 map[never] 塌 never
187
+ * (参数位)/ unknown(返回位——never 是 bottom 会被任意使用放行,反而危险)。 */
188
+ declare global {
189
+ // 预填见 quickwin_enum_map.d.ts;此处空声明兜底(预填文件缺省时引用处仍可解析)。
190
+ interface EnumMap { }
191
+ }
192
+
193
+ // token → JS 类型(实参语境)。L = 布局名→JS 形;M/D 已固化(TokenArgJsTypeMap /
194
+ // never —— 'void'/裸 'ptr'/拼错 token 经 map[K & keyof] 塌 never,参数位调用点全红
195
+ // fail-loud)。分支链:'!' 在链首(非空担保 → Ptr<N> 排 NULL,带布局形照收);
196
+ // '@E' 枚举值域(模式用 `infer E extends keyof EnumMap` 收紧:未知枚举名分支失配、
197
+ // 落链末 map[never] —— 与 struct 侧词汇定义点拒绝构成双层防线;nominal:拦异种枚举、
198
+ // 拦裸 0/NULL;单值位标注,flags 组合位不标;不并 layout 形 —— codec 入参编码成
199
+ // 地址必然落在枚举域外,留着就是绕过值域承诺的无检查后门);裸 ptr 形收
200
+ // MaybePtr | L[N]。'u32!'/'DWORD' 等不匹配任何已知形 → map[never] 塌 never
201
+ // (非法形态宁可全红也不放行)。泛型未解析位(K = string —— closure/bindLib
202
+ // 泛型体内)由 `string & keyof map` = 全键联合兜底成非 never —— 否则 BindFn 塌
203
+ // (never)=>never 使内层强转失去可比性。
204
+ export type ArgJsTypeOfToken<K extends string, L> =
205
+ K extends `<${infer N}>ptr!` ? Ptr<N> | L[N & keyof L] :
206
+ K extends `<${string}>ptr@${infer E extends keyof EnumMap}` ? EnumMap[E] :
207
+ K extends `<${infer N}>ptr` ? MaybePtr<N> | L[N & keyof L] :
208
+ K extends `${C_Number}@${infer E extends keyof EnumMap}` ? EnumMap[E] :
209
+ TokenArgJsTypeMap[K & keyof TokenArgJsTypeMap] extends never ? never
210
+ : TokenArgJsTypeMap[K & keyof TokenArgJsTypeMap]
211
+
212
+ // token → JS 类型(返回语境)。D = 返回位布局表 decode 值域:该键有 decode →
213
+ // D[N] | NULL(解码结果替换地址,空指针 0 保真为 NULL);否则 MaybePtr<N>,
214
+ // 与实参位同型(空品牌 <''> 归一裸 number,联合坍缩)。'!' 分支 = 「C 保证非零」
215
+ // 的作者担保:去掉 NULL 分支(有 decode → D[N],无 decode → Ptr<N>),运行时返回
216
+ // 0 当场 throw 兜底标错;'@E' 分支 = 担保返回值在枚举值域(标错同担,值域校验按需
217
+ // Phase 2)。非法 token('i3z'/裸 'ptr')经 map[never] 塌 unknown —— 返回位刻意不是
218
+ // never:never 是 bottom 会被任意使用全放行,unknown 逼调用方显式收窄(与参数位
219
+ // never 拦调用的不对称是设计)。
220
+ export type RetJsTypeOfToken<K extends string, D> =
221
+ K extends `<${infer N}>ptr!`
222
+ ? (D[N & keyof D] extends never ? Ptr<N> : D[N & keyof D])
223
+ : K extends `<${string}>ptr@${infer E extends keyof EnumMap}`
224
+ ? EnumMap[E]
225
+ : K extends `<${infer N}>ptr`
226
+ ? (D[N & keyof D] extends never ? MaybePtr<N> : D[N & keyof D] | NULL)
227
+ : K extends `${C_Number}@${infer E extends keyof EnumMap}`
228
+ ? EnumMap[E]
229
+ : TokenReturnJsTypeMap[K & keyof TokenReturnJsTypeMap] extends never ? unknown
230
+ : TokenReturnJsTypeMap[K & keyof TokenReturnJsTypeMap]
231
+
232
+ // token 归一:只收规范 token(C_BASIC_TYPE 去 'ptr' = C_Number ∪ 'void',由 §1 唯一手写
233
+ // 清单派生)与指针形;拼错 token、已移除的 typedef 别名('DWORD'/'HANDLE')一律 fail-loud。
234
+ export function normToken(t: string): C_BasicType_Token {
235
+ if (isCPtrToken(t)) {
236
+ // 只剥 '@Name'(枚举标注是纯类型层语义,运行时不校验、不理解);'!' 保留在
237
+ // token 上——运行时要执法的语义(0 检查)跟着 token 走到 callPacked/closure,
238
+ // 无独立 flag 可被解构丢失。槽宽/ABI 判型走 isCPtrToken,带 '!' 不受影响。
239
+ const m = PTR_TOKEN_RE.exec(t)
240
+ return (m?.[2]?.startsWith('@') ? `<${m[1]}>ptr` : t) as C_BasicType_Token
241
+ }
242
+ // 整数档尾缀 '@Name'(枚举值域标注,与 ptr 位的 '@' 同源):剥掉归一到原 token,
243
+ // 槽宽/ABI 只看归一形式。@ 后缀须为纯枚举名形 —— 'u32@WmMsg:3' 这类混进位域/数组
244
+ // 串的直接 throw,不静默吞成纯标量(布局会错);名 ∈ EnumMap 的语义校验留给类型层
245
+ // (运行时不理解枚举名)。白名单 = C_BASIC_TYPE(§1 唯一手写清单);'u32!' 等非
246
+ // ptr 位 '!' 尾缀、别名、拼错 token 不命中,落 throw(类型层先塌 never,这里
247
+ // 兜底动态传入)。
248
+ const at = t.indexOf('@')
249
+ const base = at >= 0 ? t.slice(0, at) : t
250
+ const suffixOk = at < 0 || /^[A-Za-z_]\w*$/.test(t.slice(at + 1))
251
+ if (suffixOk && base !== 'ptr' && (C_BASIC_TYPE as readonly string[]).includes(base)) return base as C_BasicType_Token
252
+ throw new Error("Unknown token: " + t)
253
+ }
254
+
255
+ export function readScalar(dv: DataView, off: number, k: C_BasicType_No_Void): number | bigint | null {
256
+ switch (k) {
257
+ case 'u8': return dv.getUint8(off)
258
+ case 'i8': return dv.getInt8(off)
259
+ case 'u16': return dv.getUint16(off, true)
260
+ case 'i16': return dv.getInt16(off, true)
261
+ case 'u32': return dv.getUint32(off, true)
262
+ case 'i32': return dv.getInt32(off, true)
263
+ case 'u64': return dv.getBigUint64(off, true)
264
+ case 'i64': return dv.getBigInt64(off, true)
265
+ case 'f32': return dv.getFloat32(off, true)
266
+ case 'f64': return dv.getFloat64(off, true)
267
+ case 'ptr': {
268
+ // 有符号读(对齐 C 版 JS_NewInt64):-1 哨兵等负值保真;用户态指针高位
269
+ // 为 0,有符号读与无符号同值。0 保真返回(即 NULL 品牌零,类型层
270
+ // MaybePtr = NULL | Ptr<N>,传参前收窄掉空位)。写回位模式见 writeScalar。
271
+ return PTR_SIZE === 8 ? Number(dv.getBigInt64(off, true)) : dv.getInt32(off, true)
272
+ }
273
+ }
274
+ }
275
+
276
+ // IR kind 词汇(C_BasicType_No_Void 含 'ptr')——用户词表无 ptr 档,这里自行增量。
277
+ type Entry =
278
+ { [K in C_BasicType_No_Void]: { k: K; v: (C_TypeJsTypeMap & { ptr: number })[K] } }[C_BasicType_No_Void];
279
+
280
+ /** 内存写:按 sizeof 精确写,供 struct 字段编码用。小整数只占自己的字节,
281
+ * 否则 `{a:u8,b:u8}` 这类紧凑布局会被 setUint32 越界覆盖相邻字段。 */
282
+ export function writeScalar(dv: DataView, off: number, { k, v }: Entry): void {
283
+ switch (k) {
284
+ case 'u8': dv.setUint8(off, Number(v)); break
285
+ case 'i8': dv.setInt8(off, Number(v)); break
286
+ case 'u16': dv.setUint16(off, Number(v), true); break
287
+ case 'i16': dv.setInt16(off, Number(v), true); break
288
+ case 'u32': dv.setUint32(off, (v) >>> 0, true); break
289
+ case 'i32': dv.setUint32(off, (v) >>> 0, true); break
290
+ case 'u64': dv.setBigUint64(off, v, true); break
291
+ case 'i64': dv.setBigInt64(off, v, true); break
292
+ case 'f32': dv.setFloat32(off, (v), true); break
293
+ case 'f64': dv.setFloat64(off, (v), true); break
294
+ case 'ptr': {
295
+ // JS null/undefined 不是指针词汇(空位写 NULL)——fail-loud,不做 null→0 转换。
296
+ if ((v as unknown) == null) {
297
+ throw new Error('ffi: got null/undefined for a ptr slot; pass the imported NULL constant for empty')
298
+ }
299
+ // 读回的负值(LRESULT/句柄)再作参数回传时按二补位位模式写:asUintN /
300
+ // >>> 0 取模保真,否则 x64 传负 setBigUint64 直接 RangeError。
301
+ if (PTR_SIZE === 8) dv.setBigUint64(off, BigInt.asUintN(64, BigInt(v)), true)
302
+ else dv.setUint32(off, v >>> 0, true)
303
+ break
304
+ }
305
+ }
306
+ }
307
+
308
+ /** 槽写:供 bind 的参数槽/返回槽用。小整数扩展到 4 字节 —— 可变参数按 int 提升读,
309
+ * `i8 = -1` 必须给 `0xFFFFFFFF` 而非 `0x000000FF`。u32/i32/u64/i64/f32/f64/ptr
310
+ * 槽宽 == sizeof,直接委托 writeScalar。 */
311
+ export function writeSlot(dv: DataView, off: number, e: Entry): void {
312
+ const { k, v } = e
313
+ if (k === 'u8' || k === 'i8' || k === 'u16' || k === 'i16') {
314
+ dv.setUint32(off, Number(v) >>> 0, true)
315
+ return
316
+ }
317
+ writeScalar(dv, off, e)
318
+ }
319
+
320
+ /** buffer + 品牌指针合一:一个引用既是 ArrayBuffer(喂 decode / encode(v, buf) 复用)
321
+ * 又带 `.ptr` 直接喂 <N>ptr 形参 —— 替代旧 { buf, ptr } 结构包装。
322
+ * `ptr` 是构造时快照(与旧 StructAlloc 同性质:底层数据不可 detach/transfer)。
323
+ * 品牌 N 由创建方声明:struct def 的 alloc()/encode() 返回值在类型层带 def 的 N(不可伪造);
324
+ * 直接 new 属自报品牌,仅限内部/原型场景 —— 生产路径一律走 def.alloc()。 */
325
+ export class PtrArrayBuffer<N extends string> extends ArrayBuffer {
326
+ readonly ptr: Ptr<N>
327
+ constructor(byteLength: number) {
328
+ super(byteLength)
329
+ this.ptr = bufferPtr(this) as Ptr<N>
330
+ }
331
+ }
@@ -0,0 +1,6 @@
1
+ // FFI 公共入口:bind()/bindLib()/closure()(bind.ts)+ struct()/union()(struct.ts)。
2
+ // 共享的 CType IR / 标量 kind / C 别名表在 ctype.ts(bind.ts 再透传其类型与函数)。
3
+ // 仓库内调用点按需深引用(./bind.js / ./struct.js / ./ctype.js)以保留 tree-shaking;
4
+ // 此 barrel 主要供外部/package 使用。
5
+ export * from './bind.js';
6
+ export * from './struct.js';
@@ -0,0 +1,6 @@
1
+ // FFI 公共入口:bind()/bindLib()/closure()(bind.ts)+ struct()/union()(struct.ts)。
2
+ // 共享的 CType IR / 标量 kind / C 别名表在 ctype.ts(bind.ts 再透传其类型与函数)。
3
+ // 仓库内调用点按需深引用(./bind.js / ./struct.js / ./ctype.js)以保留 tree-shaking;
4
+ // 此 barrel 主要供外部/package 使用。
5
+ export * from './bind.js'
6
+ export * from './struct.js'