quickwin 3.0.0 → 3.0.2

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 (253) hide show
  1. package/README.en.md +3 -0
  2. package/README.md +2 -0
  3. package/lib/abort.d.ts +21 -0
  4. package/lib/assert.d.ts +2 -0
  5. package/lib/bitmap.d.ts +7 -0
  6. package/lib/cache-utils.d.ts +6 -0
  7. package/lib/certs.d.ts +1 -0
  8. package/lib/dpi.d.ts +9 -0
  9. package/lib/dumpRects.d.ts +2 -0
  10. package/lib/exec-log.d.ts +23 -0
  11. package/lib/fetch.d.ts +4 -0
  12. package/lib/ffi/bind.d.ts +70 -0
  13. package/lib/ffi/ctype.d.ts +117 -0
  14. package/lib/ffi/index.d.ts +2 -0
  15. package/lib/ffi/struct.d.ts +174 -0
  16. package/lib/http-common.d.ts +143 -0
  17. package/lib/http-server.d.ts +59 -0
  18. package/lib/polyfill.d.ts +22 -0
  19. package/lib/react-qw/components/Button.d.ts +9 -0
  20. package/lib/react-qw/components/CheckBox.d.ts +11 -0
  21. package/lib/react-qw/components/ComboBox.d.ts +11 -0
  22. package/lib/react-qw/components/DateTimePicker.d.ts +15 -0
  23. package/lib/react-qw/components/Input.d.ts +15 -0
  24. package/lib/react-qw/components/Link.d.ts +12 -0
  25. package/lib/react-qw/components/ListBox.d.ts +13 -0
  26. package/lib/react-qw/components/ListView.d.ts +37 -0
  27. package/lib/react-qw/components/PathPicker.d.ts +23 -0
  28. package/lib/react-qw/components/ProgressBar.d.ts +9 -0
  29. package/lib/react-qw/components/RadioButton.d.ts +11 -0
  30. package/lib/react-qw/components/ScrollView.d.ts +9 -0
  31. package/lib/react-qw/components/Slider.d.ts +12 -0
  32. package/lib/react-qw/components/Tab.d.ts +14 -0
  33. package/lib/react-qw/components/Tooltip.d.ts +7 -0
  34. package/lib/react-qw/components/TreeView.d.ts +18 -0
  35. package/lib/react-qw/components/index.d.ts +16 -0
  36. package/lib/react-qw/index.d.ts +24 -0
  37. package/lib/react-qw/layout.d.ts +29 -0
  38. package/lib/react-qw/nmhdr.d.ts +1 -0
  39. package/lib/react-qw/props.d.ts +2 -0
  40. package/lib/react-qw/reconciler.d.ts +21 -0
  41. package/lib/stream.d.ts +20 -0
  42. package/lib/text-codec.d.ts +39 -0
  43. package/lib/text-measure.d.ts +14 -0
  44. package/lib/url.d.ts +54 -0
  45. package/lib/websocket.d.ts +73 -0
  46. package/lib/windows/{advapi32.ts → advapi32.d.ts} +6 -21
  47. package/lib/windows/comctl32.d.ts +14 -0
  48. package/lib/windows/comdlg32.d.ts +4 -0
  49. package/lib/windows/{gdi32.ts → gdi32.d.ts} +161 -98
  50. package/lib/windows/{kernel32.ts → kernel32.d.ts} +138 -115
  51. package/lib/windows/{msimg32.ts → msimg32.d.ts} +11 -14
  52. package/lib/windows/ntdll.d.ts +5 -0
  53. package/lib/windows/ole32.d.ts +3 -0
  54. package/lib/windows/{psapi.ts → psapi.d.ts} +9 -18
  55. package/lib/windows/{shell32.ts → shell32.d.ts} +25 -24
  56. package/lib/windows/structs.d.ts +3141 -0
  57. package/lib/windows/{user32.ts → user32.d.ts} +196 -193
  58. package/lib/windows/version.d.ts +27 -0
  59. package/lib/windows/{winspool.ts → winspool.d.ts} +18 -32
  60. package/package.json +15 -8
  61. package/qwin-nowasm-x86.exe +0 -0
  62. package/qwin-nowasm.exe +0 -0
  63. package/qwin-x86.exe +0 -0
  64. package/qwin.exe +0 -0
  65. package/examples/PdfCanvas.js +0 -38
  66. package/examples/PdfCanvas.tsx +0 -54
  67. package/examples/execd/main.js +0 -23379
  68. package/examples/execd/tray.js +0 -192
  69. package/examples/execd/worker.js +0 -122
  70. package/examples/pdf_preview2.js +0 -398
  71. package/examples/pdf_preview2.ts +0 -353
  72. package/examples/pdf_viewer.js +0 -22515
  73. package/examples/pdf_viewer.tsx +0 -228
  74. package/examples/test_gallery.js +0 -18692
  75. package/examples/test_gallery.tsx +0 -291
  76. package/examples/test_react_lazy.js +0 -18560
  77. package/examples/test_react_lazy.tsx +0 -65
  78. package/examples/test_react_listbox.js +0 -18562
  79. package/examples/test_react_listbox.tsx +0 -73
  80. package/examples/test_root_window.js +0 -18532
  81. package/examples/test_root_window.tsx +0 -41
  82. package/examples/test_tab.js +0 -18563
  83. package/examples/test_tab.tsx +0 -77
  84. package/examples/tray_demo.js +0 -86
  85. package/examples/tray_demo.tsx +0 -89
  86. package/lib/abort.ts +0 -69
  87. package/lib/assert.ts +0 -7
  88. package/lib/bitmap.ts +0 -39
  89. package/lib/cache-utils.ts +0 -40
  90. package/lib/certs.ts +0 -258
  91. package/lib/dpi.ts +0 -31
  92. package/lib/dumpRects.ts +0 -34
  93. package/lib/exec-log.ts +0 -30
  94. package/lib/fetch.ts +0 -589
  95. package/lib/ffi/bind.ts +0 -540
  96. package/lib/ffi/ctype.ts +0 -371
  97. package/lib/ffi/index.ts +0 -6
  98. package/lib/ffi/struct.ts +0 -780
  99. package/lib/http-common.ts +0 -441
  100. package/lib/http-server.ts +0 -604
  101. package/lib/polyfill.ts +0 -171
  102. package/lib/react-qw/components/index.ts +0 -17
  103. package/lib/react-qw/index.ts +0 -96
  104. package/lib/react-qw/layout.ts +0 -111
  105. package/lib/react-qw/nmhdr.ts +0 -7
  106. package/lib/react-qw/props.ts +0 -41
  107. package/lib/react-qw/reconciler.ts +0 -379
  108. package/lib/stream.ts +0 -22
  109. package/lib/text-codec.ts +0 -171
  110. package/lib/text-measure.ts +0 -44
  111. package/lib/url.ts +0 -201
  112. package/lib/websocket.ts +0 -600
  113. package/lib/windows/comctl32.ts +0 -20
  114. package/lib/windows/comdlg32.ts +0 -10
  115. package/lib/windows/ntdll.ts +0 -11
  116. package/lib/windows/ole32.ts +0 -9
  117. package/lib/windows/structs.ts +0 -807
  118. package/lib/windows/version.ts +0 -35
  119. package/test/add.wasm +0 -0
  120. package/test/bench_ffi_callraw.js +0 -235
  121. package/test/bench_ffi_callraw.ts +0 -235
  122. package/test/bench_ffi_overhead.js +0 -139
  123. package/test/bench_ffi_overhead.ts +0 -141
  124. package/test/bench_sendmessage.js +0 -104
  125. package/test/bench_sendmessage.ts +0 -100
  126. package/test/bench_sendmessage_nonnull.js +0 -187
  127. package/test/bench_sendmessage_nonnull.ts +0 -188
  128. package/test/complex.wasm +0 -0
  129. package/test/complex_imports.wasm +0 -0
  130. package/test/debug/http_server_e2e.js +0 -31
  131. package/test/debug/minwasm.js +0 -52
  132. package/test/debug/mupdf_debug.js +0 -86
  133. package/test/debug/wasm.js +0 -122
  134. package/test/debug/wasm_export_debug.js +0 -54
  135. package/test/debug/wasm_names.js +0 -36
  136. package/test/example.pdf +0 -0
  137. package/test/global_imports.wasm +0 -0
  138. package/test/import_func.wasm +0 -0
  139. package/test/imports.wasm +0 -0
  140. package/test/mupdf-cdn.js +0 -40
  141. package/test/mupdf-cdn.ts +0 -43
  142. package/test/run.js +0 -111
  143. package/test/run.ts +0 -116
  144. package/test/sjlj.wasm +0 -0
  145. package/test/test_basic.js +0 -7
  146. package/test/test_basic.ts +0 -9
  147. package/test/test_basic_fetch.js +0 -63
  148. package/test/test_basic_fetch.ts +0 -59
  149. package/test/test_brotli.js +0 -48
  150. package/test/test_brotli.ts +0 -52
  151. package/test/test_fetch_cache.js +0 -169
  152. package/test/test_fetch_cache.ts +0 -187
  153. package/test/test_ffi.js +0 -157
  154. package/test/test_ffi.ts +0 -170
  155. package/test/test_ffi_abi.js +0 -290
  156. package/test/test_ffi_abi.ts +0 -274
  157. package/test/test_ffi_array.js +0 -116
  158. package/test/test_ffi_array.ts +0 -141
  159. package/test/test_ffi_bind.js +0 -656
  160. package/test/test_ffi_bind.ts +0 -670
  161. package/test/test_ffi_struct.js +0 -779
  162. package/test/test_ffi_struct.ts +0 -828
  163. package/test/test_ffi_struct_layout.js +0 -495
  164. package/test/test_ffi_struct_layout.ts +0 -543
  165. package/test/test_ffi_user32.js +0 -103
  166. package/test/test_ffi_user32.ts +0 -113
  167. package/test/test_ffi_windows.js +0 -172
  168. package/test/test_ffi_windows.ts +0 -187
  169. package/test/test_files.js +0 -102
  170. package/test/test_files.ts +0 -121
  171. package/test/test_frame_encoding.js +0 -128
  172. package/test/test_frame_encoding.ts +0 -132
  173. package/test/test_gdi_draw.js +0 -681
  174. package/test/test_gdi_draw.ts +0 -725
  175. package/test/test_helper.js +0 -91
  176. package/test/test_helper.ts +0 -88
  177. package/test/test_http_import.js +0 -96
  178. package/test/test_http_import.ts +0 -93
  179. package/test/test_http_server.js +0 -214
  180. package/test/test_http_server.ts +0 -211
  181. package/test/test_import_dir.js +0 -4
  182. package/test/test_import_dir.ts +0 -5
  183. package/test/test_ipv6.js +0 -134
  184. package/test/test_ipv6.ts +0 -130
  185. package/test/test_jsx.js +0 -1314
  186. package/test/test_meta_url.js +0 -3
  187. package/test/test_meta_url.ts +0 -4
  188. package/test/test_mupdf_render.js +0 -51
  189. package/test/test_mupdf_render.ts +0 -55
  190. package/test/test_mupdf_twice.js +0 -62
  191. package/test/test_mupdf_twice.ts +0 -65
  192. package/test/test_mupdf_wasm.js +0 -39
  193. package/test/test_mupdf_wasm.ts +0 -32
  194. package/test/test_net_event.js +0 -57
  195. package/test/test_net_event.ts +0 -56
  196. package/test/test_net_fetch.js +0 -384
  197. package/test/test_net_fetch.ts +0 -373
  198. package/test/test_net_websocket.js +0 -180
  199. package/test/test_net_websocket.ts +0 -164
  200. package/test/test_polyfill.js +0 -144
  201. package/test/test_polyfill.ts +0 -159
  202. package/test/test_printer.js +0 -137
  203. package/test/test_printer.ts +0 -148
  204. package/test/test_proc.js +0 -141
  205. package/test/test_proc.ts +0 -163
  206. package/test/test_react_complex.js +0 -18678
  207. package/test/test_react_counter.js +0 -18555
  208. package/test/test_react_flex.js +0 -18871
  209. package/test/test_react_hidden.js +0 -18560
  210. package/test/test_react_listview_link.js +0 -18590
  211. package/test/test_react_notify.js +0 -18797
  212. package/test/test_react_render.js +0 -18550
  213. package/test/test_registry.js +0 -74
  214. package/test/test_registry.ts +0 -95
  215. package/test/test_sysinfo.js +0 -122
  216. package/test/test_sysinfo.ts +0 -141
  217. package/test/test_url.js +0 -173
  218. package/test/test_url.ts +0 -183
  219. package/test/test_wasm_basic.js +0 -85
  220. package/test/test_wasm_basic.ts +0 -73
  221. package/test/test_wasm_import_global.js +0 -41
  222. package/test/test_wasm_import_global.ts +0 -45
  223. package/test/test_wasm_sjlj.js +0 -145
  224. package/test/test_wasm_sjlj.ts +0 -125
  225. package/test/test_wasm_types.js +0 -97
  226. package/test/test_wasm_types.ts +0 -124
  227. package/test/test_wolfssl_read.js +0 -203
  228. package/test/test_wolfssl_read.ts +0 -184
  229. package/test/test_worker.js +0 -21
  230. package/test/test_worker.ts +0 -24
  231. package/test/test_worker_concurrent.js +0 -45
  232. package/test/test_worker_concurrent.ts +0 -56
  233. package/test/test_worker_data_url.js +0 -46
  234. package/test/test_worker_data_url.ts +0 -51
  235. package/test/test_worker_fetch.js +0 -26
  236. package/test/test_worker_fetch.ts +0 -27
  237. package/test/test_worker_net.js +0 -69
  238. package/test/test_worker_net.ts +0 -66
  239. package/test/test_worker_wasm_concurrent.js +0 -44
  240. package/test/test_worker_wasm_concurrent.ts +0 -45
  241. package/test/types.wasm +0 -0
  242. package/test/worker_fetch.js +0 -13
  243. package/test/worker_fetch.ts +0 -13
  244. package/test/worker_helper.js +0 -1
  245. package/test/worker_helper.ts +0 -1
  246. package/test/worker_http.js +0 -18
  247. package/test/worker_http.ts +0 -20
  248. package/test/worker_import_lib.js +0 -12
  249. package/test/worker_import_lib.ts +0 -13
  250. package/test/worker_module.js +0 -16
  251. package/test/worker_module.ts +0 -17
  252. package/test/worker_wasm_concurrent.js +0 -22
  253. package/test/worker_wasm_concurrent.ts +0 -22
