querysub 0.356.0 → 0.357.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 (70) hide show
  1. package/.cursorrules +8 -0
  2. package/bin/movelogs.js +4 -0
  3. package/package.json +12 -6
  4. package/scripts/postinstall.js +23 -0
  5. package/src/-a-archives/archiveCache.ts +10 -12
  6. package/src/-a-archives/archives.ts +29 -0
  7. package/src/-a-archives/archivesBackBlaze.ts +60 -12
  8. package/src/-a-archives/archivesDisk.ts +27 -8
  9. package/src/-a-archives/archivesLimitedCache.ts +21 -0
  10. package/src/-a-archives/archivesMemoryCache.ts +350 -0
  11. package/src/-a-archives/archivesPrivateFileSystem.ts +22 -0
  12. package/src/-g-core-values/NodeCapabilities.ts +3 -0
  13. package/src/0-path-value-core/auditLogs.ts +5 -1
  14. package/src/0-path-value-core/pathValueCore.ts +7 -7
  15. package/src/4-dom/qreact.tsx +1 -0
  16. package/src/4-querysub/Querysub.ts +1 -5
  17. package/src/config.ts +5 -0
  18. package/src/diagnostics/MachineThreadInfo.tsx +235 -0
  19. package/src/diagnostics/NodeViewer.tsx +3 -2
  20. package/src/diagnostics/logs/FastArchiveAppendable.ts +79 -42
  21. package/src/diagnostics/logs/FastArchiveController.ts +102 -63
  22. package/src/diagnostics/logs/FastArchiveViewer.tsx +36 -8
  23. package/src/diagnostics/logs/IndexedLogs/BufferIndex.ts +461 -0
  24. package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.cpp +327 -0
  25. package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.d.ts +18 -0
  26. package/src/diagnostics/logs/IndexedLogs/BufferIndexCPP.js +1 -0
  27. package/src/diagnostics/logs/IndexedLogs/BufferIndexHelpers.ts +140 -0
  28. package/src/diagnostics/logs/IndexedLogs/BufferIndexLogsOptimizationConstants.ts +22 -0
  29. package/src/diagnostics/logs/IndexedLogs/BufferIndexWAT.wat +1145 -0
  30. package/src/diagnostics/logs/IndexedLogs/BufferIndexWAT.wat.d.ts +178 -0
  31. package/src/diagnostics/logs/IndexedLogs/BufferListStreamer.ts +206 -0
  32. package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +719 -0
  33. package/src/diagnostics/logs/IndexedLogs/BufferUnitSet.ts +146 -0
  34. package/src/diagnostics/logs/IndexedLogs/FilePathSelector.tsx +408 -0
  35. package/src/diagnostics/logs/IndexedLogs/FindProgressTracker.ts +45 -0
  36. package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +598 -0
  37. package/src/diagnostics/logs/IndexedLogs/LogStreamer.ts +47 -0
  38. package/src/diagnostics/logs/IndexedLogs/LogViewer3.tsx +702 -0
  39. package/src/diagnostics/logs/IndexedLogs/TimeFileTree.ts +236 -0
  40. package/src/diagnostics/logs/IndexedLogs/binding.gyp +23 -0
  41. package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +221 -0
  42. package/src/diagnostics/logs/IndexedLogs/moveLogsEntry.ts +10 -0
  43. package/src/diagnostics/logs/LogViewer2.tsx +120 -55
  44. package/src/diagnostics/logs/TimeRangeSelector.tsx +5 -2
  45. package/src/diagnostics/logs/diskLogger.ts +32 -48
  46. package/src/diagnostics/logs/errorNotifications/ErrorNotificationController.ts +3 -2
  47. package/src/diagnostics/logs/errorNotifications/errorDigests.tsx +1 -0
  48. package/src/diagnostics/logs/lifeCycleAnalysis/LifeCyclePages.tsx +150 -0
  49. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +132 -15
  50. package/src/diagnostics/logs/lifeCycleAnalysis/test.ts +180 -0
  51. package/src/diagnostics/logs/lifeCycleAnalysis/test.wat +106 -0
  52. package/src/diagnostics/logs/lifeCycleAnalysis/test.wat.d.ts +2 -0
  53. package/src/diagnostics/logs/lifeCycleAnalysis/testHoist.ts +5 -0
  54. package/src/diagnostics/logs/logViewerExtractField.ts +2 -3
  55. package/src/diagnostics/managementPages.tsx +10 -0
  56. package/src/diagnostics/trackResources.ts +1 -1
  57. package/src/misc/lz4_wasm_nodejs.d.ts +34 -0
  58. package/src/misc/lz4_wasm_nodejs.js +178 -0
  59. package/src/misc/lz4_wasm_nodejs_bg.js +94 -0
  60. package/src/misc/lz4_wasm_nodejs_bg.wasm +0 -0
  61. package/src/misc/lz4_wasm_nodejs_bg.wasm.d.ts +15 -0
  62. package/src/storage/CompressedStream.ts +13 -0
  63. package/src/storage/LZ4.ts +32 -0
  64. package/src/storage/ZSTD.ts +10 -0
  65. package/src/wat/watCompiler.ts +1716 -0
  66. package/src/wat/watGrammar.pegjs +93 -0
  67. package/src/wat/watHandler.ts +179 -0
  68. package/src/wat/watInstructions.txt +707 -0
  69. package/src/zip.ts +3 -89
  70. package/src/diagnostics/logs/lifeCycleAnalysis/spec.md +0 -125
