quickwin 3.0.1 → 3.0.3

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 (163) hide show
  1. package/README.en.md +3 -0
  2. package/README.md +2 -0
  3. package/package.json +2 -3
  4. package/qwin-nowasm-x86.exe +0 -0
  5. package/qwin-nowasm.exe +0 -0
  6. package/qwin-x86.exe +0 -0
  7. package/qwin.exe +0 -0
  8. package/examples/PdfCanvas.js +0 -38
  9. package/examples/PdfCanvas.tsx +0 -54
  10. package/examples/execd/main.js +0 -23379
  11. package/examples/execd/tray.js +0 -192
  12. package/examples/execd/worker.js +0 -122
  13. package/examples/pdf_preview2.js +0 -398
  14. package/examples/pdf_preview2.ts +0 -353
  15. package/examples/pdf_viewer.js +0 -22515
  16. package/examples/pdf_viewer.tsx +0 -228
  17. package/examples/test_gallery.js +0 -18692
  18. package/examples/test_gallery.tsx +0 -291
  19. package/examples/test_react_lazy.js +0 -18560
  20. package/examples/test_react_lazy.tsx +0 -65
  21. package/examples/test_react_listbox.js +0 -18562
  22. package/examples/test_react_listbox.tsx +0 -73
  23. package/examples/test_root_window.js +0 -18532
  24. package/examples/test_root_window.tsx +0 -41
  25. package/examples/test_tab.js +0 -18563
  26. package/examples/test_tab.tsx +0 -77
  27. package/examples/tray_demo.js +0 -86
  28. package/examples/tray_demo.tsx +0 -89
  29. package/test/add.wasm +0 -0
  30. package/test/bench_ffi_callraw.js +0 -235
  31. package/test/bench_ffi_callraw.ts +0 -235
  32. package/test/bench_ffi_overhead.js +0 -139
  33. package/test/bench_ffi_overhead.ts +0 -141
  34. package/test/bench_sendmessage.js +0 -104
  35. package/test/bench_sendmessage.ts +0 -100
  36. package/test/bench_sendmessage_nonnull.js +0 -187
  37. package/test/bench_sendmessage_nonnull.ts +0 -188
  38. package/test/complex.wasm +0 -0
  39. package/test/complex_imports.wasm +0 -0
  40. package/test/debug/http_server_e2e.js +0 -31
  41. package/test/debug/minwasm.js +0 -52
  42. package/test/debug/mupdf_debug.js +0 -86
  43. package/test/debug/wasm.js +0 -122
  44. package/test/debug/wasm_export_debug.js +0 -54
  45. package/test/debug/wasm_names.js +0 -36
  46. package/test/example.pdf +0 -0
  47. package/test/global_imports.wasm +0 -0
  48. package/test/import_func.wasm +0 -0
  49. package/test/imports.wasm +0 -0
  50. package/test/mupdf-cdn.js +0 -40
  51. package/test/mupdf-cdn.ts +0 -43
  52. package/test/run.js +0 -111
  53. package/test/run.ts +0 -116
  54. package/test/sjlj.wasm +0 -0
  55. package/test/test_basic.js +0 -7
  56. package/test/test_basic.ts +0 -9
  57. package/test/test_basic_fetch.js +0 -63
  58. package/test/test_basic_fetch.ts +0 -59
  59. package/test/test_brotli.js +0 -48
  60. package/test/test_brotli.ts +0 -52
  61. package/test/test_fetch_cache.js +0 -169
  62. package/test/test_fetch_cache.ts +0 -187
  63. package/test/test_ffi.js +0 -157
  64. package/test/test_ffi.ts +0 -170
  65. package/test/test_ffi_abi.js +0 -290
  66. package/test/test_ffi_abi.ts +0 -274
  67. package/test/test_ffi_array.js +0 -116
  68. package/test/test_ffi_array.ts +0 -141
  69. package/test/test_ffi_bind.js +0 -656
  70. package/test/test_ffi_bind.ts +0 -670
  71. package/test/test_ffi_struct.js +0 -779
  72. package/test/test_ffi_struct.ts +0 -828
  73. package/test/test_ffi_struct_layout.js +0 -495
  74. package/test/test_ffi_struct_layout.ts +0 -543
  75. package/test/test_ffi_user32.js +0 -103
  76. package/test/test_ffi_user32.ts +0 -113
  77. package/test/test_ffi_windows.js +0 -172
  78. package/test/test_ffi_windows.ts +0 -187
  79. package/test/test_files.js +0 -102
  80. package/test/test_files.ts +0 -121
  81. package/test/test_frame_encoding.js +0 -128
  82. package/test/test_frame_encoding.ts +0 -132
  83. package/test/test_gdi_draw.js +0 -681
  84. package/test/test_gdi_draw.ts +0 -725
  85. package/test/test_helper.js +0 -91
  86. package/test/test_helper.ts +0 -88
  87. package/test/test_http_import.js +0 -96
  88. package/test/test_http_import.ts +0 -93
  89. package/test/test_http_server.js +0 -214
  90. package/test/test_http_server.ts +0 -211
  91. package/test/test_import_dir.js +0 -4
  92. package/test/test_import_dir.ts +0 -5
  93. package/test/test_ipv6.js +0 -134
  94. package/test/test_ipv6.ts +0 -130
  95. package/test/test_jsx.js +0 -1314
  96. package/test/test_meta_url.js +0 -3
  97. package/test/test_meta_url.ts +0 -4
  98. package/test/test_mupdf_render.js +0 -51
  99. package/test/test_mupdf_render.ts +0 -55
  100. package/test/test_mupdf_twice.js +0 -62
  101. package/test/test_mupdf_twice.ts +0 -65
  102. package/test/test_mupdf_wasm.js +0 -39
  103. package/test/test_mupdf_wasm.ts +0 -32
  104. package/test/test_net_event.js +0 -57
  105. package/test/test_net_event.ts +0 -56
  106. package/test/test_net_fetch.js +0 -384
  107. package/test/test_net_fetch.ts +0 -373
  108. package/test/test_net_websocket.js +0 -180
  109. package/test/test_net_websocket.ts +0 -164
  110. package/test/test_polyfill.js +0 -144
  111. package/test/test_polyfill.ts +0 -159
  112. package/test/test_printer.js +0 -137
  113. package/test/test_printer.ts +0 -148
  114. package/test/test_proc.js +0 -141
  115. package/test/test_proc.ts +0 -163
  116. package/test/test_react_complex.js +0 -18678
  117. package/test/test_react_counter.js +0 -18555
  118. package/test/test_react_flex.js +0 -18871
  119. package/test/test_react_hidden.js +0 -18560
  120. package/test/test_react_listview_link.js +0 -18590
  121. package/test/test_react_notify.js +0 -18797
  122. package/test/test_react_render.js +0 -18550
  123. package/test/test_registry.js +0 -74
  124. package/test/test_registry.ts +0 -95
  125. package/test/test_sysinfo.js +0 -122
  126. package/test/test_sysinfo.ts +0 -141
  127. package/test/test_url.js +0 -173
  128. package/test/test_url.ts +0 -183
  129. package/test/test_wasm_basic.js +0 -85
  130. package/test/test_wasm_basic.ts +0 -73
  131. package/test/test_wasm_import_global.js +0 -41
  132. package/test/test_wasm_import_global.ts +0 -45
  133. package/test/test_wasm_sjlj.js +0 -145
  134. package/test/test_wasm_sjlj.ts +0 -125
  135. package/test/test_wasm_types.js +0 -97
  136. package/test/test_wasm_types.ts +0 -124
  137. package/test/test_wolfssl_read.js +0 -203
  138. package/test/test_wolfssl_read.ts +0 -184
  139. package/test/test_worker.js +0 -21
  140. package/test/test_worker.ts +0 -24
  141. package/test/test_worker_concurrent.js +0 -45
  142. package/test/test_worker_concurrent.ts +0 -56
  143. package/test/test_worker_data_url.js +0 -46
  144. package/test/test_worker_data_url.ts +0 -51
  145. package/test/test_worker_fetch.js +0 -26
  146. package/test/test_worker_fetch.ts +0 -27
  147. package/test/test_worker_net.js +0 -69
  148. package/test/test_worker_net.ts +0 -66
  149. package/test/test_worker_wasm_concurrent.js +0 -44
  150. package/test/test_worker_wasm_concurrent.ts +0 -45
  151. package/test/types.wasm +0 -0
  152. package/test/worker_fetch.js +0 -13
  153. package/test/worker_fetch.ts +0 -13
  154. package/test/worker_helper.js +0 -1
  155. package/test/worker_helper.ts +0 -1
  156. package/test/worker_http.js +0 -18
  157. package/test/worker_http.ts +0 -20
  158. package/test/worker_import_lib.js +0 -12
  159. package/test/worker_import_lib.ts +0 -13
  160. package/test/worker_module.js +0 -16
  161. package/test/worker_module.ts +0 -17
  162. package/test/worker_wasm_concurrent.js +0 -22
  163. package/test/worker_wasm_concurrent.ts +0 -22