@@ -1,290 +0,0 @@
1
- import * as std from 'std';
2
- import * as ffi from 'ffi';
3
- import * as os from 'os';
4
- import { bind } from '../lib/ffi/bind.js';
5
- import { struct } from '../lib/ffi/struct.js';
6
- import { NULL, PtrArrayBuffer } from '../lib/ffi/ctype.js';
7
- import { POINT, RECT } from '../lib/windows/structs.js';
8
- // ABI 边界回归:覆盖 REMOVE_LIBFFI_PLAN.md §4.2 的维度,全部用真实 Win32/CRT
9
- // 函数(不新增测试专用导出)。缺函数的平台记 SKIP 而非 FAIL,避免环境差异误报。
10
- //
11
- // 维度 → 用例:
12
- // 参数个数 0/1/3/4/5/6/8/12:GetTickCount/fabs/memcpy/GetLocaleInfoA/
13
- // SetRect/CompareStringW/WideCharToMultiByte/SetDIBitsToDevice
14
- // 整浮混合:ldexp(f64,i32)、frexp(f64,ptr)、modf(f64,ptr)
15
- // 前 4 全整数(RCX-R9):GetLocaleInfoA 的 4 个参数
16
- // 负数返回 i32/i16/i8:lstrcmpW;i64:InterlockedIncrement64
17
- // 窄整型负值传参:abs(i8/i16/u8/u16)
18
- // 大 64 位传参:InterlockedExchange64
19
- // 指针 NULL:GetDC(NULL)、IsWindow(NULL);null/undefined 不是指针合法值,应拒绝
20
- function readAscii(buf, len) {
21
- const dv = new DataView(buf);
22
- let s = '';
23
- for (let i = 0; i < len; i++) {
24
- const c = dv.getUint8(i);
25
- if (c === 0)
26
- break;
27
- s += String.fromCharCode(c);
28
- }
29
- return s;
30
- }
31
- function has(dll, name) {
32
- return !!dll && !!ffi.GetProcAddress(dll, name);
33
- }
34
- export const suite = {
35
- name: 'ffi-abi',
36
- run: (t) => {
37
- const k32 = ffi.LoadLibrary('kernel32.dll');
38
- const usr = ffi.LoadLibrary('user32.dll');
39
- const gdi = ffi.LoadLibrary('gdi32.dll');
40
- const crt = ffi.LoadLibrary('msvcrt.dll');
41
- t.section('param count 0 ( -> u32)');
42
- if (has(k32, 'GetTickCount')) {
43
- const getTickCount = bind('kernel32.dll', 'GetTickCount', ' -> u32');
44
- const tc = getTickCount();
45
- t.checkTrue('GetTickCount() > 0', tc > 0);
46
- }
47
- else
48
- t.skipCase('GetTickCount missing');
49
- t.section('param count 1 (f64 -> f64)');
50
- if (has(crt, 'fabs')) {
51
- const fabs = bind('msvcrt.dll', 'fabs', 'f64 -> f64');
52
- t.check('fabs(-3.5)', 3.5, fabs(-3.5));
53
- }
54
- else
55
- t.skipCase('fabs missing');
56
- t.section('param count 3 (<BYTE>ptr <BYTE>ptr u32 -> ptr)');
57
- if (has(crt, 'memcpy')) {
58
- const memcpy = bind('msvcrt.dll', 'memcpy', '<BYTE>ptr <BYTE>ptr u32 -> <>ptr');
59
- const srcB = new PtrArrayBuffer(8);
60
- const src = new Uint8Array(srcB);
61
- src.set([1, 2, 3, 4, 5, 6, 7, 8]);
62
- const dstB = new PtrArrayBuffer(8);
63
- const dst = new Uint8Array(dstB);
64
- const r = memcpy(dstB, srcB, 8);
65
- const rd = Array.from(dst).join(',');
66
- t.check('memcpy copied bytes', '1,2,3,4,5,6,7,8', rd);
67
- t.checkTrue('memcpy returns dest ptr', typeof r === 'number' && r !== 0);
68
- }
69
- else
70
- t.skipCase('memcpy missing');
71
- t.section('param count 4 + all-int-regs (u32 u32 <BYTE>ptr i32 -> i32)');
72
- if (has(k32, 'GetLocaleInfoA')) {
73
- const getLocaleInfoA = bind('kernel32.dll', 'GetLocaleInfoA', 'u32 u32 <BYTE>ptr i32 -> i32');
74
- const buf = new PtrArrayBuffer(64);
75
- const n = getLocaleInfoA(0x0409, 0x00001001 /* LOCALE_SENGLANGUAGE */, buf, 64);
76
- if (n > 0) {
77
- const s = readAscii(buf, n);
78
- std.printf(' GetLocaleInfoA(0x0409, LOCALE_SENGLANGUAGE) = "%s" (%d)\n', s, n);
79
- t.check('en-US language name', 'English', s);
80
- }
81
- else
82
- t.skipCase('en-US locale unavailable');
83
- }
84
- else
85
- t.skipCase('GetLocaleInfoA missing');
86
- t.section('param count 5 (<BYTE>ptr i32 i32 i32 i32 -> i32)');
87
- if (has(usr, 'SetRect')) {
88
- const setRect = bind('user32.dll', 'SetRect', '<BYTE>ptr i32 i32 i32 i32 -> i32');
89
- const rc = new PtrArrayBuffer(16);
90
- const ok = setRect(rc, 1, 2, 30, 40);
91
- const dv = new DataView(rc);
92
- t.checkTrue('SetRect returns nonzero', ok !== 0);
93
- t.check('SetRect left', 1, dv.getInt32(0, true));
94
- t.check('SetRect top', 2, dv.getInt32(4, true));
95
- t.check('SetRect right', 30, dv.getInt32(8, true));
96
- t.check('SetRect bottom', 40, dv.getInt32(12, true));
97
- }
98
- else
99
- t.skipCase('SetRect missing');
100
- t.section('param count 6 (u32 u32 <WCHAR>ptr i32 <WCHAR>ptr i32 -> i32)');
101
- if (has(k32, 'CompareStringW')) {
102
- const compareStringW = bind('kernel32.dll', 'CompareStringW', 'u32 u32 <WCHAR>ptr i32 <WCHAR>ptr i32 -> i32');
103
- const r = compareStringW(0x0400 /* LOCALE_USER_DEFAULT */, 0, 'abc', -1, 'abd', -1);
104
- t.check('CompareStringW("abc","abd") = CSTR_LESS_THAN', 1, r);
105
- }
106
- else
107
- t.skipCase('CompareStringW missing');
108
- t.section('param count 8 (u32 u32 <WCHAR>ptr i32 <BYTE>ptr i32 <>ptr <>ptr -> i32)');
109
- if (has(k32, 'WideCharToMultiByte')) {
110
- const wideCharToMultiByte = bind('kernel32.dll', 'WideCharToMultiByte', 'u32 u32 <WCHAR>ptr i32 <BYTE>ptr i32 <>ptr <>ptr -> i32');
111
- const out = new PtrArrayBuffer(16);
112
- // 显式给长度(cchWideChar=2)避免 -1 空终止语义下返回值是否含 '\0' 的版本差异。
113
- const n = wideCharToMultiByte(0 /* CP_ACP */, 0, 'AB', 2, out, 16, NULL, NULL);
114
- t.check('WideCharToMultiByte char count', 2, n);
115
- t.check('converted byte 0 = A', 65, new DataView(out).getUint8(0));
116
- t.check('converted byte 1 = B', 66, new DataView(out).getUint8(1));
117
- }
118
- else
119
- t.skipCase('WideCharToMultiByte missing');
120
- t.section('param count 12 (SetDIBitsToDevice on memory DC)');
121
- if (has(gdi, 'SetDIBitsToDevice') && has(gdi, 'CreateCompatibleDC') && has(gdi, 'DeleteDC')) {
122
- const createCompatibleDC = bind('gdi32.dll', 'CreateCompatibleDC', '<>ptr -> <>ptr');
123
- const deleteDC = bind('gdi32.dll', 'DeleteDC', '<>ptr -> i32');
124
- const setDIBitsToDevice = bind('gdi32.dll', 'SetDIBitsToDevice', '<>ptr i32 i32 u32 u32 i32 i32 u32 u32 <BYTE>ptr <BYTE>ptr u32 -> i32');
125
- const hdc = createCompatibleDC(0);
126
- if (hdc) {
127
- const bmi = new PtrArrayBuffer(40);
128
- const bdv = new DataView(bmi);
129
- bdv.setUint32(0, 40, true); // biSize
130
- bdv.setInt32(4, 1, true); // biWidth
131
- bdv.setInt32(8, 1, true); // biHeight (bottom-up)
132
- bdv.setUint16(12, 1, true); // biPlanes
133
- bdv.setUint16(14, 32, true); // biBitCount
134
- bdv.setUint32(16, 0, true); // biCompression = BI_RGB
135
- const bits = new PtrArrayBuffer(4);
136
- const lines = setDIBitsToDevice(hdc, 0, 0, 1, 1, 0, 0, 0, 1, bits, bmi, 0 /* DIB_RGB_COLORS */);
137
- t.check('SetDIBitsToDevice scanlines copied', 1, lines);
138
- deleteDC(hdc);
139
- }
140
- else
141
- t.skipCase('CreateCompatibleDC returned NULL');
142
- }
143
- else
144
- t.skipCase('SetDIBitsToDevice/CreateCompatibleDC missing');
145
- t.section('int/float mix: f64 i32 -> f64');
146
- if (has(crt, 'ldexp')) {
147
- const ldexp = bind('msvcrt.dll', 'ldexp', 'f64 i32 -> f64');
148
- t.check('ldexp(1.5, 3)', 12.0, ldexp(1.5, 3));
149
- }
150
- else
151
- t.skipCase('ldexp missing');
152
- t.section('int/float mix: f64 <BYTE>ptr -> f64');
153
- if (has(crt, 'frexp')) {
154
- const frexp = bind('msvcrt.dll', 'frexp', 'f64 <BYTE>ptr -> f64');
155
- const e = new PtrArrayBuffer(4);
156
- const m = frexp(12.0, e);
157
- t.check('frexp(12).mantissa', 0.75, m);
158
- t.check('frexp(12).exponent', 4, new DataView(e).getInt32(0, true));
159
- }
160
- else
161
- t.skipCase('frexp missing');
162
- if (has(crt, 'modf')) {
163
- const modf = bind('msvcrt.dll', 'modf', 'f64 <BYTE>ptr -> f64');
164
- const ip = new PtrArrayBuffer(8);
165
- const frac = modf(3.75, ip);
166
- t.check('modf(3.75).frac', 0.75, frac);
167
- t.check('modf(3.75).int', 3.0, new DataView(ip).getFloat64(0, true));
168
- }
169
- else
170
- t.skipCase('modf missing');
171
- t.section('narrow signed args (msvcrt!abs)');
172
- if (has(crt, 'abs')) {
173
- const absI8 = bind('msvcrt.dll', 'abs', 'i8 -> i32');
174
- const absU8 = bind('msvcrt.dll', 'abs', 'u8 -> i32');
175
- const absI16 = bind('msvcrt.dll', 'abs', 'i16 -> i32');
176
- const absU16 = bind('msvcrt.dll', 'abs', 'u16 -> i32');
177
- t.check('abs(i8 -128)', 128, absI8(-128));
178
- t.check('abs(i8 -5)', 5, absI8(-5));
179
- t.check('abs(u8 200)', 200, absU8(200));
180
- t.check('abs(i16 -300)', 300, absI16(-300));
181
- t.check('abs(u16 60000)', 60000, absU16(60000));
182
- }
183
- else
184
- t.skipCase('abs missing');
185
- t.section('negative return i32/i16/i8 (lstrcmpW)');
186
- if (has(k32, 'lstrcmpW')) {
187
- const cmpI32 = bind('kernel32.dll', 'lstrcmpW', '<WCHAR>ptr <WCHAR>ptr -> i32');
188
- const cmpI16 = bind('kernel32.dll', 'lstrcmpW', '<WCHAR>ptr <WCHAR>ptr -> i16');
189
- const cmpI8 = bind('kernel32.dll', 'lstrcmpW', '<WCHAR>ptr <WCHAR>ptr -> i8');
190
- t.checkTrue('lstrcmpW i32 < 0', cmpI32('a', 'b') < 0);
191
- t.checkTrue('lstrcmpW i16 < 0', cmpI16('a', 'b') < 0);
192
- t.checkTrue('lstrcmpW i8 < 0', cmpI8('a', 'b') < 0);
193
- }
194
- else
195
- t.skipCase('lstrcmpW missing');
196
- t.section('negative return i64 (InterlockedIncrement64)');
197
- if (has(k32, 'InterlockedExchange64') && has(k32, 'InterlockedIncrement64')) {
198
- const exchange64 = bind('kernel32.dll', 'InterlockedExchange64', '<BYTE>ptr i64 -> i64');
199
- const increment64 = bind('kernel32.dll', 'InterlockedIncrement64', '<BYTE>ptr -> i64');
200
- const cell = new PtrArrayBuffer(8);
201
- exchange64(cell, -2n);
202
- t.check('InterlockedIncrement64(-2)', -1n, increment64(cell));
203
- }
204
- else
205
- t.skipCase('Interlocked*64 missing');
206
- t.section('64-bit arg transport (InterlockedExchange64)');
207
- if (has(k32, 'InterlockedExchange64')) {
208
- const exchange64 = bind('kernel32.dll', 'InterlockedExchange64', '<BYTE>ptr i64 -> i64');
209
- const cell = new PtrArrayBuffer(8);
210
- const prev0 = exchange64(cell, 4294967297n); // 2^32+1:超过 32 位
211
- const prev1 = exchange64(cell, -1n); // 64 位全 1
212
- const prev2 = exchange64(cell, 0n);
213
- t.check('exchange empty cell returns 0', 0n, prev0);
214
- t.check('exchange returns 2^32+1', 4294967297n, prev1);
215
- t.check('exchange returns -1', -1n, prev2);
216
- }
217
- else
218
- t.skipCase('InterlockedExchange64 missing');
219
- t.section('ptr NULL/undefined');
220
- if (has(usr, 'GetDC') && has(usr, 'IsWindow')) {
221
- const getDC = bind('user32.dll', 'GetDC', '<>ptr -> <>ptr');
222
- const isWindow = bind('user32.dll', 'IsWindow', '<>ptr -> i32');
223
- t.checkTrue('GetDC(NULL) non-null', !!getDC(NULL));
224
- // undefined / null 不是指针合法值(指针只收 NULL 或有效地址),必须拒绝而非静默当 0。
225
- let errUndef = '';
226
- try {
227
- getDC(undefined);
228
- }
229
- catch (e) {
230
- errUndef = String(e);
231
- }
232
- t.checkTrue('GetDC(undefined) rejected', errUndef.includes('undefined'));
233
- t.check('IsWindow(NULL) = FALSE', 0, isWindow(NULL));
234
- }
235
- else
236
- t.skipCase('GetDC/IsWindow missing');
237
- t.section('by-value struct arg: <POINT> (slot value, not a pointer)');
238
- if (has(usr, 'PtInRect') && has(usr, 'WindowFromPoint') && has(usr, 'ChildWindowFromPointEx')) {
239
- // PtInRect(rect, pt):RECT 按指针、POINT 按值(x64 位置寄存器 / ia32 压 8 字节上栈)。
240
- // 非对称矩形 {0,0,10,100} —— 若 x/y 在打包中交换,(99,5) 与 (5,99) 的判定会互换,
241
- // 本段即可抓出「槽内是地址而非值」「字段顺序/位置错」两类 ABI 错误。
242
- const ptInRect = bind('user32.dll', 'PtInRect', '<RECT>ptr <POINT> -> i32', { RECT, POINT });
243
- const rectOut = RECT.alloc();
244
- const rdv = new DataView(rectOut);
245
- rdv.setInt32(8, 10, true); // right(left/top = 0,零填充)
246
- rdv.setInt32(12, 100, true); // bottom
247
- t.check('PtInRect inside (5,5)', 1, ptInRect(rectOut.ptr, { x: 5, y: 5 }));
248
- t.check('PtInRect inside (5,99)', 1, ptInRect(rectOut.ptr, { x: 5, y: 99 }));
249
- t.check('PtInRect outside (99,5) — swap-sensitive', 0, ptInRect(rectOut.ptr, { x: 99, y: 5 }));
250
- t.check('PtInRect outside x=10 (right edge exclusive)', 0, ptInRect(rectOut.ptr, { x: 10, y: 5 }));
251
- // 位置 1(首参即结构):WindowFromPoint(POINT) → 屏内必非 NULL
252
- const getSystemMetrics = bind('user32.dll', 'GetSystemMetrics', 'i32 -> i32');
253
- const cx = getSystemMetrics(0); // SM_CXSCREEN
254
- const cy = getSystemMetrics(1); // SM_CYSCREEN
255
- const windowFromPoint = bind('user32.dll', 'WindowFromPoint', '<POINT> -> <>ptr', { POINT });
256
- t.checkTrue('WindowFromPoint(screen center) non-null', !!windowFromPoint({ x: cx >> 1, y: cy >> 1 }));
257
- // 位置 2 + 其后参数推移(POINT 之后还有 UINT flags —— 槽宽算错会把 flags 读错位)
258
- const childFromPointEx = bind('user32.dll', 'ChildWindowFromPointEx', '<>ptr <POINT> u32 -> <>ptr', { POINT });
259
- const getDesktopWindow = bind('user32.dll', 'GetDesktopWindow', ' -> <>ptr');
260
- const desk = getDesktopWindow();
261
- t.checkTrue('ChildWindowFromPointEx inside → non-null', !!childFromPointEx(desk, { x: 1, y: 1 }, 0 /* CWP_ALL */));
262
- t.check('ChildWindowFromPointEx outside → NULL', 0, childFromPointEx(desk, { x: cx + 100, y: cy + 100 }, 0 /* CWP_ALL */));
263
- }
264
- else
265
- t.skipCase('PtInRect/WindowFromPoint/ChildWindowFromPointEx missing');
266
- t.section('by-value struct >8B / odd size: x64 by-reference (caller temp)');
267
- if (os.arch === 'x64' && has(crt, 'memcpy')) {
268
- // MS x64:size ∉ {1,2,4,8} 的聚合体按引用传 —— 槽内是指向 caller 分配的
269
- // 16 字节对齐临时副本的指针(单参数永不跨寄存器)。memcpy 第 2 参语义即
270
- // 「源地址」,借它白盒验证整条 by-ref 路径:若槽内不是合法副本地址、或
271
- // 字节未写进副本,拷回内容必错。ia32 按值恒把字节压栈(callee 读地址会
272
- // 拿到垃圾值),故本段 x64 限定;ia32 侧由上面真实 API 段覆盖栈上传值。
273
- const BYVAL12 = struct('BYVAL12', { a: 'u32', b: 'u32', c: 'u32' });
274
- const BYVAL3 = struct('BYVAL3', { p: 'u8', q: 'u8', r: 'u8' });
275
- const cp12 = bind('msvcrt.dll', 'memcpy', '<BYTE>ptr <BYVAL12> u32 -> <>ptr', { BYVAL12 });
276
- const cp3 = bind('msvcrt.dll', 'memcpy', '<BYTE>ptr <BYVAL3> u32 -> <>ptr', { BYVAL3 });
277
- const d12 = new PtrArrayBuffer(12);
278
- cp12(d12, { a: 1, b: 0x11223344, c: 0x7fffffff }, 12);
279
- const dv12 = new DataView(d12);
280
- t.check('12B (>8B) by-ref word 0', 1, dv12.getUint32(0, true));
281
- t.check('12B (>8B) by-ref word 1', 0x11223344, dv12.getUint32(4, true));
282
- t.check('12B (>8B) by-ref word 2', 0x7fffffff, dv12.getUint32(8, true));
283
- const d3 = new PtrArrayBuffer(3);
284
- cp3(d3, { p: 0xaa, q: 0xbb, r: 0xcc }, 3);
285
- t.check('3B (odd size) by-ref bytes', 'AA,BB,CC', Array.from(new Uint8Array(d3)).map(x => x.toString(16).toUpperCase()).join(','));
286
- }
287
- else
288
- t.skipCase('by-ref path: x64-only whitebox (ia32 pushes value bytes verbatim)');
289
- },
290
- };
@@ -1,274 +0,0 @@
1
- import * as std from 'std'
2
- import * as ffi from 'ffi'
3
- import * as os from 'os'
4
- import { bind } from '../lib/ffi/bind.js'
5
- import { struct } from '../lib/ffi/struct.js'
6
- import { NULL, PtrArrayBuffer, type MaybePtr } from '../lib/ffi/ctype.js'
7
- import { POINT, RECT } from '../lib/windows/structs.js'
8
- import { Tester } from './test_helper.js'
9
-
10
- // ABI 边界回归:覆盖 REMOVE_LIBFFI_PLAN.md §4.2 的维度,全部用真实 Win32/CRT
11
- // 函数(不新增测试专用导出)。缺函数的平台记 SKIP 而非 FAIL,避免环境差异误报。
12
- //
13
- // 维度 → 用例:
14
- // 参数个数 0/1/3/4/5/6/8/12:GetTickCount/fabs/memcpy/GetLocaleInfoA/
15
- // SetRect/CompareStringW/WideCharToMultiByte/SetDIBitsToDevice
16
- // 整浮混合:ldexp(f64,i32)、frexp(f64,ptr)、modf(f64,ptr)
17
- // 前 4 全整数(RCX-R9):GetLocaleInfoA 的 4 个参数
18
- // 负数返回 i32/i16/i8:lstrcmpW;i64:InterlockedIncrement64
19
- // 窄整型负值传参:abs(i8/i16/u8/u16)
20
- // 大 64 位传参:InterlockedExchange64
21
- // 指针 NULL:GetDC(NULL)、IsWindow(NULL);null/undefined 不是指针合法值,应拒绝
22
-
23
- function readAscii(buf: ArrayBuffer, len: number): string {
24
- const dv = new DataView(buf)
25
- let s = ''
26
- for (let i = 0; i < len; i++) {
27
- const c = dv.getUint8(i)
28
- if (c === 0) break
29
- s += String.fromCharCode(c)
30
- }
31
- return s
32
- }
33
-
34
- function has(dll: MaybePtr<'HMODULE'>, name: string): boolean {
35
- return !!dll && !!ffi.GetProcAddress(dll, name)
36
- }
37
-
38
- export const suite = {
39
- name: 'ffi-abi',
40
- run: (t: Tester) => {
41
- const k32 = ffi.LoadLibrary('kernel32.dll')
42
- const usr = ffi.LoadLibrary('user32.dll')
43
- const gdi = ffi.LoadLibrary('gdi32.dll')
44
- const crt = ffi.LoadLibrary('msvcrt.dll')
45
-
46
- t.section('param count 0 ( -> u32)')
47
- if (has(k32, 'GetTickCount')) {
48
- const getTickCount = bind('kernel32.dll', 'GetTickCount', ' -> u32')
49
- const tc = getTickCount()
50
- t.checkTrue('GetTickCount() > 0', tc > 0)
51
- } else t.skipCase('GetTickCount missing')
52
-
53
- t.section('param count 1 (f64 -> f64)')
54
- if (has(crt, 'fabs')) {
55
- const fabs = bind('msvcrt.dll', 'fabs', 'f64 -> f64')
56
- t.check('fabs(-3.5)', 3.5, fabs(-3.5))
57
- } else t.skipCase('fabs missing')
58
-
59
- t.section('param count 3 (<BYTE>ptr <BYTE>ptr u32 -> ptr)')
60
- if (has(crt, 'memcpy')) {
61
- const memcpy = bind('msvcrt.dll', 'memcpy', '<BYTE>ptr <BYTE>ptr u32 -> <>ptr')
62
- const srcB = new PtrArrayBuffer(8)
63
- const src = new Uint8Array(srcB)
64
- src.set([1, 2, 3, 4, 5, 6, 7, 8])
65
- const dstB = new PtrArrayBuffer(8)
66
- const dst = new Uint8Array(dstB)
67
- const r = memcpy(dstB, srcB, 8)
68
- const rd = Array.from(dst).join(',')
69
- t.check('memcpy copied bytes', '1,2,3,4,5,6,7,8', rd)
70
- t.checkTrue('memcpy returns dest ptr', typeof r === 'number' && r !== 0)
71
- } else t.skipCase('memcpy missing')
72
-
73
- t.section('param count 4 + all-int-regs (u32 u32 <BYTE>ptr i32 -> i32)')
74
- if (has(k32, 'GetLocaleInfoA')) {
75
- const getLocaleInfoA = bind('kernel32.dll', 'GetLocaleInfoA', 'u32 u32 <BYTE>ptr i32 -> i32')
76
- const buf = new PtrArrayBuffer(64)
77
- const n = getLocaleInfoA(0x0409, 0x00001001 /* LOCALE_SENGLANGUAGE */, buf, 64)
78
- if (n > 0) {
79
- const s = readAscii(buf, n)
80
- std.printf(' GetLocaleInfoA(0x0409, LOCALE_SENGLANGUAGE) = "%s" (%d)\n', s, n)
81
- t.check('en-US language name', 'English', s)
82
- } else t.skipCase('en-US locale unavailable')
83
- } else t.skipCase('GetLocaleInfoA missing')
84
-
85
- t.section('param count 5 (<BYTE>ptr i32 i32 i32 i32 -> i32)')
86
- if (has(usr, 'SetRect')) {
87
- const setRect = bind('user32.dll', 'SetRect', '<BYTE>ptr i32 i32 i32 i32 -> i32')
88
- const rc = new PtrArrayBuffer(16)
89
- const ok = setRect(rc, 1, 2, 30, 40)
90
- const dv = new DataView(rc)
91
- t.checkTrue('SetRect returns nonzero', ok !== 0)
92
- t.check('SetRect left', 1, dv.getInt32(0, true))
93
- t.check('SetRect top', 2, dv.getInt32(4, true))
94
- t.check('SetRect right', 30, dv.getInt32(8, true))
95
- t.check('SetRect bottom', 40, dv.getInt32(12, true))
96
- } else t.skipCase('SetRect missing')
97
-
98
- t.section('param count 6 (u32 u32 <WCHAR>ptr i32 <WCHAR>ptr i32 -> i32)')
99
- if (has(k32, 'CompareStringW')) {
100
- const compareStringW = bind('kernel32.dll', 'CompareStringW', 'u32 u32 <WCHAR>ptr i32 <WCHAR>ptr i32 -> i32')
101
- const r = compareStringW(0x0400 /* LOCALE_USER_DEFAULT */, 0, 'abc', -1, 'abd', -1)
102
- t.check('CompareStringW("abc","abd") = CSTR_LESS_THAN', 1, r)
103
- } else t.skipCase('CompareStringW missing')
104
-
105
- t.section('param count 8 (u32 u32 <WCHAR>ptr i32 <BYTE>ptr i32 <>ptr <>ptr -> i32)')
106
- if (has(k32, 'WideCharToMultiByte')) {
107
- const wideCharToMultiByte = bind('kernel32.dll', 'WideCharToMultiByte', 'u32 u32 <WCHAR>ptr i32 <BYTE>ptr i32 <>ptr <>ptr -> i32')
108
- const out = new PtrArrayBuffer(16)
109
- // 显式给长度(cchWideChar=2)避免 -1 空终止语义下返回值是否含 '\0' 的版本差异。
110
- const n = wideCharToMultiByte(0 /* CP_ACP */, 0, 'AB', 2, out, 16, NULL, NULL)
111
- t.check('WideCharToMultiByte char count', 2, n)
112
- t.check('converted byte 0 = A', 65, new DataView(out).getUint8(0))
113
- t.check('converted byte 1 = B', 66, new DataView(out).getUint8(1))
114
- } else t.skipCase('WideCharToMultiByte missing')
115
-
116
- t.section('param count 12 (SetDIBitsToDevice on memory DC)')
117
- if (has(gdi, 'SetDIBitsToDevice') && has(gdi, 'CreateCompatibleDC') && has(gdi, 'DeleteDC')) {
118
- const createCompatibleDC = bind('gdi32.dll', 'CreateCompatibleDC', '<>ptr -> <>ptr')
119
- const deleteDC = bind('gdi32.dll', 'DeleteDC', '<>ptr -> i32')
120
- const setDIBitsToDevice = bind('gdi32.dll', 'SetDIBitsToDevice',
121
- '<>ptr i32 i32 u32 u32 i32 i32 u32 u32 <BYTE>ptr <BYTE>ptr u32 -> i32')
122
- const hdc = createCompatibleDC(0)
123
- if (hdc) {
124
- const bmi = new PtrArrayBuffer(40)
125
- const bdv = new DataView(bmi)
126
- bdv.setUint32(0, 40, true) // biSize
127
- bdv.setInt32(4, 1, true) // biWidth
128
- bdv.setInt32(8, 1, true) // biHeight (bottom-up)
129
- bdv.setUint16(12, 1, true) // biPlanes
130
- bdv.setUint16(14, 32, true) // biBitCount
131
- bdv.setUint32(16, 0, true) // biCompression = BI_RGB
132
- const bits = new PtrArrayBuffer(4)
133
- const lines = setDIBitsToDevice(hdc, 0, 0, 1, 1, 0, 0, 0, 1, bits, bmi, 0 /* DIB_RGB_COLORS */)
134
- t.check('SetDIBitsToDevice scanlines copied', 1, lines)
135
- deleteDC(hdc)
136
- } else t.skipCase('CreateCompatibleDC returned NULL')
137
- } else t.skipCase('SetDIBitsToDevice/CreateCompatibleDC missing')
138
-
139
- t.section('int/float mix: f64 i32 -> f64')
140
- if (has(crt, 'ldexp')) {
141
- const ldexp = bind('msvcrt.dll', 'ldexp', 'f64 i32 -> f64')
142
- t.check('ldexp(1.5, 3)', 12.0, ldexp(1.5, 3))
143
- } else t.skipCase('ldexp missing')
144
-
145
- t.section('int/float mix: f64 <BYTE>ptr -> f64')
146
- if (has(crt, 'frexp')) {
147
- const frexp = bind('msvcrt.dll', 'frexp', 'f64 <BYTE>ptr -> f64')
148
- const e = new PtrArrayBuffer(4)
149
- const m = frexp(12.0, e)
150
- t.check('frexp(12).mantissa', 0.75, m)
151
- t.check('frexp(12).exponent', 4, new DataView(e).getInt32(0, true))
152
- } else t.skipCase('frexp missing')
153
- if (has(crt, 'modf')) {
154
- const modf = bind('msvcrt.dll', 'modf', 'f64 <BYTE>ptr -> f64')
155
- const ip = new PtrArrayBuffer(8)
156
- const frac = modf(3.75, ip)
157
- t.check('modf(3.75).frac', 0.75, frac)
158
- t.check('modf(3.75).int', 3.0, new DataView(ip).getFloat64(0, true))
159
- } else t.skipCase('modf missing')
160
-
161
- t.section('narrow signed args (msvcrt!abs)')
162
- if (has(crt, 'abs')) {
163
- const absI8 = bind('msvcrt.dll', 'abs', 'i8 -> i32')
164
- const absU8 = bind('msvcrt.dll', 'abs', 'u8 -> i32')
165
- const absI16 = bind('msvcrt.dll', 'abs', 'i16 -> i32')
166
- const absU16 = bind('msvcrt.dll', 'abs', 'u16 -> i32')
167
- t.check('abs(i8 -128)', 128, absI8(-128))
168
- t.check('abs(i8 -5)', 5, absI8(-5))
169
- t.check('abs(u8 200)', 200, absU8(200))
170
- t.check('abs(i16 -300)', 300, absI16(-300))
171
- t.check('abs(u16 60000)', 60000, absU16(60000))
172
- } else t.skipCase('abs missing')
173
-
174
- t.section('negative return i32/i16/i8 (lstrcmpW)')
175
- if (has(k32, 'lstrcmpW')) {
176
- const cmpI32 = bind('kernel32.dll', 'lstrcmpW', '<WCHAR>ptr <WCHAR>ptr -> i32')
177
- const cmpI16 = bind('kernel32.dll', 'lstrcmpW', '<WCHAR>ptr <WCHAR>ptr -> i16')
178
- const cmpI8 = bind('kernel32.dll', 'lstrcmpW', '<WCHAR>ptr <WCHAR>ptr -> i8')
179
- t.checkTrue('lstrcmpW i32 < 0', cmpI32('a', 'b') < 0)
180
- t.checkTrue('lstrcmpW i16 < 0', cmpI16('a', 'b') < 0)
181
- t.checkTrue('lstrcmpW i8 < 0', cmpI8('a', 'b') < 0)
182
- } else t.skipCase('lstrcmpW missing')
183
-
184
- t.section('negative return i64 (InterlockedIncrement64)')
185
- if (has(k32, 'InterlockedExchange64') && has(k32, 'InterlockedIncrement64')) {
186
- const exchange64 = bind('kernel32.dll', 'InterlockedExchange64', '<BYTE>ptr i64 -> i64')
187
- const increment64 = bind('kernel32.dll', 'InterlockedIncrement64', '<BYTE>ptr -> i64')
188
- const cell = new PtrArrayBuffer(8)
189
- exchange64(cell, -2n)
190
- t.check('InterlockedIncrement64(-2)', -1n, increment64(cell))
191
- } else t.skipCase('Interlocked*64 missing')
192
-
193
- t.section('64-bit arg transport (InterlockedExchange64)')
194
- if (has(k32, 'InterlockedExchange64')) {
195
- const exchange64 = bind('kernel32.dll', 'InterlockedExchange64', '<BYTE>ptr i64 -> i64')
196
- const cell = new PtrArrayBuffer(8)
197
- const prev0 = exchange64(cell, 4294967297n) // 2^32+1:超过 32 位
198
- const prev1 = exchange64(cell, -1n) // 64 位全 1
199
- const prev2 = exchange64(cell, 0n)
200
- t.check('exchange empty cell returns 0', 0n, prev0)
201
- t.check('exchange returns 2^32+1', 4294967297n, prev1)
202
- t.check('exchange returns -1', -1n, prev2)
203
- } else t.skipCase('InterlockedExchange64 missing')
204
-
205
- t.section('ptr NULL/undefined')
206
- if (has(usr, 'GetDC') && has(usr, 'IsWindow')) {
207
- const getDC = bind('user32.dll', 'GetDC', '<>ptr -> <>ptr')
208
- const isWindow = bind('user32.dll', 'IsWindow', '<>ptr -> i32')
209
- t.checkTrue('GetDC(NULL) non-null', !!getDC(NULL))
210
- // undefined / null 不是指针合法值(指针只收 NULL 或有效地址),必须拒绝而非静默当 0。
211
- let errUndef = ''
212
- try {
213
- (getDC as unknown as (v: unknown) => number)(undefined)
214
- } catch (e) {
215
- errUndef = String(e)
216
- }
217
- t.checkTrue('GetDC(undefined) rejected', errUndef.includes('undefined'))
218
- t.check('IsWindow(NULL) = FALSE', 0, isWindow(NULL))
219
- } else t.skipCase('GetDC/IsWindow missing')
220
-
221
- t.section('by-value struct arg: <POINT> (slot value, not a pointer)')
222
- if (has(usr, 'PtInRect') && has(usr, 'WindowFromPoint') && has(usr, 'ChildWindowFromPointEx')) {
223
- // PtInRect(rect, pt):RECT 按指针、POINT 按值(x64 位置寄存器 / ia32 压 8 字节上栈)。
224
- // 非对称矩形 {0,0,10,100} —— 若 x/y 在打包中交换,(99,5) 与 (5,99) 的判定会互换,
225
- // 本段即可抓出「槽内是地址而非值」「字段顺序/位置错」两类 ABI 错误。
226
- const ptInRect = bind('user32.dll', 'PtInRect', '<RECT>ptr <POINT> -> i32', { RECT, POINT })
227
- const rectOut = RECT.alloc()
228
- const rdv = new DataView(rectOut)
229
- rdv.setInt32(8, 10, true) // right(left/top = 0,零填充)
230
- rdv.setInt32(12, 100, true) // bottom
231
- t.check('PtInRect inside (5,5)', 1, ptInRect(rectOut.ptr, { x: 5, y: 5 }))
232
- t.check('PtInRect inside (5,99)', 1, ptInRect(rectOut.ptr, { x: 5, y: 99 }))
233
- t.check('PtInRect outside (99,5) — swap-sensitive', 0, ptInRect(rectOut.ptr, { x: 99, y: 5 }))
234
- t.check('PtInRect outside x=10 (right edge exclusive)', 0, ptInRect(rectOut.ptr, { x: 10, y: 5 }))
235
-
236
- // 位置 1(首参即结构):WindowFromPoint(POINT) → 屏内必非 NULL
237
- const getSystemMetrics = bind('user32.dll', 'GetSystemMetrics', 'i32 -> i32')
238
- const cx = getSystemMetrics(0) // SM_CXSCREEN
239
- const cy = getSystemMetrics(1) // SM_CYSCREEN
240
- const windowFromPoint = bind('user32.dll', 'WindowFromPoint', '<POINT> -> <>ptr', { POINT })
241
- t.checkTrue('WindowFromPoint(screen center) non-null', !!windowFromPoint({ x: cx >> 1, y: cy >> 1 }))
242
-
243
- // 位置 2 + 其后参数推移(POINT 之后还有 UINT flags —— 槽宽算错会把 flags 读错位)
244
- const childFromPointEx = bind('user32.dll', 'ChildWindowFromPointEx', '<>ptr <POINT> u32 -> <>ptr', { POINT })
245
- const getDesktopWindow = bind('user32.dll', 'GetDesktopWindow', ' -> <>ptr')
246
- const desk = getDesktopWindow()
247
- t.checkTrue('ChildWindowFromPointEx inside → non-null', !!childFromPointEx(desk, { x: 1, y: 1 }, 0 /* CWP_ALL */))
248
- t.check('ChildWindowFromPointEx outside → NULL', 0, childFromPointEx(desk, { x: cx + 100, y: cy + 100 }, 0 /* CWP_ALL */))
249
- } else t.skipCase('PtInRect/WindowFromPoint/ChildWindowFromPointEx missing')
250
-
251
- t.section('by-value struct >8B / odd size: x64 by-reference (caller temp)')
252
- if (os.arch === 'x64' && has(crt, 'memcpy')) {
253
- // MS x64:size ∉ {1,2,4,8} 的聚合体按引用传 —— 槽内是指向 caller 分配的
254
- // 16 字节对齐临时副本的指针(单参数永不跨寄存器)。memcpy 第 2 参语义即
255
- // 「源地址」,借它白盒验证整条 by-ref 路径:若槽内不是合法副本地址、或
256
- // 字节未写进副本,拷回内容必错。ia32 按值恒把字节压栈(callee 读地址会
257
- // 拿到垃圾值),故本段 x64 限定;ia32 侧由上面真实 API 段覆盖栈上传值。
258
- const BYVAL12 = struct('BYVAL12', { a: 'u32', b: 'u32', c: 'u32' })
259
- const BYVAL3 = struct('BYVAL3', { p: 'u8', q: 'u8', r: 'u8' })
260
- const cp12 = bind('msvcrt.dll', 'memcpy', '<BYTE>ptr <BYVAL12> u32 -> <>ptr', { BYVAL12 })
261
- const cp3 = bind('msvcrt.dll', 'memcpy', '<BYTE>ptr <BYVAL3> u32 -> <>ptr', { BYVAL3 })
262
- const d12 = new PtrArrayBuffer(12)
263
- cp12(d12, { a: 1, b: 0x11223344, c: 0x7fffffff }, 12)
264
- const dv12 = new DataView(d12)
265
- t.check('12B (>8B) by-ref word 0', 1, dv12.getUint32(0, true))
266
- t.check('12B (>8B) by-ref word 1', 0x11223344, dv12.getUint32(4, true))
267
- t.check('12B (>8B) by-ref word 2', 0x7fffffff, dv12.getUint32(8, true))
268
- const d3 = new PtrArrayBuffer(3)
269
- cp3(d3, { p: 0xaa, q: 0xbb, r: 0xcc }, 3)
270
- t.check('3B (odd size) by-ref bytes', 'AA,BB,CC',
271
- Array.from(new Uint8Array(d3)).map(x => x.toString(16).toUpperCase()).join(','))
272
- } else t.skipCase('by-ref path: x64-only whitebox (ia32 pushes value bytes verbatim)')
273
- },
274
- }