@@ -0,0 +1,1716 @@
1
+ // WAT Compiler - Compiles WebAssembly Text Format to binary
2
+
3
+ import * as peggy from "peggy";
4
+ import * as fs from "fs";
5
+ import * as path from "path";
6
+
7
+ type ASTNode = {
8
+ type: "module" | "sexpr" | "instruction" | "identifier" | "index" | "number" | "string" | "attribute";
9
+ keyword?: string;
10
+ items?: ASTNode[];
11
+ attributes?: ASTNode[];
12
+ operands?: ASTNode[];
13
+ name?: string;
14
+ value?: string | number;
15
+ };
16
+
17
+ // Instruction forms represent the basic patterns instructions follow
18
+ type InstructionForm =
19
+ | "nullary" // No operands (e.g., nop, return)
20
+ | "unary" // One operand (e.g., drop, local.get)
21
+ | "binary" // Two operands (e.g., i32.store)
22
+ | "block" // Block structure (e.g., block, loop, if)
23
+ | "control" // Control flow (e.g., br, br_if)
24
+ | "call" // Function calls
25
+ | "memory" // Memory operations
26
+ | "const"; // Constants (e.g., i32.const)
27
+
28
+ // Instruction metadata
29
+ type InstructionMeta = {
30
+ form: InstructionForm;
31
+ opcode: number;
32
+ immediates?: ("varuint32" | "varint32" | "varint64" | "float32" | "float64" | "blocktype" | "byte")[];
33
+ };
34
+
35
+ // Instruction lookup table - maps instruction names to their forms and opcodes
36
+ export const INSTRUCTION_MAP: Record<string, InstructionMeta> = {
37
+ // Parametric instructions
38
+ "unreachable": { form: "nullary", opcode: 0x00 },
39
+ "nop": { form: "nullary", opcode: 0x01 },
40
+ "drop": { form: "nullary", opcode: 0x1a },
41
+ "select": { form: "nullary", opcode: 0x1b },
42
+
43
+ // Control instructions
44
+ "block": { form: "block", opcode: 0x02, immediates: ["blocktype"] },
45
+ "loop": { form: "block", opcode: 0x03, immediates: ["blocktype"] },
46
+ "if": { form: "block", opcode: 0x04, immediates: ["blocktype"] },
47
+ "else": { form: "nullary", opcode: 0x05 },
48
+ "end": { form: "nullary", opcode: 0x0b },
49
+ "br": { form: "control", opcode: 0x0c, immediates: ["varuint32"] },
50
+ "br_if": { form: "control", opcode: 0x0d, immediates: ["varuint32"] },
51
+ "return": { form: "nullary", opcode: 0x0f },
52
+ "call": { form: "call", opcode: 0x10, immediates: ["varuint32"] },
53
+ "call_indirect": { form: "call", opcode: 0x11, immediates: ["varuint32", "varuint32"] },
54
+
55
+ // Variable instructions
56
+ "local.get": { form: "unary", opcode: 0x20, immediates: ["varuint32"] },
57
+ "local.set": { form: "unary", opcode: 0x21, immediates: ["varuint32"] },
58
+ "local.tee": { form: "unary", opcode: 0x22, immediates: ["varuint32"] },
59
+ "global.get": { form: "unary", opcode: 0x23, immediates: ["varuint32"] },
60
+ "global.set": { form: "unary", opcode: 0x24, immediates: ["varuint32"] },
61
+
62
+ // Table instructions
63
+ "table.get": { form: "unary", opcode: 0x25, immediates: ["varuint32"] },
64
+ "table.set": { form: "unary", opcode: 0x26, immediates: ["varuint32"] },
65
+
66
+ // Memory instructions
67
+ "i32.load": { form: "memory", opcode: 0x28 },
68
+ "i64.load": { form: "memory", opcode: 0x29 },
69
+ "f32.load": { form: "memory", opcode: 0x2a },
70
+ "f64.load": { form: "memory", opcode: 0x2b },
71
+ "i32.load8_s": { form: "memory", opcode: 0x2c },
72
+ "i32.load8_u": { form: "memory", opcode: 0x2d },
73
+ "i32.load16_s": { form: "memory", opcode: 0x2e },
74
+ "i32.load16_u": { form: "memory", opcode: 0x2f },
75
+ "i64.load8_s": { form: "memory", opcode: 0x30 },
76
+ "i64.load8_u": { form: "memory", opcode: 0x31 },
77
+ "i64.load16_s": { form: "memory", opcode: 0x32 },
78
+ "i64.load16_u": { form: "memory", opcode: 0x33 },
79
+ "i64.load32_s": { form: "memory", opcode: 0x34 },
80
+ "i64.load32_u": { form: "memory", opcode: 0x35 },
81
+ "i32.store": { form: "memory", opcode: 0x36 },
82
+ "i64.store": { form: "memory", opcode: 0x37 },
83
+ "f32.store": { form: "memory", opcode: 0x38 },
84
+ "f64.store": { form: "memory", opcode: 0x39 },
85
+ "i32.store8": { form: "memory", opcode: 0x3a },
86
+ "i32.store16": { form: "memory", opcode: 0x3b },
87
+ "i64.store8": { form: "memory", opcode: 0x3c },
88
+ "i64.store16": { form: "memory", opcode: 0x3d },
89
+ "i64.store32": { form: "memory", opcode: 0x3e },
90
+ "memory.size": { form: "unary", opcode: 0x3f, immediates: ["varuint32"] },
91
+ "memory.grow": { form: "unary", opcode: 0x40, immediates: ["varuint32"] },
92
+
93
+ // Numeric instructions - constants
94
+ "i32.const": { form: "const", opcode: 0x41, immediates: ["varint32"] },
95
+ "i64.const": { form: "const", opcode: 0x42, immediates: ["varint64"] },
96
+ "f32.const": { form: "const", opcode: 0x43, immediates: ["float32"] },
97
+ "f64.const": { form: "const", opcode: 0x44, immediates: ["float64"] },
98
+
99
+ // i32 comparison and test operations
100
+ "i32.eqz": { form: "nullary", opcode: 0x45 },
101
+ "i32.eq": { form: "nullary", opcode: 0x46 },
102
+ "i32.ne": { form: "nullary", opcode: 0x47 },
103
+ "i32.lt_s": { form: "nullary", opcode: 0x48 },
104
+ "i32.lt_u": { form: "nullary", opcode: 0x49 },
105
+ "i32.gt_s": { form: "nullary", opcode: 0x4a },
106
+ "i32.gt_u": { form: "nullary", opcode: 0x4b },
107
+ "i32.le_s": { form: "nullary", opcode: 0x4c },
108
+ "i32.le_u": { form: "nullary", opcode: 0x4d },
109
+ "i32.ge_s": { form: "nullary", opcode: 0x4e },
110
+ "i32.ge_u": { form: "nullary", opcode: 0x4f },
111
+
112
+ // i64 comparison and test operations
113
+ "i64.eqz": { form: "nullary", opcode: 0x50 },
114
+ "i64.eq": { form: "nullary", opcode: 0x51 },
115
+ "i64.ne": { form: "nullary", opcode: 0x52 },
116
+ "i64.lt_s": { form: "nullary", opcode: 0x53 },
117
+ "i64.lt_u": { form: "nullary", opcode: 0x54 },
118
+ "i64.gt_s": { form: "nullary", opcode: 0x55 },
119
+ "i64.gt_u": { form: "nullary", opcode: 0x56 },
120
+ "i64.le_s": { form: "nullary", opcode: 0x57 },
121
+ "i64.le_u": { form: "nullary", opcode: 0x58 },
122
+ "i64.ge_s": { form: "nullary", opcode: 0x59 },
123
+ "i64.ge_u": { form: "nullary", opcode: 0x5a },
124
+
125
+ // f32 comparison operations
126
+ "f32.eq": { form: "nullary", opcode: 0x5b },
127
+ "f32.ne": { form: "nullary", opcode: 0x5c },
128
+ "f32.lt": { form: "nullary", opcode: 0x5d },
129
+ "f32.gt": { form: "nullary", opcode: 0x5e },
130
+ "f32.le": { form: "nullary", opcode: 0x5f },
131
+ "f32.ge": { form: "nullary", opcode: 0x60 },
132
+
133
+ // f64 comparison operations
134
+ "f64.eq": { form: "nullary", opcode: 0x61 },
135
+ "f64.ne": { form: "nullary", opcode: 0x62 },
136
+ "f64.lt": { form: "nullary", opcode: 0x63 },
137
+ "f64.gt": { form: "nullary", opcode: 0x64 },
138
+ "f64.le": { form: "nullary", opcode: 0x65 },
139
+ "f64.ge": { form: "nullary", opcode: 0x66 },
140
+
141
+ // i32 arithmetic and bitwise operations
142
+ "i32.clz": { form: "nullary", opcode: 0x67 },
143
+ "i32.ctz": { form: "nullary", opcode: 0x68 },
144
+ "i32.popcnt": { form: "nullary", opcode: 0x69 },
145
+ "i32.add": { form: "nullary", opcode: 0x6a },
146
+ "i32.sub": { form: "nullary", opcode: 0x6b },
147
+ "i32.mul": { form: "nullary", opcode: 0x6c },
148
+ "i32.div_s": { form: "nullary", opcode: 0x6d },
149
+ "i32.div_u": { form: "nullary", opcode: 0x6e },
150
+ "i32.rem_s": { form: "nullary", opcode: 0x6f },
151
+ "i32.rem_u": { form: "nullary", opcode: 0x70 },
152
+ "i32.and": { form: "nullary", opcode: 0x71 },
153
+ "i32.or": { form: "nullary", opcode: 0x72 },
154
+ "i32.xor": { form: "nullary", opcode: 0x73 },
155
+ "i32.shl": { form: "nullary", opcode: 0x74 },
156
+ "i32.shr_s": { form: "nullary", opcode: 0x75 },
157
+ "i32.shr_u": { form: "nullary", opcode: 0x76 },
158
+ "i32.rotl": { form: "nullary", opcode: 0x77 },
159
+ "i32.rotr": { form: "nullary", opcode: 0x78 },
160
+
161
+ // i64 arithmetic and bitwise operations
162
+ "i64.clz": { form: "nullary", opcode: 0x79 },
163
+ "i64.ctz": { form: "nullary", opcode: 0x7a },
164
+ "i64.popcnt": { form: "nullary", opcode: 0x7b },
165
+ "i64.add": { form: "nullary", opcode: 0x7c },
166
+ "i64.sub": { form: "nullary", opcode: 0x7d },
167
+ "i64.mul": { form: "nullary", opcode: 0x7e },
168
+ "i64.div_s": { form: "nullary", opcode: 0x7f },
169
+ "i64.div_u": { form: "nullary", opcode: 0x80 },
170
+ "i64.rem_s": { form: "nullary", opcode: 0x81 },
171
+ "i64.rem_u": { form: "nullary", opcode: 0x82 },
172
+ "i64.and": { form: "nullary", opcode: 0x83 },
173
+ "i64.or": { form: "nullary", opcode: 0x84 },
174
+ "i64.xor": { form: "nullary", opcode: 0x85 },
175
+ "i64.shl": { form: "nullary", opcode: 0x86 },
176
+ "i64.shr_s": { form: "nullary", opcode: 0x87 },
177
+ "i64.shr_u": { form: "nullary", opcode: 0x88 },
178
+ "i64.rotl": { form: "nullary", opcode: 0x89 },
179
+ "i64.rotr": { form: "nullary", opcode: 0x8a },
180
+
181
+ // f32 arithmetic operations
182
+ "f32.abs": { form: "nullary", opcode: 0x8b },
183
+ "f32.neg": { form: "nullary", opcode: 0x8c },
184
+ "f32.ceil": { form: "nullary", opcode: 0x8d },
185
+ "f32.floor": { form: "nullary", opcode: 0x8e },
186
+ "f32.trunc": { form: "nullary", opcode: 0x8f },
187
+ "f32.nearest": { form: "nullary", opcode: 0x90 },
188
+ "f32.sqrt": { form: "nullary", opcode: 0x91 },
189
+ "f32.add": { form: "nullary", opcode: 0x92 },
190
+ "f32.sub": { form: "nullary", opcode: 0x93 },
191
+ "f32.mul": { form: "nullary", opcode: 0x94 },
192
+ "f32.div": { form: "nullary", opcode: 0x95 },
193
+ "f32.min": { form: "nullary", opcode: 0x96 },
194
+ "f32.max": { form: "nullary", opcode: 0x97 },
195
+ "f32.copysign": { form: "nullary", opcode: 0x98 },
196
+
197
+ // f64 arithmetic operations
198
+ "f64.abs": { form: "nullary", opcode: 0x99 },
199
+ "f64.neg": { form: "nullary", opcode: 0x9a },
200
+ "f64.ceil": { form: "nullary", opcode: 0x9b },
201
+ "f64.floor": { form: "nullary", opcode: 0x9c },
202
+ "f64.trunc": { form: "nullary", opcode: 0x9d },
203
+ "f64.nearest": { form: "nullary", opcode: 0x9e },
204
+ "f64.sqrt": { form: "nullary", opcode: 0x9f },
205
+ "f64.add": { form: "nullary", opcode: 0xa0 },
206
+ "f64.sub": { form: "nullary", opcode: 0xa1 },
207
+ "f64.mul": { form: "nullary", opcode: 0xa2 },
208
+ "f64.div": { form: "nullary", opcode: 0xa3 },
209
+ "f64.min": { form: "nullary", opcode: 0xa4 },
210
+ "f64.max": { form: "nullary", opcode: 0xa5 },
211
+ "f64.copysign": { form: "nullary", opcode: 0xa6 },
212
+
213
+ // Conversion operations
214
+ "i32.wrap_i64": { form: "nullary", opcode: 0xa7 },
215
+ "i32.trunc_s_f32": { form: "nullary", opcode: 0xa8 },
216
+ "i32.trunc_u_f32": { form: "nullary", opcode: 0xa9 },
217
+ "i32.trunc_s_f64": { form: "nullary", opcode: 0xaa },
218
+ "i32.trunc_u_f64": { form: "nullary", opcode: 0xab },
219
+ "i64.extend_i32_s": { form: "nullary", opcode: 0xac },
220
+ "i64.extend_i32_u": { form: "nullary", opcode: 0xad },
221
+ "i64.trunc_s_f32": { form: "nullary", opcode: 0xae },
222
+ "i64.trunc_u_f32": { form: "nullary", opcode: 0xaf },
223
+ "i64.trunc_s_f64": { form: "nullary", opcode: 0xb0 },
224
+ "i64.trunc_u_f64": { form: "nullary", opcode: 0xb1 },
225
+ "f32.convert_s_i32": { form: "nullary", opcode: 0xb2 },
226
+ "f32.convert_u_i32": { form: "nullary", opcode: 0xb3 },
227
+ "f32.convert_s_i64": { form: "nullary", opcode: 0xb4 },
228
+ "f32.convert_u_i64": { form: "nullary", opcode: 0xb5 },
229
+ "f32.demote_f64": { form: "nullary", opcode: 0xb6 },
230
+ "f64.convert_s_i32": { form: "nullary", opcode: 0xb7 },
231
+ "f64.convert_u_i32": { form: "nullary", opcode: 0xb8 },
232
+ "f64.convert_s_i64": { form: "nullary", opcode: 0xb9 },
233
+ "f64.convert_u_i64": { form: "nullary", opcode: 0xba },
234
+ "f64.promote_f32": { form: "nullary", opcode: 0xbb },
235
+ "i32.reinterpret_f32": { form: "nullary", opcode: 0xbc },
236
+ "i64.reinterpret_f64": { form: "nullary", opcode: 0xbd },
237
+ "f32.reinterpret_i32": { form: "nullary", opcode: 0xbe },
238
+ "f64.reinterpret_i64": { form: "nullary", opcode: 0xbf },
239
+
240
+ // Sign extension operations
241
+ "i32.extend8_s": { form: "nullary", opcode: 0xc0 },
242
+ "i32.extend16_s": { form: "nullary", opcode: 0xc1 },
243
+ "i64.extend8_s": { form: "nullary", opcode: 0xc2 },
244
+ "i64.extend16_s": { form: "nullary", opcode: 0xc3 },
245
+ "i64.extend32_s": { form: "nullary", opcode: 0xc4 },
246
+
247
+ // Reference instructions
248
+ "ref.is_null": { form: "nullary", opcode: 0xd1 },
249
+ "ref.eq": { form: "nullary", opcode: 0xd3 },
250
+ "ref.as_non_null": { form: "nullary", opcode: 0xd4 },
251
+
252
+ // SIMD Vector instructions (0xFD prefix + sub-opcode)
253
+ "v128.load": { form: "memory", opcode: 0xfd00 },
254
+ "v128.store": { form: "memory", opcode: 0xfd0b },
255
+ "i8x16.shuffle": { form: "unary", opcode: 0xfd0d, immediates: ["byte", "byte", "byte", "byte", "byte", "byte", "byte", "byte", "byte", "byte", "byte", "byte", "byte", "byte", "byte", "byte"] },
256
+ "i32x4.splat": { form: "nullary", opcode: 0xfd11 },
257
+ "i32x4.extract_lane": { form: "unary", opcode: 0xfd1b, immediates: ["varuint32"] },
258
+ "i32x4.replace_lane": { form: "unary", opcode: 0xfd1c, immediates: ["varuint32"] },
259
+ "i32x4.mul": { form: "nullary", opcode: 0xfdb5 },
260
+ "i32x4.shr_u": { form: "nullary", opcode: 0xfdad },
261
+ "i32x4.and": { form: "nullary", opcode: 0xfd4e },
262
+ "i32x4.sub": { form: "nullary", opcode: 0xfdb1 },
263
+ "i32x4.max_u": { form: "nullary", opcode: 0xfdb9 },
264
+ "i32x4.min_u": { form: "nullary", opcode: 0xfdb7 },
265
+ };
266
+
267
+ // Type definitions for compilation
268
+ type FuncType = {
269
+ params: string[];
270
+ results: string[];
271
+ };
272
+
273
+ type FuncDef = {
274
+ typeIdx: number;
275
+ locals: string[];
276
+ localNames: string[]; // Names for params and locals (empty string if unnamed)
277
+ body: ASTNode[];
278
+ exportName?: string;
279
+ };
280
+
281
+ type ExportDef = {
282
+ name: string;
283
+ kind: "func" | "table" | "memory" | "global";
284
+ index: number;
285
+ };
286
+
287
+ // Compiler context
288
+ class CompilerContext {
289
+ buffer: number[] = [];
290
+ labelStack: string[] = [];
291
+ localMap: Map<string, number> = new Map();
292
+ functionMap: Map<string, number> = new Map();
293
+
294
+ // Module-level data
295
+ types: FuncType[] = [];
296
+ imports: { module: string; name: string; kind: string; type?: any }[] = [];
297
+ functions: FuncDef[] = [];
298
+ globals: { type: string; mutable: boolean; init: ASTNode[] }[] = [];
299
+ exports: ExportDef[] = [];
300
+ memoryPages?: { min: number; max?: number };
301
+ hasImportedMemory: boolean = false;
302
+
303
+ emit(byte: number) {
304
+ this.buffer.push(byte & 0xff);
305
+ }
306
+
307
+ emitVarint32(value: number) {
308
+ // LEB128 signed 32-bit encoding
309
+ let more = true;
310
+ while (more) {
311
+ let byte = value & 0x7f;
312
+ value >>= 7;
313
+ if ((value === 0 && (byte & 0x40) === 0) || (value === -1 && (byte & 0x40) !== 0)) {
314
+ more = false;
315
+ } else {
316
+ byte |= 0x80;
317
+ }
318
+ this.emit(byte);
319
+ }
320
+ }
321
+
322
+ emitVaruint32(value: number) {
323
+ // LEB128 unsigned 32-bit encoding
324
+ do {
325
+ let byte = value & 0x7f;
326
+ value >>>= 7;
327
+ if (value !== 0) {
328
+ byte |= 0x80;
329
+ }
330
+ this.emit(byte);
331
+ } while (value !== 0);
332
+ }
333
+
334
+ emitFloat32(value: number) {
335
+ const buffer = new ArrayBuffer(4);
336
+ new DataView(buffer).setFloat32(0, value, true);
337
+ const bytes = new Uint8Array(buffer);
338
+ for (const byte of bytes) {
339
+ this.emit(byte);
340
+ }
341
+ }
342
+
343
+ emitFloat64(value: number) {
344
+ const buffer = new ArrayBuffer(8);
345
+ new DataView(buffer).setFloat64(0, value, true);
346
+ const bytes = new Uint8Array(buffer);
347
+ for (const byte of bytes) {
348
+ this.emit(byte);
349
+ }
350
+ }
351
+
352
+ emitString(str: string) {
353
+ const bytes = new TextEncoder().encode(str);
354
+ this.emitVaruint32(bytes.length);
355
+ for (const byte of bytes) {
356
+ this.emit(byte);
357
+ }
358
+ }
359
+
360
+ emitSection(sectionId: number, contents: number[]) {
361
+ this.emit(sectionId);
362
+ this.emitVaruint32(contents.length);
363
+ for (const byte of contents) {
364
+ this.emit(byte);
365
+ }
366
+ }
367
+
368
+ emitOpcode(opcode: number) {
369
+ // SIMD vector instructions use 0xFD prefix
370
+ if (opcode >= 0xfd00 && opcode <= 0xfdff) {
371
+ this.emit(0xfd);
372
+ this.emitVaruint32(opcode & 0xff);
373
+ } else {
374
+ this.emit(opcode);
375
+ }
376
+ }
377
+ }
378
+
379
+ // Instruction form handlers
380
+ const FORM_HANDLERS = {
381
+ nullary: (ctx: CompilerContext, meta: InstructionMeta, node: ASTNode) => {
382
+ ctx.emitOpcode(meta.opcode);
383
+ },
384
+
385
+ unary: (ctx: CompilerContext, meta: InstructionMeta, node: ASTNode) => {
386
+ ctx.emitOpcode(meta.opcode);
387
+ if (!node.operands) return;
388
+
389
+ // Handle multiple immediates (e.g., i8x16.shuffle has 16 byte immediates)
390
+ if (meta.immediates && meta.immediates.length > 1) {
391
+ for (let i = 0; i < meta.immediates.length && i < node.operands.length; i++) {
392
+ const immType = meta.immediates[i];
393
+ const operand = node.operands[i];
394
+ if (operand && operand.type === "number") {
395
+ const value = typeof operand.value === "number" ? operand.value : parseInt(operand.value as string, 10);
396
+ if (immType === "byte") {
397
+ ctx.emit(value & 0xff);
398
+ } else if (immType === "varuint32") {
399
+ ctx.emitVaruint32(value);
400
+ } else if (immType === "varint32") {
401
+ ctx.emitVarint32(value);
402
+ }
403
+ }
404
+ }
405
+ return;
406
+ }
407
+
408
+ // Handle single immediate (original behavior)
409
+ const operand = node.operands[0];
410
+ if (operand && operand.type === "number") {
411
+ const value = typeof operand.value === "number" ? operand.value : parseInt(operand.value as string, 10);
412
+ ctx.emitVaruint32(value);
413
+ } else if (operand && operand.type === "identifier") {
414
+ if (!operand.name) return;
415
+ const numericIndex = parseInt(operand.name, 10);
416
+ if (!isNaN(numericIndex)) {
417
+ ctx.emitVaruint32(numericIndex);
418
+ return;
419
+ }
420
+ const index = ctx.localMap.get(operand.name);
421
+ if (index === undefined) {
422
+ throw new Error(`Unknown local: ${operand.name}`);
423
+ }
424
+ ctx.emitVaruint32(index);
425
+ } else if (operand && operand.type === "index") {
426
+ if (operand.value === undefined) return;
427
+ ctx.emitVaruint32(operand.value as number);
428
+ }
429
+ },
430
+
431
+ const: (ctx: CompilerContext, meta: InstructionMeta, node: ASTNode) => {
432
+ ctx.emitOpcode(meta.opcode);
433
+ if (!node.operands) return;
434
+ const operand = node.operands[0];
435
+ if (operand && operand.type === "number") {
436
+ if (operand.value === undefined) return;
437
+ const value = typeof operand.value === "number" ? operand.value : parseFloat(operand.value);
438
+ if (meta.immediates && meta.immediates[0] === "varint32") {
439
+ ctx.emitVarint32(value);
440
+ } else if (meta.immediates && meta.immediates[0] === "varint64") {
441
+ // TODO: Handle 64-bit integers properly
442
+ ctx.emitVarint32(value);
443
+ } else if (meta.immediates && meta.immediates[0] === "float32") {
444
+ ctx.emitFloat32(value);
445
+ } else if (meta.immediates && meta.immediates[0] === "float64") {
446
+ ctx.emitFloat64(value);
447
+ }
448
+ }
449
+ },
450
+
451
+ block: (ctx: CompilerContext, meta: InstructionMeta, node: ASTNode) => {
452
+ ctx.emitOpcode(meta.opcode);
453
+ // Block type (0x40 = empty/void)
454
+ ctx.emit(0x40);
455
+ // Note: Block body compilation is handled specially in compileInstruction
456
+ },
457
+
458
+ control: (ctx: CompilerContext, meta: InstructionMeta, node: ASTNode) => {
459
+ ctx.emitOpcode(meta.opcode);
460
+ if (!node.operands) return;
461
+ const operand = node.operands[0];
462
+ if (operand && operand.type === "identifier") {
463
+ // Find label depth in the stack
464
+ if (!operand.name) return;
465
+ const labelName = operand.name;
466
+ let depth = -1;
467
+ for (let i = ctx.labelStack.length - 1; i >= 0; i--) {
468
+ depth++;
469
+ if (ctx.labelStack[i] === labelName) {
470
+ ctx.emitVaruint32(depth);
471
+ return;
472
+ }
473
+ }
474
+ throw new Error(`Unknown label: ${labelName}`);
475
+ } else if (operand && operand.type === "index") {
476
+ if (operand.value === undefined) return;
477
+ ctx.emitVaruint32(operand.value as number);
478
+ } else if (operand && operand.type === "number") {
479
+ // Direct numeric depth
480
+ const value = typeof operand.value === "number" ? operand.value : parseInt(operand.value as string, 10);
481
+ ctx.emitVaruint32(value);
482
+ }
483
+ },
484
+
485
+ call: (ctx: CompilerContext, meta: InstructionMeta, node: ASTNode) => {
486
+ ctx.emitOpcode(meta.opcode);
487
+ if (!node.operands) return;
488
+ const operand = node.operands[0];
489
+ if (operand && operand.type === "identifier") {
490
+ if (!operand.name) return;
491
+ const index = ctx.functionMap.get(operand.name);
492
+ if (index === undefined) {
493
+ throw new Error(`Unknown function: ${operand.name}`);
494
+ }
495
+ ctx.emitVaruint32(index);
496
+ } else if (operand && operand.type === "index") {
497
+ if (operand.value === undefined) return;
498
+ ctx.emitVaruint32(operand.value as number);
499
+ }
500
+ },
501
+
502
+ binary: (ctx: CompilerContext, meta: InstructionMeta, node: ASTNode) => {
503
+ ctx.emitOpcode(meta.opcode);
504
+ // Handle two operands
505
+ // TODO: Implement based on specific instruction needs
506
+ },
507
+
508
+ memory: (ctx: CompilerContext, meta: InstructionMeta, node: ASTNode) => {
509
+ ctx.emitOpcode(meta.opcode);
510
+ // Emit alignment and offset for memory operations
511
+ // Alignment: log2 of natural alignment (e.g., 2 for i32/f32, 3 for i64/f64)
512
+ // Offset: byte offset from the calculated address
513
+
514
+ // Extract align and offset from attributes if present
515
+ let alignment: number | undefined = undefined;
516
+ let offset = 0;
517
+
518
+ if (node.attributes) {
519
+ for (const attr of node.attributes) {
520
+ if (attr.type === "attribute" && attr.name === "align") {
521
+ alignment = typeof attr.value === "number" ? attr.value : parseInt(attr.value as string, 10);
522
+ } else if (attr.type === "attribute" && attr.name === "offset") {
523
+ offset = typeof attr.value === "number" ? attr.value : parseInt(attr.value as string, 10);
524
+ }
525
+ }
526
+ }
527
+
528
+ // If no explicit alignment, use default based on instruction
529
+ if (alignment === undefined) {
530
+ // Default alignment based on instruction type
531
+ // i32/f32 loads/stores: 2 (4-byte alignment)
532
+ // i64/f64 loads/stores: 3 (8-byte alignment)
533
+ // i8 loads/stores: 0 (byte alignment)
534
+ // i16 loads/stores: 1 (2-byte alignment)
535
+ if (meta.opcode >= 0x28 && meta.opcode <= 0x2b || meta.opcode >= 0x36 && meta.opcode <= 0x39) {
536
+ alignment = 2; // i32/f32
537
+ } else if (meta.opcode >= 0x2c && meta.opcode <= 0x2d || meta.opcode === 0x3a) {
538
+ alignment = 0; // i8
539
+ } else if (meta.opcode >= 0x2e && meta.opcode <= 0x2f || meta.opcode === 0x3b) {
540
+ alignment = 1; // i16
541
+ } else {
542
+ alignment = 0; // Default to byte alignment for safety
543
+ }
544
+ }
545
+
546
+ ctx.emitVaruint32(alignment);
547
+ ctx.emitVaruint32(offset);
548
+ },
549
+ };
550
+
551
+ export class WATCompiler {
552
+ private ctx: CompilerContext;
553
+
554
+ constructor() {
555
+ this.ctx = new CompilerContext();
556
+ }
557
+
558
+ compile(ast: ASTNode): Uint8Array {
559
+ // Two-pass compilation
560
+ // Pass 1: Collect types, functions, exports
561
+ this.collectModuleInfo(ast);
562
+
563
+ // Pass 2: Generate binary
564
+ this.generateBinary();
565
+
566
+ return new Uint8Array(this.ctx.buffer);
567
+ }
568
+
569
+ private collectModuleInfo(node: ASTNode) {
570
+ if (node.type === "module") {
571
+ if (node.items) {
572
+ for (const item of node.items) {
573
+ this.collectModuleInfo(item);
574
+ }
575
+ }
576
+ } else if (node.type === "sexpr") {
577
+ this.collectSExpr(node);
578
+ }
579
+ }
580
+
581
+ private collectSExpr(node: ASTNode) {
582
+ if (!node.keyword) return;
583
+
584
+ const keyword = node.keyword;
585
+
586
+ if (keyword === "module") {
587
+ if (node.items) {
588
+ for (const item of node.items) {
589
+ this.collectModuleInfo(item);
590
+ }
591
+ }
592
+ } else if (keyword === "func") {
593
+ this.collectFunction(node);
594
+ } else if (keyword === "memory") {
595
+ this.collectMemory(node);
596
+ } else if (keyword === "export") {
597
+ this.collectExport(node);
598
+ } else if (keyword === "import") {
599
+ this.collectImport(node);
600
+ } else if (keyword === "global") {
601
+ this.collectGlobal(node);
602
+ }
603
+ }
604
+
605
+ private generateBinary() {
606
+ // WebAssembly binary magic number
607
+ this.ctx.emit(0x00);
608
+ this.ctx.emit(0x61);
609
+ this.ctx.emit(0x73);
610
+ this.ctx.emit(0x6d);
611
+
612
+ // Version 1
613
+ this.ctx.emit(0x01);
614
+ this.ctx.emit(0x00);
615
+ this.ctx.emit(0x00);
616
+ this.ctx.emit(0x00);
617
+
618
+ // Generate sections (order matters!)
619
+ this.generateTypeSection();
620
+ this.generateImportSection();
621
+ this.generateFunctionSection();
622
+ this.generateMemorySection();
623
+ this.generateGlobalSection();
624
+ this.generateExportSection();
625
+ this.generateCodeSection();
626
+ }
627
+
628
+ private collectFunction(node: ASTNode) {
629
+ if (!node.items) return;
630
+
631
+ const params: string[] = [];
632
+ const results: string[] = [];
633
+ const locals: string[] = [];
634
+ const body: ASTNode[] = [];
635
+ let exportName: string | undefined = undefined;
636
+ let funcName: string | undefined = undefined;
637
+
638
+ const paramNames: string[] = [];
639
+
640
+ // Check if first item is a function name (e.g., $hashUnitToIndex)
641
+ if (node.items[0] && node.items[0].type === "identifier" && node.items[0].name && node.items[0].name.startsWith("$")) {
642
+ funcName = node.items[0].name;
643
+ }
644
+
645
+ for (const item of node.items) {
646
+ if (item.type === "sexpr" && item.keyword === "param") {
647
+ if (item.items) {
648
+ // Check if this is a named parameter: (param $name type)
649
+ if (item.items.length === 2 &&
650
+ item.items[0].type === "identifier" &&
651
+ item.items[0].name &&
652
+ item.items[0].name.startsWith("$")) {
653
+ // Named parameter
654
+ paramNames.push(item.items[0].name);
655
+ if (item.items[1].type === "identifier" && item.items[1].name) {
656
+ params.push(item.items[1].name);
657
+ }
658
+ } else {
659
+ // Unnamed parameters: (param type1 type2 ...)
660
+ for (const paramItem of item.items) {
661
+ if (paramItem.type === "identifier" && paramItem.name) {
662
+ params.push(paramItem.name);
663
+ paramNames.push(""); // No name for this param
664
+ }
665
+ }
666
+ }
667
+ }
668
+ } else if (item.type === "sexpr" && item.keyword === "result") {
669
+ if (item.items) {
670
+ for (const resultItem of item.items) {
671
+ if (resultItem.type === "identifier" && resultItem.name) {
672
+ results.push(resultItem.name);
673
+ }
674
+ }
675
+ }
676
+ } else if (item.type === "sexpr" && item.keyword === "local") {
677
+ if (item.items) {
678
+ // Check if this is a named local: (local $name type)
679
+ if (item.items.length === 2 &&
680
+ item.items[0].type === "identifier" &&
681
+ item.items[0].name &&
682
+ item.items[0].name.startsWith("$")) {
683
+ // Named local
684
+ paramNames.push(item.items[0].name);
685
+ if (item.items[1].type === "identifier" && item.items[1].name) {
686
+ locals.push(item.items[1].name);
687
+ }
688
+ } else {
689
+ // Unnamed locals: (local type1 type2 ...)
690
+ for (const localItem of item.items) {
691
+ if (localItem.type === "identifier" && localItem.name) {
692
+ locals.push(localItem.name);
693
+ paramNames.push(""); // No name for this local
694
+ }
695
+ }
696
+ }
697
+ }
698
+ } else if (item.type === "sexpr" && item.keyword === "export") {
699
+ if (item.items && item.items[0] && item.items[0].type === "string") {
700
+ exportName = item.items[0].value as string;
701
+ }
702
+ } else if (item.type === "sexpr") {
703
+ body.push(item);
704
+ }
705
+ }
706
+
707
+ // Find or create type
708
+ const funcType: FuncType = { params, results };
709
+ let typeIdx = this.ctx.types.findIndex(t =>
710
+ JSON.stringify(t) === JSON.stringify(funcType)
711
+ );
712
+ if (typeIdx === -1) {
713
+ typeIdx = this.ctx.types.length;
714
+ this.ctx.types.push(funcType);
715
+ }
716
+
717
+ const funcIdx = this.ctx.functions.length;
718
+ this.ctx.functions.push({ typeIdx, locals, localNames: paramNames, body, exportName });
719
+
720
+ // Register function name for internal calls
721
+ if (funcName) {
722
+ this.ctx.functionMap.set(funcName, funcIdx);
723
+ }
724
+
725
+ if (exportName) {
726
+ this.ctx.exports.push({
727
+ name: exportName,
728
+ kind: "func",
729
+ index: funcIdx
730
+ });
731
+ }
732
+ }
733
+
734
+ private collectMemory(node: ASTNode) {
735
+ if (!node.items) return;
736
+
737
+ let min = 1;
738
+ let max: number | undefined = undefined;
739
+
740
+ if (node.items[0] && node.items[0].type === "number") {
741
+ min = parseInt(node.items[0].value as string, 10);
742
+ }
743
+ if (node.items[1] && node.items[1].type === "number") {
744
+ max = parseInt(node.items[1].value as string, 10);
745
+ }
746
+
747
+ this.ctx.memoryPages = { min, max };
748
+ }
749
+
750
+ private collectImport(node: ASTNode) {
751
+ if (!node.items || node.items.length < 3) return;
752
+
753
+ // (import "module" "name" (kind ...))
754
+ const moduleItem = node.items[0];
755
+ const nameItem = node.items[1];
756
+ const descItem = node.items[2];
757
+
758
+ if (moduleItem.type !== "string" || nameItem.type !== "string") return;
759
+ if (descItem.type !== "sexpr" || !descItem.keyword) return;
760
+
761
+ const module = moduleItem.value as string;
762
+ const name = nameItem.value as string;
763
+ const kind = descItem.keyword;
764
+
765
+ if (kind === "memory") {
766
+ this.ctx.hasImportedMemory = true;
767
+ // Memory imports: (import "env" "memory" (memory i64 1))
768
+ // For now, just mark that we have an imported memory
769
+ }
770
+
771
+ this.ctx.imports.push({ module, name, kind, type: descItem });
772
+ }
773
+
774
+ private collectGlobal(node: ASTNode) {
775
+ if (!node.items) return;
776
+
777
+ // Check if this is an exported global: (global (export "name") type (init))
778
+ let exportName: string | undefined = undefined;
779
+ let type = "i32";
780
+ const mutable = false;
781
+ const init: ASTNode[] = [];
782
+
783
+ for (const item of node.items) {
784
+ if (item.type === "sexpr" && item.keyword === "export") {
785
+ if (item.items && item.items[0] && item.items[0].type === "string") {
786
+ exportName = item.items[0].value as string;
787
+ }
788
+ } else if (item.type === "identifier" && item.name) {
789
+ // Type like i32, i64, etc
790
+ type = item.name;
791
+ } else if (item.type === "sexpr") {
792
+ // Initialization expression
793
+ init.push(item);
794
+ }
795
+ }
796
+
797
+ const globalIdx = this.ctx.globals.length;
798
+ this.ctx.globals.push({ type, mutable, init });
799
+
800
+ if (exportName) {
801
+ this.ctx.exports.push({
802
+ name: exportName,
803
+ kind: "global",
804
+ index: globalIdx
805
+ });
806
+ }
807
+ }
808
+
809
+ private collectExport(node: ASTNode) {
810
+ // Exports are collected inline with functions for now
811
+ }
812
+
813
+ private generateTypeSection() {
814
+ if (this.ctx.types.length === 0) return;
815
+
816
+ const section: number[] = [];
817
+ const tempCtx = new CompilerContext();
818
+
819
+ tempCtx.emitVaruint32(this.ctx.types.length);
820
+
821
+ for (const funcType of this.ctx.types) {
822
+ tempCtx.emit(0x60); // func type
823
+ tempCtx.emitVaruint32(funcType.params.length);
824
+ for (const param of funcType.params) {
825
+ tempCtx.emit(this.getTypeCode(param));
826
+ }
827
+ tempCtx.emitVaruint32(funcType.results.length);
828
+ for (const result of funcType.results) {
829
+ tempCtx.emit(this.getTypeCode(result));
830
+ }
831
+ }
832
+
833
+ this.ctx.emitSection(1, tempCtx.buffer);
834
+ }
835
+
836
+ private generateFunctionSection() {
837
+ if (this.ctx.functions.length === 0) return;
838
+
839
+ const tempCtx = new CompilerContext();
840
+ tempCtx.emitVaruint32(this.ctx.functions.length);
841
+
842
+ for (const func of this.ctx.functions) {
843
+ tempCtx.emitVaruint32(func.typeIdx);
844
+ }
845
+
846
+ this.ctx.emitSection(3, tempCtx.buffer);
847
+ }
848
+
849
+ private generateImportSection() {
850
+ if (this.ctx.imports.length === 0) return;
851
+
852
+ const tempCtx = new CompilerContext();
853
+ tempCtx.emitVaruint32(this.ctx.imports.length);
854
+
855
+ for (const imp of this.ctx.imports) {
856
+ // Module name
857
+ tempCtx.emitString(imp.module);
858
+ // Field name
859
+ tempCtx.emitString(imp.name);
860
+
861
+ // Import kind and type
862
+ if (imp.kind === "memory") {
863
+ tempCtx.emit(0x02); // memory import kind
864
+
865
+ // Parse memory descriptor: (memory [i64|i32] min [max])
866
+ // For now, ignore i64/i32 and just parse limits
867
+ let is64 = false;
868
+ let min = 1;
869
+ let max: number | undefined = undefined;
870
+
871
+ if (imp.type && imp.type.items) {
872
+ let numbersSeen = 0;
873
+ for (let i = 0; i < imp.type.items.length; i++) {
874
+ const item = imp.type.items[i];
875
+ if (item.type === "identifier" && item.name === "i64") {
876
+ is64 = true;
877
+ } else if (item.type === "identifier" && item.name === "i32") {
878
+ is64 = false;
879
+ } else if (item.type === "number") {
880
+ if (numbersSeen === 0) {
881
+ min = parseInt(item.value as string, 10);
882
+ } else if (numbersSeen === 1) {
883
+ max = parseInt(item.value as string, 10);
884
+ }
885
+ numbersSeen++;
886
+ }
887
+ }
888
+ }
889
+
890
+ // Emit memory limits with correct flags
891
+ // Flags: bit 0 = has_max, bit 2 = is_64 (memory64)
892
+ let flags = 0;
893
+ if (max !== undefined) flags |= 0x01; // has max
894
+ if (is64) flags |= 0x04; // memory64
895
+
896
+ tempCtx.emit(flags);
897
+ tempCtx.emitVaruint32(min);
898
+ if (max !== undefined) {
899
+ tempCtx.emitVaruint32(max);
900
+ }
901
+ }
902
+ // TODO: Handle other import kinds (func, table, global)
903
+ }
904
+
905
+ this.ctx.emitSection(2, tempCtx.buffer);
906
+ }
907
+
908
+ private generateGlobalSection() {
909
+ if (this.ctx.globals.length === 0) return;
910
+
911
+ const tempCtx = new CompilerContext();
912
+ tempCtx.emitVaruint32(this.ctx.globals.length);
913
+
914
+ for (const global of this.ctx.globals) {
915
+ // Global type
916
+ tempCtx.emit(this.getTypeCode(global.type));
917
+ // Mutability
918
+ tempCtx.emit(global.mutable ? 0x01 : 0x00);
919
+
920
+ // Init expression
921
+ for (const initExpr of global.init) {
922
+ this.compileInstruction(tempCtx, initExpr);
923
+ }
924
+ tempCtx.emit(0x0b); // end of init expression
925
+ }
926
+
927
+ this.ctx.emitSection(6, tempCtx.buffer);
928
+ }
929
+
930
+ private generateMemorySection() {
931
+ if (!this.ctx.memoryPages || this.ctx.hasImportedMemory) return;
932
+
933
+ const tempCtx = new CompilerContext();
934
+ tempCtx.emitVaruint32(1); // number of memories
935
+
936
+ if (this.ctx.memoryPages.max !== undefined) {
937
+ tempCtx.emit(0x01); // has max
938
+ tempCtx.emitVaruint32(this.ctx.memoryPages.min);
939
+ tempCtx.emitVaruint32(this.ctx.memoryPages.max);
940
+ } else {
941
+ tempCtx.emit(0x00); // no max
942
+ tempCtx.emitVaruint32(this.ctx.memoryPages.min);
943
+ }
944
+
945
+ this.ctx.emitSection(5, tempCtx.buffer);
946
+ }
947
+
948
+ private generateExportSection() {
949
+ if (this.ctx.exports.length === 0) return;
950
+
951
+ const tempCtx = new CompilerContext();
952
+ tempCtx.emitVaruint32(this.ctx.exports.length);
953
+
954
+ for (const exp of this.ctx.exports) {
955
+ tempCtx.emitString(exp.name);
956
+ const kindCode = exp.kind === "func" ? 0x00 :
957
+ exp.kind === "table" ? 0x01 :
958
+ exp.kind === "memory" ? 0x02 : 0x03;
959
+ tempCtx.emit(kindCode);
960
+ tempCtx.emitVaruint32(exp.index);
961
+ }
962
+
963
+ this.ctx.emitSection(7, tempCtx.buffer);
964
+ }
965
+
966
+ private generateCodeSection() {
967
+ if (this.ctx.functions.length === 0) return;
968
+
969
+ const tempCtx = new CompilerContext();
970
+ tempCtx.emitVaruint32(this.ctx.functions.length);
971
+
972
+ for (const func of this.ctx.functions) {
973
+ const funcBody = this.compileFunctionBody(func);
974
+ tempCtx.emitVaruint32(funcBody.length);
975
+ for (const byte of funcBody) {
976
+ tempCtx.emit(byte);
977
+ }
978
+ }
979
+
980
+ this.ctx.emitSection(10, tempCtx.buffer);
981
+ }
982
+
983
+ private compileFunctionBody(func: FuncDef): number[] {
984
+ const bodyCtx = new CompilerContext();
985
+
986
+ // Copy function map from module context for internal calls
987
+ bodyCtx.functionMap = this.ctx.functionMap;
988
+
989
+ // Build local map (params first, then locals)
990
+ const funcType = this.ctx.types[func.typeIdx];
991
+ let localIdx = 0;
992
+
993
+ // Map parameter names and indices
994
+ for (let i = 0; i < funcType.params.length; i++) {
995
+ if (func.localNames[i] && func.localNames[i] !== "") {
996
+ bodyCtx.localMap.set(func.localNames[i], localIdx);
997
+ }
998
+ // Also allow numeric access
999
+ bodyCtx.localMap.set(`${i}`, localIdx);
1000
+ localIdx++;
1001
+ }
1002
+
1003
+ // Group locals by type (required by WASM format)
1004
+ const localGroups: Map<string, number> = new Map();
1005
+ for (const local of func.locals) {
1006
+ const count = localGroups.get(local) || 0;
1007
+ localGroups.set(local, count + 1);
1008
+ }
1009
+
1010
+ // Map local variable names based on GROUPED order
1011
+ for (const [type, count] of localGroups) {
1012
+ for (let i = 0; i < func.locals.length; i++) {
1013
+ if (func.locals[i] === type) {
1014
+ const nameIdx = funcType.params.length + i;
1015
+ if (func.localNames[nameIdx] && func.localNames[nameIdx] !== "") {
1016
+ bodyCtx.localMap.set(func.localNames[nameIdx], localIdx);
1017
+ }
1018
+ localIdx++;
1019
+ }
1020
+ }
1021
+ }
1022
+
1023
+ // Emit locals count (grouped by type)
1024
+ bodyCtx.emitVaruint32(localGroups.size);
1025
+ for (const [type, count] of localGroups) {
1026
+ bodyCtx.emitVaruint32(count);
1027
+ bodyCtx.emit(this.getTypeCode(type));
1028
+ }
1029
+
1030
+ // Compile function body
1031
+ for (const instr of func.body) {
1032
+ this.compileInstruction(bodyCtx, instr);
1033
+ }
1034
+
1035
+ // Emit end opcode
1036
+ bodyCtx.emit(0x0b);
1037
+
1038
+ return bodyCtx.buffer;
1039
+ }
1040
+
1041
+ private compileInstruction(ctx: CompilerContext, node: ASTNode) {
1042
+ if (!node.keyword) return;
1043
+
1044
+ // Special handling for block/loop/if structures
1045
+ if (node.keyword === "block" || node.keyword === "loop") {
1046
+ // Emit opcode
1047
+ ctx.emit(node.keyword === "block" ? 0x02 : 0x03);
1048
+ // Emit blocktype (0x40 = void)
1049
+ ctx.emit(0x40);
1050
+
1051
+ // Find and push label onto stack
1052
+ let label = "";
1053
+ if (node.items && node.items[0] && node.items[0].type === "identifier" && node.items[0].name) {
1054
+ label = node.items[0].name;
1055
+ }
1056
+ ctx.labelStack.push(label);
1057
+
1058
+ // Compile body (skip first item if it's a label like $break or $continue)
1059
+ if (node.items) {
1060
+ for (const item of node.items) {
1061
+ // Skip identifiers (labels)
1062
+ if (item.type === "identifier") continue;
1063
+ if (item.type === "sexpr" && item.keyword) {
1064
+ this.compileInstruction(ctx, item);
1065
+ }
1066
+ }
1067
+ }
1068
+
1069
+ ctx.labelStack.pop(); // Pop label
1070
+ ctx.emit(0x0b); // end opcode
1071
+ return;
1072
+ }
1073
+
1074
+ if (node.keyword === "if") {
1075
+ if (node.items) {
1076
+ let thenIdx = -1;
1077
+ let elseIdx = -1;
1078
+ let resultType: string | undefined = undefined;
1079
+
1080
+ // Find then, else, and result clauses
1081
+ for (let i = 0; i < node.items.length; i++) {
1082
+ const item = node.items[i];
1083
+ if (item.type === "sexpr" && item.keyword === "then") {
1084
+ thenIdx = i;
1085
+ } else if (item.type === "sexpr" && item.keyword === "else") {
1086
+ elseIdx = i;
1087
+ } else if (item.type === "sexpr" && item.keyword === "result") {
1088
+ // Extract result type from (result i32)
1089
+ if (item.items && item.items.length > 0) {
1090
+ const typeItem = item.items[0];
1091
+ if (typeItem.type === "identifier" && typeof typeItem.value === "string") {
1092
+ resultType = typeItem.value;
1093
+ }
1094
+ }
1095
+ }
1096
+ }
1097
+
1098
+ // Compile condition BEFORE emitting if opcode (instructions before then)
1099
+ for (let i = 0; i < (thenIdx >= 0 ? thenIdx : node.items.length); i++) {
1100
+ const item = node.items[i];
1101
+ if (item.type === "sexpr" && item.keyword && item.keyword !== "then" && item.keyword !== "else" && item.keyword !== "result") {
1102
+ this.compileInstruction(ctx, item);
1103
+ }
1104
+ }
1105
+
1106
+ // Now emit if opcode
1107
+ ctx.emit(0x04); // if opcode
1108
+ // Emit blocktype: 0x40 for void, or type code for result type
1109
+ if (resultType) {
1110
+ ctx.emit(this.getTypeCode(resultType));
1111
+ } else {
1112
+ ctx.emit(0x40); // blocktype (void)
1113
+ }
1114
+ ctx.labelStack.push(""); // if blocks have no label
1115
+
1116
+ // Compile then clause
1117
+ if (thenIdx >= 0) {
1118
+ const thenClause = node.items[thenIdx];
1119
+ if (thenClause && thenClause.items) {
1120
+ for (const item of thenClause.items) {
1121
+ if (item.type === "sexpr") {
1122
+ if (item.keyword) {
1123
+ this.compileInstruction(ctx, item);
1124
+ }
1125
+ } else if (item.type === "instruction") {
1126
+ this.compileInstruction(ctx, item);
1127
+ }
1128
+ }
1129
+ }
1130
+ }
1131
+
1132
+ // Compile else clause if present
1133
+ if (elseIdx >= 0) {
1134
+ ctx.emit(0x05); // else opcode
1135
+ const elseClause = node.items[elseIdx];
1136
+ if (elseClause && elseClause.items) {
1137
+ for (const item of elseClause.items) {
1138
+ if (item.type === "sexpr") {
1139
+ if (item.keyword) {
1140
+ this.compileInstruction(ctx, item);
1141
+ }
1142
+ } else if (item.type === "instruction") {
1143
+ this.compileInstruction(ctx, item);
1144
+ }
1145
+ }
1146
+ }
1147
+ }
1148
+
1149
+ ctx.labelStack.pop(); // Pop if label
1150
+ ctx.emit(0x0b); // end opcode
1151
+ }
1152
+ return;
1153
+ }
1154
+
1155
+ const meta = INSTRUCTION_MAP[node.keyword];
1156
+ if (!meta) {
1157
+ throw new Error(`Unknown instruction: ${node.keyword}`);
1158
+ }
1159
+
1160
+ const handler = FORM_HANDLERS[meta.form];
1161
+ if (!handler) {
1162
+ throw new Error(`No handler for form: ${meta.form}`);
1163
+ }
1164
+
1165
+ // Compile nested S-expressions BEFORE emitting the parent instruction (depth-first)
1166
+ // This is correct for stack machine semantics
1167
+ // Skip for block/loop which handle their own bodies
1168
+ if (node.keyword !== "block" && node.keyword !== "loop" && node.items) {
1169
+ for (const item of node.items) {
1170
+ if (item.type === "sexpr" && item.keyword) {
1171
+ this.compileInstruction(ctx, item);
1172
+ }
1173
+ }
1174
+ }
1175
+
1176
+ const instructionNode = {
1177
+ ...node,
1178
+ operands: node.items || [],
1179
+ attributes: node.attributes || []
1180
+ };
1181
+
1182
+ handler(ctx, meta, instructionNode);
1183
+ }
1184
+
1185
+ private getTypeCode(typeName: string): number {
1186
+ switch (typeName) {
1187
+ case "i32": return 0x7f;
1188
+ case "i64": return 0x7e;
1189
+ case "f32": return 0x7d;
1190
+ case "f64": return 0x7c;
1191
+ case "v128": return 0x7b;
1192
+ default: throw new Error(`Unknown type: ${typeName}`);
1193
+ }
1194
+ }
1195
+ }
1196
+
1197
+ // Cache the parser so we don't regenerate it every time
1198
+ let cachedParser: { parse: (input: string) => ASTNode } | undefined = undefined;
1199
+
1200
+ function getParser(): { parse: (input: string) => ASTNode } {
1201
+ if (cachedParser === undefined) {
1202
+ const grammarPath = path.join(__dirname, "watGrammar.pegjs");
1203
+ const grammarSource = fs.readFileSync(grammarPath, "utf-8");
1204
+ cachedParser = peggy.generate(grammarSource) as { parse: (input: string) => ASTNode };
1205
+ }
1206
+ return cachedParser;
1207
+ }
1208
+
1209
+ export function parseWAT(source: string): ASTNode {
1210
+ const parser = getParser();
1211
+ try {
1212
+ return parser.parse(source);
1213
+ } catch (e: unknown) {
1214
+ if (e && typeof e === "object" && "location" in e) {
1215
+ const error = e as { location: { start: { line: number; column: number } }; message: string };
1216
+ const loc = error.location.start;
1217
+ throw new Error(`Parse error at line ${loc.line}, column ${loc.column}: ${error.message}`);
1218
+ }
1219
+ throw e;
1220
+ }
1221
+ }
1222
+
1223
+ export function compileWAT(source: string): Uint8Array {
1224
+ const ast = parseWAT(source);
1225
+ const compiler = new WATCompiler();
1226
+ return compiler.compile(ast);
1227
+ }
1228
+
1229
+ // Test function to verify the WAT compiler works
1230
+ export async function testWATCompiler() {
1231
+ console.log("Testing WAT compiler...\n");
1232
+
1233
+ // Test 1: Simple arithmetic function
1234
+ const simpleWAT = `
1235
+ (module
1236
+ (func (export "add") (param i32 i32) (result i32)
1237
+ (local.get 0)
1238
+ (local.get 1)
1239
+ (i32.add)
1240
+ )
1241
+ )
1242
+ `;
1243
+
1244
+ try {
1245
+ const wasmBytes = compileWAT(simpleWAT);
1246
+ console.log(`✓ Compiled ${wasmBytes.length} bytes`);
1247
+
1248
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1249
+ const wasmInstance = new WebAssembly.Instance(wasmModule);
1250
+
1251
+ const add = wasmInstance.exports.add as (a: number, b: number) => number;
1252
+ const result = add(5, 7);
1253
+
1254
+ if (result === 12) {
1255
+ console.log("✓ Test 1 passed: add(5, 7) = 12");
1256
+ } else {
1257
+ console.error(`✗ Test 1 failed: add(5, 7) = ${result}, expected 12`);
1258
+ }
1259
+ } catch (e) {
1260
+ console.error("✗ Test 1 failed with error:", e);
1261
+ }
1262
+
1263
+ // Test 2: Conditional logic with select
1264
+ const conditionalWAT = `
1265
+ (module
1266
+ (func (export "max") (param i32 i32) (result i32)
1267
+ (local.get 0)
1268
+ (local.get 1)
1269
+ (local.get 0)
1270
+ (local.get 1)
1271
+ (i32.gt_s)
1272
+ (select)
1273
+ )
1274
+ )
1275
+ `;
1276
+
1277
+ try {
1278
+ const wasmBytes = compileWAT(conditionalWAT);
1279
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1280
+ const wasmInstance = new WebAssembly.Instance(wasmModule);
1281
+
1282
+ const max = wasmInstance.exports.max as (a: number, b: number) => number;
1283
+ const result1 = max(10, 5);
1284
+ const result2 = max(3, 8);
1285
+
1286
+ if (result1 === 10 && result2 === 8) {
1287
+ console.log("✓ Test 2 passed: max(10, 5) = 10, max(3, 8) = 8");
1288
+ } else {
1289
+ console.error(`✗ Test 2 failed: max(10, 5) = ${result1}, max(3, 8) = ${result2}`);
1290
+ }
1291
+ } catch (e) {
1292
+ console.error("✗ Test 2 failed with error:", e);
1293
+ }
1294
+
1295
+ // Test 3: Multiple operations
1296
+ const multiOpWAT = `
1297
+ (module
1298
+ (func (export "compute") (param i32) (result i32)
1299
+ (local.get 0)
1300
+ (i32.const 10)
1301
+ (i32.mul)
1302
+ (i32.const 5)
1303
+ (i32.add)
1304
+ )
1305
+ )
1306
+ `;
1307
+
1308
+ try {
1309
+ const wasmBytes = compileWAT(multiOpWAT);
1310
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1311
+ const wasmInstance = new WebAssembly.Instance(wasmModule);
1312
+
1313
+ const compute = wasmInstance.exports.compute as (a: number) => number;
1314
+ const result = compute(3);
1315
+
1316
+ if (result === 35) {
1317
+ console.log("✓ Test 3 passed: compute(3) = 35 (3 * 10 + 5)");
1318
+ } else {
1319
+ console.error(`✗ Test 3 failed: compute(3) = ${result}, expected 35`);
1320
+ }
1321
+ } catch (e) {
1322
+ console.error("✗ Test 3 failed with error:", e);
1323
+ }
1324
+
1325
+ // Test 4: Subtraction and division
1326
+ const mathWAT = `
1327
+ (module
1328
+ (func (export "calculate") (param i32 i32) (result i32)
1329
+ (local.get 0)
1330
+ (local.get 1)
1331
+ (i32.sub)
1332
+ (i32.const 2)
1333
+ (i32.div_s)
1334
+ )
1335
+ )
1336
+ `;
1337
+
1338
+ try {
1339
+ const wasmBytes = compileWAT(mathWAT);
1340
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1341
+ const wasmInstance = new WebAssembly.Instance(wasmModule);
1342
+
1343
+ const calculate = wasmInstance.exports.calculate as (a: number, b: number) => number;
1344
+ const result = calculate(20, 10);
1345
+
1346
+ if (result === 5) {
1347
+ console.log("✓ Test 4 passed: calculate(20, 10) = 5 ((20 - 10) / 2)");
1348
+ } else {
1349
+ console.error(`✗ Test 4 failed: calculate(20, 10) = ${result}, expected 5`);
1350
+ }
1351
+ } catch (e) {
1352
+ console.error("✗ Test 4 failed with error:", e);
1353
+ }
1354
+
1355
+ // Test 5: Local.set with named parameters
1356
+ const localSetWAT = `
1357
+ (module
1358
+ (func (export "test") (param $a i32) (result i32)
1359
+ (local $b i32)
1360
+ (local.set $b (i32.add (local.get $a) (i32.const 5)))
1361
+ (local.get $b)
1362
+ )
1363
+ )
1364
+ `;
1365
+
1366
+ try {
1367
+ const wasmBytes = compileWAT(localSetWAT);
1368
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1369
+ const wasmInstance = new WebAssembly.Instance(wasmModule);
1370
+
1371
+ const test = wasmInstance.exports.test as (a: number) => number;
1372
+ const result = test(10);
1373
+
1374
+ if (result === 15) {
1375
+ console.log("✓ Test 5 passed: test(10) = 15 (10 + 5)");
1376
+ } else {
1377
+ console.error(`✗ Test 5 failed: test(10) = ${result}, expected 15`);
1378
+ }
1379
+ } catch (e) {
1380
+ console.error("✗ Test 5 failed with error:", e);
1381
+ }
1382
+
1383
+ // Test 6: Block with local.set
1384
+ const blockWAT = `
1385
+ (module
1386
+ (func (export "test") (param $a i32) (result i32)
1387
+ (local $b i32)
1388
+ (local.set $b (i32.const 0))
1389
+ (block $myblock
1390
+ (local.set $b (i32.add (local.get $a) (i32.const 10)))
1391
+ )
1392
+ (local.get $b)
1393
+ )
1394
+ )
1395
+ `;
1396
+
1397
+ try {
1398
+ const wasmBytes = compileWAT(blockWAT);
1399
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1400
+ const wasmInstance = new WebAssembly.Instance(wasmModule);
1401
+
1402
+ const test = wasmInstance.exports.test as (a: number) => number;
1403
+ const result = test(5);
1404
+
1405
+ if (result === 15) {
1406
+ console.log("✓ Test 6 passed: test(5) = 15 (block with local.set)");
1407
+ } else {
1408
+ console.error(`✗ Test 6 failed: test(5) = ${result}, expected 15`);
1409
+ }
1410
+ } catch (e) {
1411
+ console.error("✗ Test 6 failed with error:", e);
1412
+ }
1413
+
1414
+ // Test 7: Many parameters and locals
1415
+ const manyLocalsWAT = `
1416
+ (module
1417
+ (func (export "test")
1418
+ (param $a i32) (param $b i32) (param $c i32) (param $d i32)
1419
+ (local $e i32) (local $f i32) (local $g i32)
1420
+ (local.set $e (i32.const 1))
1421
+ (local.set $f (i32.const 2))
1422
+ (local.set $g (i32.add (local.get $e) (local.get $f)))
1423
+ )
1424
+ )
1425
+ `;
1426
+
1427
+ try {
1428
+ const wasmBytes = compileWAT(manyLocalsWAT);
1429
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1430
+ console.log("✓ Test 7 passed: many parameters and locals compiled");
1431
+ } catch (e) {
1432
+ console.error("✗ Test 7 failed with error:", e);
1433
+ }
1434
+
1435
+ // Test 8: BufferIndex excerpt
1436
+ const bufferIndexExcerpt = `(module
1437
+ (func (export "phase1_count_deduplicate")
1438
+ (param $unitRefsPtr i32)
1439
+ (param $totalUnits i32)
1440
+ (param $hashTablePtr i32)
1441
+ (param $hashTableCapacity i32)
1442
+ (param $uniqueUnitListPtr i32)
1443
+ (param $maxUniqueCount i32)
1444
+ (result i32)
1445
+
1446
+ (local $i i32)
1447
+ (local $hashTableSize i32)
1448
+
1449
+ (local.set $hashTableSize
1450
+ (i32.mul (local.get $hashTableCapacity) (i32.const 16)))
1451
+
1452
+ (local.set $i (i32.const 0))
1453
+
1454
+ (local.get $i)
1455
+ )
1456
+ )`;
1457
+
1458
+ try {
1459
+ const wasmBytes = compileWAT(bufferIndexExcerpt);
1460
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1461
+ const wasmInstance = new WebAssembly.Instance(wasmModule);
1462
+ const fn = wasmInstance.exports.phase1_count_deduplicate as (...args: number[]) => number;
1463
+ const result = fn(0, 0, 0, 16, 0, 0);
1464
+ console.log(`✓ Test 8 passed: BufferIndex excerpt compiled, result = ${result}`);
1465
+ } catch (e) {
1466
+ console.error("✗ Test 8 failed with error:", e);
1467
+ }
1468
+
1469
+ // Test 9: Memory operations (i32.store)
1470
+ const memoryWAT = `(module
1471
+ (memory 1)
1472
+ (func (export "test") (result i32)
1473
+ (local $addr i32)
1474
+ (local.set $addr (i32.const 0))
1475
+ (i32.store (local.get $addr) (i32.const 42))
1476
+ (i32.load (local.get $addr))
1477
+ )
1478
+ )`;
1479
+
1480
+ try {
1481
+ const wasmBytes = compileWAT(memoryWAT);
1482
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1483
+ const wasmInstance = new WebAssembly.Instance(wasmModule);
1484
+ const fn = wasmInstance.exports.test as () => number;
1485
+ const result = fn();
1486
+ if (result === 42) {
1487
+ console.log("✓ Test 9 passed: memory operations work");
1488
+ } else {
1489
+ console.error(`✗ Test 9 failed: expected 42, got ${result}`);
1490
+ }
1491
+ } catch (e) {
1492
+ console.error("✗ Test 9 failed with error:", e);
1493
+ }
1494
+
1495
+ // Test 10: Loop with if/then
1496
+ const loopIfWAT = `(module
1497
+ (func (export "test") (param $max i32) (result i32)
1498
+ (local $i i32)
1499
+ (local $sum i32)
1500
+ (local.set $i (i32.const 0))
1501
+ (local.set $sum (i32.const 0))
1502
+ (block $break
1503
+ (loop $continue
1504
+ (br_if $break (i32.ge_u (local.get $i) (local.get $max)))
1505
+ (if (i32.eq (i32.rem_u (local.get $i) (i32.const 2)) (i32.const 0))
1506
+ (then
1507
+ (local.set $sum (i32.add (local.get $sum) (local.get $i)))
1508
+ )
1509
+ )
1510
+ (local.set $i (i32.add (local.get $i) (i32.const 1)))
1511
+ (br $continue)
1512
+ )
1513
+ )
1514
+ (local.get $sum)
1515
+ )
1516
+ )`;
1517
+
1518
+ try {
1519
+ const wasmBytes = compileWAT(loopIfWAT);
1520
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1521
+ const wasmInstance = new WebAssembly.Instance(wasmModule);
1522
+ const fn = wasmInstance.exports.test as (max: number) => number;
1523
+ const result = fn(10);
1524
+ // Sum of even numbers 0,2,4,6,8 = 20
1525
+ if (result === 20) {
1526
+ console.log("✓ Test 10 passed: loop with if/then works");
1527
+ } else {
1528
+ console.error(`✗ Test 10 failed: expected 20, got ${result}`);
1529
+ }
1530
+ } catch (e) {
1531
+ console.error("✗ Test 10 failed with error:", e);
1532
+ }
1533
+
1534
+ // Test 11: Nested multi-line expression in memory operation
1535
+ const nestedLoadWAT = `(module
1536
+ (memory 1)
1537
+ (func (export "test") (param $ptr i32) (param $i i32) (result i32)
1538
+ (local $unit i32)
1539
+ (local.set $unit
1540
+ (i32.load (i32.add (local.get $ptr)
1541
+ (i32.mul (local.get $i) (i32.const 12)))))
1542
+ (local.get $unit)
1543
+ )
1544
+ )`;
1545
+
1546
+ try {
1547
+ const wasmBytes = compileWAT(nestedLoadWAT);
1548
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1549
+ console.log("✓ Test 11 passed: nested multi-line expression compiled");
1550
+ } catch (e) {
1551
+ console.error("✗ Test 11 failed with error:", e);
1552
+ }
1553
+
1554
+ // Test 12: Memory64 with i64 pointers
1555
+ const memory64WAT = `(module
1556
+ (import "env" "memory" (memory i64 1))
1557
+ (func (export "write_read") (param $ptr i64) (param $value i32) (result i32)
1558
+ (i32.store (local.get $ptr) (local.get $value))
1559
+ (i32.load (local.get $ptr))
1560
+ )
1561
+ )`;
1562
+
1563
+ try {
1564
+ const wasmBytes = compileWAT(memory64WAT);
1565
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1566
+ const memory = new WebAssembly.Memory({ initial: BigInt(1), address: "i64" } as any);
1567
+ const wasmInstance = new WebAssembly.Instance(wasmModule, { env: { memory } });
1568
+ const fn = wasmInstance.exports.write_read as (ptr: bigint, value: number) => number;
1569
+ const result = fn(BigInt(0), 42);
1570
+ if (result === 42) {
1571
+ console.log("✓ Test 12 passed: memory64 write/read with i64 pointers");
1572
+ } else {
1573
+ console.error(`✗ Test 12 failed: expected 42, got ${result}`);
1574
+ }
1575
+ } catch (e) {
1576
+ console.error("✗ Test 12 failed with error:", e);
1577
+ }
1578
+
1579
+ // Test 13: Memory64 with i64 address arithmetic
1580
+ const memory64ArithmeticWAT = `(module
1581
+ (import "env" "memory" (memory i64 1))
1582
+ (func (export "store_at_offset") (param $base i64) (param $offset i32) (param $value i32)
1583
+ (i32.store
1584
+ (i64.add (local.get $base) (i64.extend_i32_u (local.get $offset)))
1585
+ (local.get $value))
1586
+ )
1587
+ (func (export "load_from_offset") (param $base i64) (param $offset i32) (result i32)
1588
+ (i32.load
1589
+ (i64.add (local.get $base) (i64.extend_i32_u (local.get $offset))))
1590
+ )
1591
+ )`;
1592
+
1593
+ try {
1594
+ const wasmBytes = compileWAT(memory64ArithmeticWAT);
1595
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1596
+ const memory = new WebAssembly.Memory({ initial: BigInt(1), address: "i64" } as any);
1597
+ const wasmInstance = new WebAssembly.Instance(wasmModule, { env: { memory } });
1598
+ const store = wasmInstance.exports.store_at_offset as (base: bigint, offset: number, value: number) => void;
1599
+ const load = wasmInstance.exports.load_from_offset as (base: bigint, offset: number) => number;
1600
+
1601
+ store(BigInt(100), 8, 123);
1602
+ const result = load(BigInt(100), 8);
1603
+ if (result === 123) {
1604
+ console.log("✓ Test 13 passed: memory64 with i64 address arithmetic");
1605
+ } else {
1606
+ console.error(`✗ Test 13 failed: expected 123, got ${result}`);
1607
+ }
1608
+ } catch (e) {
1609
+ console.error("✗ Test 13 failed with error:", e);
1610
+ }
1611
+
1612
+ // Test 14: Memory64 with array-like access pattern
1613
+ const memory64ArrayWAT = `(module
1614
+ (import "env" "memory" (memory i64 1))
1615
+ (func (export "write_array") (param $basePtr i64) (param $index i32) (param $value i32)
1616
+ (i32.store
1617
+ (i64.add (local.get $basePtr)
1618
+ (i64.extend_i32_u (i32.mul (local.get $index) (i32.const 4))))
1619
+ (local.get $value))
1620
+ )
1621
+ (func (export "read_array") (param $basePtr i64) (param $index i32) (result i32)
1622
+ (i32.load
1623
+ (i64.add (local.get $basePtr)
1624
+ (i64.extend_i32_u (i32.mul (local.get $index) (i32.const 4)))))
1625
+ )
1626
+ (func (export "sum_array") (param $basePtr i64) (param $count i32) (result i32)
1627
+ (local $i i32)
1628
+ (local $sum i32)
1629
+ (local.set $i (i32.const 0))
1630
+ (local.set $sum (i32.const 0))
1631
+ (block $break
1632
+ (loop $continue
1633
+ (br_if $break (i32.ge_u (local.get $i) (local.get $count)))
1634
+ (local.set $sum
1635
+ (i32.add (local.get $sum)
1636
+ (i32.load
1637
+ (i64.add (local.get $basePtr)
1638
+ (i64.extend_i32_u (i32.mul (local.get $i) (i32.const 4)))))))
1639
+ (local.set $i (i32.add (local.get $i) (i32.const 1)))
1640
+ (br $continue)
1641
+ )
1642
+ )
1643
+ (local.get $sum)
1644
+ )
1645
+ )`;
1646
+
1647
+ try {
1648
+ const wasmBytes = compileWAT(memory64ArrayWAT);
1649
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1650
+ const memory = new WebAssembly.Memory({ initial: BigInt(1), address: "i64" } as any);
1651
+ const wasmInstance = new WebAssembly.Instance(wasmModule, { env: { memory } });
1652
+ const writeArray = wasmInstance.exports.write_array as (basePtr: bigint, index: number, value: number) => void;
1653
+ const readArray = wasmInstance.exports.read_array as (basePtr: bigint, index: number) => number;
1654
+ const sumArray = wasmInstance.exports.sum_array as (basePtr: bigint, count: number) => number;
1655
+
1656
+ // Write values [10, 20, 30, 40, 50]
1657
+ writeArray(BigInt(1000), 0, 10);
1658
+ writeArray(BigInt(1000), 1, 20);
1659
+ writeArray(BigInt(1000), 2, 30);
1660
+ writeArray(BigInt(1000), 3, 40);
1661
+ writeArray(BigInt(1000), 4, 50);
1662
+
1663
+ // Verify reads
1664
+ const val0 = readArray(BigInt(1000), 0);
1665
+ const val2 = readArray(BigInt(1000), 2);
1666
+ const val4 = readArray(BigInt(1000), 4);
1667
+
1668
+ // Sum array
1669
+ const sum = sumArray(BigInt(1000), 5);
1670
+
1671
+ if (val0 === 10 && val2 === 30 && val4 === 50 && sum === 150) {
1672
+ console.log("✓ Test 14 passed: memory64 array operations (sum = 150)");
1673
+ } else {
1674
+ console.error(`✗ Test 14 failed: val0=${val0}, val2=${val2}, val4=${val4}, sum=${sum}`);
1675
+ }
1676
+ } catch (e) {
1677
+ console.error("✗ Test 14 failed with error:", e);
1678
+ }
1679
+
1680
+ // Test 15: Memory64 large offset (beyond 4GB-like address)
1681
+ const memory64LargeOffsetWAT = `(module
1682
+ (import "env" "memory" (memory i64 1))
1683
+ (func (export "test_large_ptr") (param $ptr i64) (param $value i32) (result i32)
1684
+ (i32.store (local.get $ptr) (local.get $value))
1685
+ (i32.load (local.get $ptr))
1686
+ )
1687
+ )`;
1688
+
1689
+ try {
1690
+ const wasmBytes = compileWAT(memory64LargeOffsetWAT);
1691
+ const wasmModule = new WebAssembly.Module(wasmBytes);
1692
+ const memory = new WebAssembly.Memory({ initial: BigInt(1), address: "i64" } as any);
1693
+ const wasmInstance = new WebAssembly.Instance(wasmModule, { env: { memory } });
1694
+ const fn = wasmInstance.exports.test_large_ptr as (ptr: bigint, value: number) => number;
1695
+
1696
+ // Test with a large address within first page (< 64KB)
1697
+ const largeAddr = BigInt(60000);
1698
+ const result = fn(largeAddr, 999);
1699
+ if (result === 999) {
1700
+ console.log("✓ Test 15 passed: memory64 with large address (60000)");
1701
+ } else {
1702
+ console.error(`✗ Test 15 failed: expected 999, got ${result}`);
1703
+ }
1704
+ } catch (e) {
1705
+ console.error("✗ Test 15 failed with error:", e);
1706
+ }
1707
+
1708
+ // Test 16: Instruction count
1709
+ const totalInstructions = Object.keys(INSTRUCTION_MAP).length;
1710
+ console.log(`\n✓ Total WebAssembly instructions loaded: ${totalInstructions}`);
1711
+
1712
+ console.log("\nWAT compiler tests complete!");
1713
+ }
1714
+
1715
+ // Run tests
1716
+ //testWATCompiler().catch(console.error);