@@ -1,77 +0,0 @@
1
- import '../lib/polyfill.js'
2
- import * as gui from 'gui'
3
- import { useState } from 'react'
4
- import { render, Button, Tab } from '../lib/react-qw/index.js'
5
- import { ShowWindowCmd, WindowStyle, WmMsg } from '../constants'
6
- import { PostQuitMessage, ShowWindow } from '../lib/windows/user32.js'
7
-
8
- gui.RegisterClass('TabDemo', (hwnd, msg, wParam, lParam) => {
9
- if (!hwnd) return gui.DefWindowProc(hwnd, msg, wParam, lParam)
10
- if (msg === WmMsg.DESTROY) {
11
- PostQuitMessage(0)
12
- return 0
13
- }
14
- return gui.DefWindowProc(hwnd, msg, wParam, lParam)
15
- })
16
-
17
- function App() {
18
- const [tabIndex, setTabIndex] = useState(0)
19
-
20
- return (
21
- <w type="STATIC"
22
- ws={WindowStyle.VISIBLE}
23
- style={{ flexDirection: 'column', width: 600, height: 400, padding: 10 }}
24
- >
25
- <Tab
26
- tabs={[
27
- {
28
- title: 'Counter',
29
- content: (
30
- <w type="STATIC"
31
- ws={WindowStyle.VISIBLE | WindowStyle.CLIPCHILDREN}
32
- style={{ flexDirection: 'column', gap: 10, padding: 10, width: 580, height: 300 }}>
33
- <Button onClick={() => setTabIndex(1)}>Go to Tab 2</Button>
34
- </w>
35
- ),
36
- },
37
- {
38
- title: 'Buttons',
39
- content: (
40
- <w type="STATIC"
41
- ws={WindowStyle.VISIBLE | WindowStyle.CLIPCHILDREN}
42
- style={{ flexDirection: 'row', gap: 10, padding: 10, width: 580, height: 300, alignItems: 'stretch' }}>
43
- <Button onClick={() => setTabIndex(2)}>Go to Tab 3</Button>
44
- <Button onClick={() => setTabIndex(0)} style={{ width: 120 }}>Back</Button>
45
- </w>
46
- ),
47
- },
48
- {
49
- title: 'Final',
50
- content: (
51
- <w type="STATIC"
52
- ws={WindowStyle.VISIBLE | WindowStyle.CLIPCHILDREN}
53
- style={{ flexDirection: 'column', gap: 10, padding: 10, width: 580, height: 300 }}>
54
- <Button onClick={() => setTabIndex(0)}>Back to Tab 1</Button>
55
- <Button onClick={() => setTabIndex(1)}>Back to Tab 2</Button>
56
- </w>
57
- ),
58
- },
59
- ]}
60
- style={{ flexGrow: 1 }}
61
- selectedIndex={tabIndex}
62
- onChange={(i) => setTabIndex(i)}
63
- />
64
- </w>
65
- )
66
- }
67
-
68
- const hwnd = gui.CreateWindow(
69
- 'TabDemo', 'Tab Demo',
70
- WindowStyle.OVERLAPPEDWINDOW,
71
- 100, 100, 640, 480, null, null
72
- )
73
-
74
- if (hwnd) {
75
- ShowWindow(hwnd, ShowWindowCmd.SHOWNORMAL)
76
- render(<App />, hwnd)
77
- }
@@ -1,86 +0,0 @@
1
- import * as gui from 'gui';
2
- import * as std from 'std';
3
- import { NULL } from '../lib/ffi/ctype.js';
4
- import { GetCursorPos, CreatePopupMenu, AppendMenu, TrackPopupMenu, DestroyMenu, GetSystemMetrics, LoadImageOrdinal, PostQuitMessage, SetForegroundWindow, ShowWindow } from '../lib/windows/user32.js';
5
- import { NOTIFYICONDATAW, POINT } from '../lib/windows/structs.js';
6
- import { ShellNotifyIcon } from '../lib/windows/shell32.js';
7
- import { createSystemDpiFont } from '../lib/dpi.js';
8
- const WM_TRAY = 0x8001;
9
- gui.RegisterClass('TrayApp', (hwnd, msg, wParam, lParam) => {
10
- if (msg === WM_TRAY) {
11
- const evt = lParam;
12
- if (evt === 513 /* WmMsg.LBUTTONDOWN */) {
13
- ShowWindow(hwnd, 5 /* ShowWindowCmd.SHOW */);
14
- SetForegroundWindow(hwnd);
15
- }
16
- else if (evt === 516 /* WmMsg.RBUTTONDOWN */ || evt === 123 /* WmMsg.CONTEXTMENU */) {
17
- SetForegroundWindow(hwnd);
18
- const pt = POINT.alloc();
19
- const pos = GetCursorPos(pt.ptr) ? POINT.decode(pt) : null;
20
- const x = pos ? pos.x : 0;
21
- const y = pos ? pos.y : 0;
22
- const hMenu = CreatePopupMenu();
23
- if (hMenu !== NULL) {
24
- AppendMenu(hMenu, 0, 1, '显示窗口');
25
- AppendMenu(hMenu, 2048 /* MenuFlag.SEPARATOR */, 0, NULL);
26
- AppendMenu(hMenu, 0, 2, '退出');
27
- const cmd = TrackPopupMenu(hMenu, 256 /* TrackPopupMenuFlag.RETURNCMD */ | 2 /* TrackPopupMenuFlag.RIGHTBUTTON */, x, y, 0, hwnd, NULL);
28
- DestroyMenu(hMenu);
29
- if (cmd === 1) {
30
- ShowWindow(hwnd, 5 /* ShowWindowCmd.SHOW */);
31
- }
32
- else if (cmd === 2) {
33
- ShellNotifyIcon(2 /* NotifyIconCmd.DELETE */, { cbSize: NOTIFYICONDATAW.size, hWnd: hwnd, uID: 1 });
34
- PostQuitMessage(0);
35
- }
36
- }
37
- }
38
- return 0;
39
- }
40
- if (msg === 16 /* WmMsg.CLOSE */) {
41
- ShowWindow(hwnd, 0 /* ShowWindowCmd.HIDE */);
42
- return 0;
43
- }
44
- return gui.DefWindowProc(hwnd, msg, wParam, lParam);
45
- });
46
- // 创建窗口
47
- const W = 500, H = 400;
48
- const scr = [GetSystemMetrics(0 /* SysMetrics.CXSCREEN */), GetSystemMetrics(1 /* SysMetrics.CYSCREEN */)];
49
- const x = (scr[0] - W) >> 1;
50
- const y = (scr[1] - H) >> 1;
51
- const hwnd = gui.CreateWindow('TrayApp', '系统托盘示例', 13565952 /* WindowStyle.OVERLAPPEDWINDOW */, x, y, W, H, null, null);
52
- if (!hwnd)
53
- std.exit(0);
54
- ShowWindow(hwnd, 1 /* ShowWindowCmd.SHOWNORMAL */);
55
- // 添加说明文字
56
- const style = 1073741824 /* WindowStyle.CHILD */ | 268435456 /* WindowStyle.VISIBLE */ | 4 /* EditStyle.MULTILINE */ | 2048 /* EditStyle.READONLY */;
57
- const text = gui.CreateWindow('EDIT', 'QuickWin 系统托盘示例\r\n'
58
- + '\r\n'
59
- + '右键点击托盘图标打开上下文菜单。\r\n'
60
- + '关闭此窗口将隐藏到托盘。\r\n'
61
- + '从菜单中选择"显示窗口"恢复窗口,"退出"结束程序。', style, 15, 15, W - 30, H - 70, hwnd, null);
62
- if (text) {
63
- const font = createSystemDpiFont();
64
- if (font)
65
- gui.SendMessage(text, 48 /* WmMsg.SETFONT */, font, 1);
66
- }
67
- // LoadImageW 返回的句柄随 uType 变(HICON/HCURSOR/HBITMAP),绑定只能给裸地址档;
68
- // LoadAppIcon 的 HICON 是同一档的品牌子型,故变量按裸地址声明
69
- let hIcon = gui.LoadAppIcon();
70
- if (!hIcon)
71
- hIcon = LoadImageOrdinal(NULL, 32512 /* IDI.APPLICATION */, 1 /* ImageType.ICON */, 0, 0, 32768 /* LoadImageFlag.SHARED */);
72
- if (hIcon) {
73
- const ok = ShellNotifyIcon(0 /* NotifyIconCmd.ADD */, {
74
- cbSize: NOTIFYICONDATAW.size,
75
- hWnd: hwnd, uID: 1,
76
- uFlags: 1 /* NotifyIconFlag.MESSAGE */ | 2 /* NotifyIconFlag.ICON */,
77
- uCallbackMessage: WM_TRAY,
78
- hIcon,
79
- });
80
- std.out.printf('tray icon %s\n', ok ? 'added' : 'FAILED');
81
- std.out.flush();
82
- }
83
- else {
84
- std.out.printf('failed to load icon\n');
85
- std.out.flush();
86
- }
@@ -1,89 +0,0 @@
1
- import * as gui from 'gui'
2
- import * as std from 'std'
3
- import { NULL, type MaybePtr } from '../lib/ffi/ctype.js'
4
- import { GetCursorPos, CreatePopupMenu, AppendMenu, TrackPopupMenu, DestroyMenu, GetSystemMetrics, LoadImageOrdinal, PostQuitMessage, SetForegroundWindow, ShowWindow } from '../lib/windows/user32.js'
5
- import { NOTIFYICONDATAW, POINT } from '../lib/windows/structs.js'
6
- import { ShellNotifyIcon } from '../lib/windows/shell32.js'
7
- import { createSystemDpiFont } from '../lib/dpi.js'
8
- import { EditStyle, IDI, ImageType, LoadImageFlag, MenuFlag, NotifyIconCmd, NotifyIconFlag, ShowWindowCmd, SysMetrics, TrackPopupMenuFlag, WindowStyle, WmMsg } from '../constants'
9
-
10
- const WM_TRAY = 0x8001
11
-
12
- gui.RegisterClass('TrayApp', (hwnd, msg, wParam, lParam) => {
13
- if (msg === WM_TRAY) {
14
- const evt = lParam
15
- if (evt === WmMsg.LBUTTONDOWN) {
16
- ShowWindow(hwnd, ShowWindowCmd.SHOW)
17
- SetForegroundWindow(hwnd)
18
- } else if (evt === WmMsg.RBUTTONDOWN || evt === WmMsg.CONTEXTMENU) {
19
- SetForegroundWindow(hwnd)
20
- const pt = POINT.alloc()
21
- const pos = GetCursorPos(pt.ptr) ? POINT.decode(pt) : null
22
- const x = pos ? pos.x : 0
23
- const y = pos ? pos.y : 0
24
- const hMenu = CreatePopupMenu()
25
- if (hMenu !== NULL) {
26
- AppendMenu(hMenu, 0, 1, '显示窗口')
27
- AppendMenu(hMenu, MenuFlag.SEPARATOR, 0, NULL)
28
- AppendMenu(hMenu, 0, 2, '退出')
29
- const cmd = TrackPopupMenu(hMenu, TrackPopupMenuFlag.RETURNCMD | TrackPopupMenuFlag.RIGHTBUTTON, x, y, 0, hwnd, NULL)
30
- DestroyMenu(hMenu)
31
- if (cmd === 1) {
32
- ShowWindow(hwnd, ShowWindowCmd.SHOW)
33
- } else if (cmd === 2) {
34
- ShellNotifyIcon(NotifyIconCmd.DELETE, { cbSize: NOTIFYICONDATAW.size, hWnd: hwnd, uID: 1 })
35
- PostQuitMessage(0)
36
- }
37
- }
38
- }
39
- return 0
40
- }
41
- if (msg === WmMsg.CLOSE) {
42
- ShowWindow(hwnd, ShowWindowCmd.HIDE)
43
- return 0
44
- }
45
- return gui.DefWindowProc(hwnd, msg, wParam, lParam)
46
- })
47
-
48
- // 创建窗口
49
- const W = 500, H = 400
50
- const scr: [number, number] = [GetSystemMetrics(SysMetrics.CXSCREEN), GetSystemMetrics(SysMetrics.CYSCREEN)]
51
- const x = (scr[0] - W) >> 1
52
- const y = (scr[1] - H) >> 1
53
- const hwnd = gui.CreateWindow('TrayApp', '系统托盘示例', WindowStyle.OVERLAPPEDWINDOW, x, y, W, H, null, null)
54
- if (!hwnd) std.exit(0)
55
- ShowWindow(hwnd, ShowWindowCmd.SHOWNORMAL)
56
-
57
- // 添加说明文字
58
- const style = WindowStyle.CHILD | WindowStyle.VISIBLE | EditStyle.MULTILINE | EditStyle.READONLY
59
- const text = gui.CreateWindow('EDIT',
60
- 'QuickWin 系统托盘示例\r\n'
61
- + '\r\n'
62
- + '右键点击托盘图标打开上下文菜单。\r\n'
63
- + '关闭此窗口将隐藏到托盘。\r\n'
64
- + '从菜单中选择"显示窗口"恢复窗口,"退出"结束程序。',
65
- style, 15, 15, W - 30, H - 70, hwnd, null)
66
- if (text) {
67
- const font = createSystemDpiFont()
68
- if (font)
69
- gui.SendMessage(text, WmMsg.SETFONT, font, 1)
70
- }
71
-
72
- // LoadImageW 返回的句柄随 uType 变(HICON/HCURSOR/HBITMAP),绑定只能给裸地址档;
73
- // LoadAppIcon 的 HICON 是同一档的品牌子型,故变量按裸地址声明
74
- let hIcon: MaybePtr<''> | null = gui.LoadAppIcon()
75
- if (!hIcon) hIcon = LoadImageOrdinal(NULL, IDI.APPLICATION, ImageType.ICON, 0, 0, LoadImageFlag.SHARED)
76
- if (hIcon) {
77
- const ok = ShellNotifyIcon(NotifyIconCmd.ADD, {
78
- cbSize: NOTIFYICONDATAW.size,
79
- hWnd: hwnd, uID: 1,
80
- uFlags: NotifyIconFlag.MESSAGE | NotifyIconFlag.ICON,
81
- uCallbackMessage: WM_TRAY,
82
- hIcon,
83
- })
84
- std.out.printf('tray icon %s\n', ok ? 'added' : 'FAILED')
85
- std.out.flush()
86
- } else {
87
- std.out.printf('failed to load icon\n')
88
- std.out.flush()
89
- }
package/test/add.wasm DELETED
Binary file
@@ -1,235 +0,0 @@
1
- // 一次性性能 bench(归因场):绕过 callPacked 直调 ffi.ffiCall,把 bind() 调用链拆层。
2
- // 不入 CI。A 臂 = bind() 同场对照(对齐 bench_ffi_overhead.ts 的 S0-S3 量级)。
3
- // 用法:make js 后 win11 POST /exec {"cmd":"qwin.exe test\\bench_ffi_callraw.js","timeout":600000}
4
- // 臂(同 proc、同帧布局,唯一变量是「每次调用经过哪些层」):
5
- // A bind rest 参数 + callPacked 全链(每调 argFrame/DataView/retBuf/readRet-dv
6
- // 分配、getWidth 闭包、writeSlot 对象字面量、ffiCall、readRet)
7
- // B raw 帧复用 + 每调最小写槽/读返 + ffiCall → native 桩 + QuickJS C 边界
8
- // C raw-alloc 每调 new ArrayBuffer(32)+DataView+ArrayBuffer(8)+读返 DataView
9
- // (复刻 callPacked 分配面)+ 写槽 + ffiCall(仅 S0/S1)
10
- // 差值解读:B = trampoline 地板;C−B = 每调分配;A−C = callPacked JS 层其余。
11
- // x64 帧约定:argFrame 恒 32B(4×8 槽,桩无条件读 4 槽),retBuf 8B(ffiCall ≥8 校验)。
12
- // S1/S2/S3 各臂消费返回值到 sink(A/B/C 同口径,防 DCE 疑虑 + 收尾打印证活)。
13
- // 结束必须 DestroyWindow + std.exit。
14
- import * as std from 'std';
15
- import * as gui from 'gui';
16
- import * as ffi from 'ffi';
17
- import { bind } from '../lib/ffi/bind.js';
18
- const WM_NULL = 0x0000;
19
- const FRAME_BYTES = 32; // x64:4 槽 × 8B,桩无条件读 slots[0..3]
20
- const ROUNDS = 15;
21
- const TARGET_MS = 400;
22
- const med = (t) => {
23
- const s = [...t].sort((x, y) => x - y);
24
- const m = s.length >> 1;
25
- return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
26
- };
27
- // 文件日志(cwd=Z:\quickwin → 宿主 _build/bench_ffi_callraw_log.txt)
28
- const log = std.open('bench_ffi_callraw_log.txt', 'w');
29
- function step(msg) {
30
- std.printf('%s\n', msg);
31
- if (log) {
32
- log.puts(msg + '\n');
33
- log.flush();
34
- }
35
- }
36
- step('stage: imports ok');
37
- // ---- proc 地址(B/C 臂绕过 bind 直驱 ffiCall)----
38
- function procOf(dll, name) {
39
- const h = ffi.LoadLibrary(dll);
40
- if (!h)
41
- throw new Error('LoadLibrary(' + dll + ') failed');
42
- const p = ffi.GetProcAddress(h, name);
43
- if (!p)
44
- throw new Error('GetProcAddress(' + dll + '!' + name + ') failed');
45
- return p;
46
- }
47
- const pNop0 = procOf('test/nop.dll', 'nop0');
48
- const pNop1 = procOf('test/nop.dll', 'nop1');
49
- const pPid = procOf('kernel32.dll', 'GetCurrentProcessId');
50
- const pSm = procOf('user32.dll', 'SendMessageW');
51
- // B 臂复用帧 + 视图(一次分配,跨轮复用)
52
- const frame = new ArrayBuffer(FRAME_BYTES);
53
- const retBuf = new ArrayBuffer(8);
54
- const fv = new DataView(frame);
55
- const rv = new DataView(retBuf);
56
- let sink = 0;
57
- const fNop0 = bind('test/nop.dll', 'nop0', ' -> void');
58
- const fNop1 = bind('test/nop.dll', 'nop1', 'i32 -> i32');
59
- const fPid = bind('kernel32.dll', 'GetCurrentProcessId', ' -> u32');
60
- const winRaw = gui.CreateWindow('STATIC', 'bench-title', 0, 0, 0, 20, 20, null, null);
61
- if (!winRaw)
62
- throw new Error('CreateWindow(STATIC) failed');
63
- const hwnd = winRaw;
64
- const fSm = bind('user32.dll', 'SendMessageW', '<HWND>ptr u32 <>ptr <>ptr -> <>ptr');
65
- step('stage: window created');
66
- // ---- 可行性探针:任一臂签名/帧布局错位立刻炸 ----
67
- fNop0();
68
- if (fNop1(1) !== 2)
69
- throw new Error('sanity: bind nop1(1) !== 2');
70
- if (fPid() <= 0)
71
- throw new Error('sanity: bind pid <= 0');
72
- ffi.ffiCall(pNop0, frame, retBuf, 0);
73
- fv.setInt32(0, 1, true);
74
- ffi.ffiCall(pNop1, frame, retBuf, 0);
75
- if (rv.getInt32(0, true) !== 2)
76
- throw new Error('sanity: raw nop1 !== 2, got ' + rv.getInt32(0, true));
77
- ffi.ffiCall(pPid, frame, retBuf, 0);
78
- if (rv.getUint32(0, true) <= 0)
79
- throw new Error('sanity: raw pid <= 0');
80
- // S3 帧布局预演(hwnd 槽 + msg + wParam + lParam)
81
- fv.setBigUint64(0, BigInt(Number(hwnd)), true);
82
- fv.setUint32(8, WM_NULL, true);
83
- fv.setBigUint64(16, 0n, true);
84
- fv.setBigUint64(24, 0x1234n, true);
85
- ffi.ffiCall(pSm, frame, retBuf, 0);
86
- const smRet = rv.getBigUint64(0, true);
87
- step('stage: probes ok, raw SendMessage ret=' + String(smRet) + ' (expect 0n)');
88
- // ---- 臂循环:函数取到局部 const,两侧/各臂同形态 ----
89
- const runA0 = (n) => {
90
- const f = fNop0;
91
- const t0 = Date.now();
92
- for (let i = 0; i < n; i++)
93
- f();
94
- return Date.now() - t0;
95
- };
96
- const runB0 = (n) => {
97
- const p = pNop0, fr = frame, rb = retBuf;
98
- const t0 = Date.now();
99
- for (let i = 0; i < n; i++)
100
- ffi.ffiCall(p, fr, rb, 0);
101
- return Date.now() - t0;
102
- };
103
- const runC0 = (n) => {
104
- const p = pNop0;
105
- const t0 = Date.now();
106
- for (let i = 0; i < n; i++) {
107
- const f = new ArrayBuffer(FRAME_BYTES);
108
- const d = new DataView(f); // callPacked 无条件建 dv,照抄
109
- void d;
110
- ffi.ffiCall(p, f, new ArrayBuffer(8), 0);
111
- }
112
- return Date.now() - t0;
113
- };
114
- const runA1 = (n) => {
115
- const f = fNop1;
116
- const t0 = Date.now();
117
- for (let i = 0; i < n; i++)
118
- sink += f(1);
119
- return Date.now() - t0;
120
- };
121
- const runB1 = (n) => {
122
- const p = pNop1, fr = frame, rb = retBuf, d = fv, rd = rv;
123
- const t0 = Date.now();
124
- for (let i = 0; i < n; i++) {
125
- d.setInt32(0, 1, true);
126
- ffi.ffiCall(p, fr, rb, 0);
127
- sink += rd.getInt32(0, true);
128
- }
129
- return Date.now() - t0;
130
- };
131
- const runC1 = (n) => {
132
- const p = pNop1;
133
- const t0 = Date.now();
134
- for (let i = 0; i < n; i++) {
135
- const f = new ArrayBuffer(FRAME_BYTES);
136
- const d = new DataView(f);
137
- d.setInt32(0, 1, true);
138
- const r = new ArrayBuffer(8);
139
- ffi.ffiCall(p, f, r, 0);
140
- sink += new DataView(r).getInt32(0, true); // readRet 同构:每次 new DataView
141
- }
142
- return Date.now() - t0;
143
- };
144
- const runA2 = (n) => {
145
- const f = fPid;
146
- const t0 = Date.now();
147
- for (let i = 0; i < n; i++)
148
- sink += f();
149
- return Date.now() - t0;
150
- };
151
- const runB2 = (n) => {
152
- const p = pPid, fr = frame, rb = retBuf, rd = rv;
153
- const t0 = Date.now();
154
- for (let i = 0; i < n; i++) {
155
- ffi.ffiCall(p, fr, rb, 0);
156
- sink += rd.getUint32(0, true);
157
- }
158
- return Date.now() - t0;
159
- };
160
- const runA3 = (n) => {
161
- const f = fSm;
162
- const t0 = Date.now();
163
- for (let i = 0; i < n; i++)
164
- sink += f(hwnd, WM_NULL, 0, 0x1234);
165
- return Date.now() - t0;
166
- };
167
- const runB3 = (n) => {
168
- const p = pSm, fr = frame, rb = retBuf, d = fv, rd = rv;
169
- const t0 = Date.now();
170
- for (let i = 0; i < n; i++) {
171
- d.setBigUint64(0, BigInt(Number(hwnd)), true);
172
- d.setUint32(8, WM_NULL, true);
173
- d.setBigUint64(16, 0n, true);
174
- d.setBigUint64(24, 0x1234n, true);
175
- ffi.ffiCall(p, fr, rb, 0);
176
- sink += Number(rd.getBigUint64(0, true));
177
- }
178
- return Date.now() - t0;
179
- };
180
- // 自校准:探针逐级放大 n0 至 dt≥20ms(1ms 量化步长 ≤5%),按 TARGET_MS 定批量。
181
- // B 臂可能比 A 快两个量级,固定批量必有一侧撞量化墙——与 overhead 场同法。
182
- function calibrate(label, fn) {
183
- let n0 = 10_000;
184
- let dt = 0;
185
- for (;;) {
186
- dt = fn(n0);
187
- if (dt >= 20 || n0 >= 10_000_000)
188
- break;
189
- n0 *= 10;
190
- }
191
- const per = dt / n0;
192
- const n = Math.max(10_000, Math.min(10_000_000, Math.round(TARGET_MS / per)));
193
- step(' calib ' + label + ': probe n=' + String(n0) + ' dt=' + String(dt) +
194
- 'ms → batch=' + String(n));
195
- return n;
196
- }
197
- const mkArm = (label, fn) => ({ label, n: calibrate(label, fn), fn });
198
- function suite(name, arms) {
199
- step('suite: ' + name + ' warmup');
200
- for (const a of arms)
201
- a.fn(a.n);
202
- const times = arms.map(() => []);
203
- for (let r = 0; r < ROUNDS; r++) {
204
- for (let k = 0; k < arms.length; k++) {
205
- const i = (r + k) % arms.length;
206
- times[i].push(arms[i].fn(arms[i].n));
207
- }
208
- }
209
- for (let r = 0; r < Math.min(ROUNDS, 3); r++) {
210
- step(' round ' + String(r) + ': ' +
211
- arms.map((a, i) => a.label + '=' + String(times[i][r]) + 'ms').join(' '));
212
- }
213
- const ns = (i, ms) => (ms * 1e6 / arms[i].n).toFixed(1);
214
- const meds = times.map(med);
215
- const mins = times.map(t => Math.min(...t));
216
- step(' ** median: ' + arms.map((a, i) => a.label + '=' + ns(i, meds[i]) + 'ns').join(' | '));
217
- step(' min: ' + arms.map((a, i) => a.label + '=' + ns(i, mins[i]) + 'ns').join(' | '));
218
- if (arms.length >= 3) {
219
- const a = Number(ns(0, meds[0])), b = Number(ns(1, meds[1])), c = Number(ns(2, meds[2]));
220
- step(' split: C-B=' + (c - b).toFixed(1) + 'ns (每调分配) A-C=' +
221
- (a - c).toFixed(1) + 'ns (callPacked JS 层其余)');
222
- }
223
- }
224
- step('bench_ffi_callraw (attribution): A=bind / B=raw ffiCall / C=raw+per-call alloc; ' +
225
- 'calib ~' + String(TARGET_MS) + 'ms, ' + String(ROUNDS) + 'r (median)');
226
- suite('S0 nop0 0参 void', [mkArm('A bind', runA0), mkArm('B raw ', runB0), mkArm('C allc', runC0)]);
227
- suite('S1 nop1 1×i32', [mkArm('A bind', runA1), mkArm('B raw ', runB1), mkArm('C allc', runC1)]);
228
- suite('S2 GetCurrentProcessId', [mkArm('A bind', runA2), mkArm('B raw ', runB2)]);
229
- suite('S3 SendMessageW WM_NULL', [mkArm('A bind', runA3), mkArm('B raw ', runB3)]);
230
- step('sink=' + String(sink) + ' (liveness: >0 证明各臂 native 调用真实发生)');
231
- gui.DestroyWindow(hwnd);
232
- step('DONE');
233
- if (log)
234
- log.close();
235
- std.exit(0);