@fraczak/k 6.4.0 → 6.6.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.
- package/CHANGELOG.md +17 -0
- package/Examples/arithmetics.k +86 -63
- package/Value.mjs +8 -2
- package/backends/arm64/src/runtime.c +20 -4
- package/backends/arm64/tests/integration.sh +1 -1
- package/backends/llvm/runtime/krt.c +387 -2
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/src/kvm2llvm.mjs +98 -29
- package/backends/llvm/tests/perf-support.mjs +1 -1
- package/backends/wasm/TECHNICAL-DEBT.md +11 -13
- package/backends/wasm/runtime.wat +375 -2
- package/backends/wasm/src/kvm2wasm.mjs +36 -0
- package/backends/wasm/src/wasm.mjs +48 -4
- package/backends/wasm/tests/test-wasm.mjs +8 -0
- package/browser/repl-browser.mjs +24 -7
- package/browser/tree-view.mjs +599 -0
- package/browser/vfs-data.mjs +3 -3
- package/codecs/runtime/show-value.mjs +10 -1
- package/package.json +1 -1
- package/repl.mjs +4 -0
- package/scripts/build-repl-html.mjs +301 -0
- package/scripts/perf-ieee.mjs +5 -2
- package/scripts/perf-int.mjs +331 -0
- package/scripts/run-tests.mjs +1 -0
- package/tests/test-repl-html.mjs +141 -0
- package/tests/test-tree-view.mjs +185 -0
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
(memory (export "memory") 1)
|
|
3
3
|
|
|
4
4
|
;; Start allocation at byte offset 1024 to leave a safety margin and avoid returning 0 (nullptr)
|
|
5
|
-
(global $arena_free (mut i32) (i32.const 1024))
|
|
5
|
+
(global $arena_free (export "arena_free") (mut i32) (i32.const 1024))
|
|
6
6
|
;; Maximum address of the currently allocated pages (1 page = 64KB)
|
|
7
|
-
(global $arena_max (mut i32) (i32.const 65536))
|
|
7
|
+
(global $arena_max (export "arena_max") (mut i32) (i32.const 65536))
|
|
8
|
+
;; Out-of-memory status flags
|
|
9
|
+
(global $oom_flag (export "oom_flag") (mut i32) (i32.const 0))
|
|
10
|
+
(global $oom_size (export "oom_size") (mut i32) (i32.const 0))
|
|
8
11
|
|
|
9
12
|
;; Bump allocator function: aligns allocation size to 8 bytes, grows memory if needed, and returns the pointer
|
|
10
13
|
(func $alloc (export "alloc") (param $size i32) (result i32)
|
|
@@ -13,6 +16,18 @@
|
|
|
13
16
|
(local $pages_needed i32)
|
|
14
17
|
(local $grow_res i32)
|
|
15
18
|
|
|
19
|
+
;; Check if size exceeds maximum allocatable size
|
|
20
|
+
local.get $size
|
|
21
|
+
i32.const -8
|
|
22
|
+
i32.gt_u
|
|
23
|
+
if
|
|
24
|
+
i32.const 1
|
|
25
|
+
global.set $oom_flag
|
|
26
|
+
local.get $size
|
|
27
|
+
global.set $oom_size
|
|
28
|
+
unreachable
|
|
29
|
+
end
|
|
30
|
+
|
|
16
31
|
;; 1. Align size to 8 bytes: size = (size + 7) & ~8
|
|
17
32
|
local.get $size
|
|
18
33
|
i32.const 7
|
|
@@ -31,6 +46,18 @@
|
|
|
31
46
|
i32.add
|
|
32
47
|
local.set $new_free
|
|
33
48
|
|
|
49
|
+
;; Check for 32-bit unsigned overflow
|
|
50
|
+
local.get $new_free
|
|
51
|
+
local.get $old_free
|
|
52
|
+
i32.lt_u
|
|
53
|
+
if
|
|
54
|
+
i32.const 1
|
|
55
|
+
global.set $oom_flag
|
|
56
|
+
local.get $size
|
|
57
|
+
global.set $oom_size
|
|
58
|
+
unreachable
|
|
59
|
+
end
|
|
60
|
+
|
|
34
61
|
;; 2. Check if we exceed current memory size
|
|
35
62
|
local.get $new_free
|
|
36
63
|
global.get $arena_max
|
|
@@ -56,6 +83,10 @@
|
|
|
56
83
|
i32.const -1
|
|
57
84
|
i32.eq
|
|
58
85
|
if
|
|
86
|
+
i32.const 1
|
|
87
|
+
global.set $oom_flag
|
|
88
|
+
local.get $size
|
|
89
|
+
global.set $oom_size
|
|
59
90
|
unreachable
|
|
60
91
|
end
|
|
61
92
|
|
|
@@ -99,4 +130,346 @@
|
|
|
99
130
|
local.get $mark
|
|
100
131
|
global.set $arena_free
|
|
101
132
|
)
|
|
133
|
+
|
|
134
|
+
;; Compaction staging globals
|
|
135
|
+
(global $compact_buf_start (mut i32) (i32.const 0))
|
|
136
|
+
(global $compact_buf_free (mut i32) (i32.const 0))
|
|
137
|
+
|
|
138
|
+
;; Ensure memory capacity in staging buffer
|
|
139
|
+
(func $ensure_buf_capacity (param $size i32)
|
|
140
|
+
(local $needed i32)
|
|
141
|
+
(local $pages i32)
|
|
142
|
+
(local $grow_res i32)
|
|
143
|
+
|
|
144
|
+
global.get $compact_buf_free
|
|
145
|
+
local.get $size
|
|
146
|
+
i32.add
|
|
147
|
+
local.set $needed
|
|
148
|
+
|
|
149
|
+
;; Check for 32-bit overflow
|
|
150
|
+
local.get $needed
|
|
151
|
+
global.get $compact_buf_free
|
|
152
|
+
i32.lt_u
|
|
153
|
+
if
|
|
154
|
+
i32.const 1
|
|
155
|
+
global.set $oom_flag
|
|
156
|
+
local.get $size
|
|
157
|
+
global.set $oom_size
|
|
158
|
+
unreachable
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
local.get $needed
|
|
162
|
+
global.get $arena_max
|
|
163
|
+
i32.gt_u
|
|
164
|
+
if
|
|
165
|
+
local.get $needed
|
|
166
|
+
global.get $arena_max
|
|
167
|
+
i32.sub
|
|
168
|
+
i32.const 65535
|
|
169
|
+
i32.add
|
|
170
|
+
i32.const 65536
|
|
171
|
+
i32.div_u
|
|
172
|
+
local.set $pages
|
|
173
|
+
|
|
174
|
+
local.get $pages
|
|
175
|
+
memory.grow
|
|
176
|
+
local.set $grow_res
|
|
177
|
+
|
|
178
|
+
local.get $grow_res
|
|
179
|
+
i32.const -1
|
|
180
|
+
i32.eq
|
|
181
|
+
if
|
|
182
|
+
i32.const 1
|
|
183
|
+
global.set $oom_flag
|
|
184
|
+
local.get $size
|
|
185
|
+
global.set $oom_size
|
|
186
|
+
unreachable
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
global.get $arena_max
|
|
190
|
+
local.get $pages
|
|
191
|
+
i32.const 65536
|
|
192
|
+
i32.mul
|
|
193
|
+
i32.add
|
|
194
|
+
global.set $arena_max
|
|
195
|
+
end
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
;; Initialize compaction staging buffer at current arena top
|
|
199
|
+
(func $compact_start (export "compact_start")
|
|
200
|
+
global.get $arena_free
|
|
201
|
+
global.set $compact_buf_start
|
|
202
|
+
global.get $arena_free
|
|
203
|
+
global.set $compact_buf_free
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
;; Compact an object graph reachable from $root down toward $mark
|
|
207
|
+
(func $compact_obj (export "compact_obj") (param $root i32) (param $mark i32) (result i32)
|
|
208
|
+
(local $curr i32)
|
|
209
|
+
(local $first_final i32)
|
|
210
|
+
(local $prev_dest i32)
|
|
211
|
+
(local $dest i32)
|
|
212
|
+
(local $final_ptr i32)
|
|
213
|
+
(local $tag i32)
|
|
214
|
+
(local $payload i32)
|
|
215
|
+
(local $size i32)
|
|
216
|
+
(local $alignedSize i32)
|
|
217
|
+
(local $N i32)
|
|
218
|
+
(local $i i32)
|
|
219
|
+
(local $offsetVal i32)
|
|
220
|
+
(local $fieldPtr i32)
|
|
221
|
+
(local $child_final i32)
|
|
222
|
+
|
|
223
|
+
local.get $root
|
|
224
|
+
local.set $curr
|
|
225
|
+
i32.const 0
|
|
226
|
+
local.set $prev_dest
|
|
227
|
+
|
|
228
|
+
(block $break_chain
|
|
229
|
+
(loop $chain_loop
|
|
230
|
+
;; 1. Check null / empty
|
|
231
|
+
local.get $curr
|
|
232
|
+
i32.eqz
|
|
233
|
+
if
|
|
234
|
+
local.get $prev_dest
|
|
235
|
+
if
|
|
236
|
+
local.get $prev_dest
|
|
237
|
+
i32.const 0
|
|
238
|
+
i32.store offset=8
|
|
239
|
+
end
|
|
240
|
+
br $break_chain
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
;; 2. Check if < mark
|
|
244
|
+
local.get $curr
|
|
245
|
+
local.get $mark
|
|
246
|
+
i32.lt_u
|
|
247
|
+
if
|
|
248
|
+
local.get $prev_dest
|
|
249
|
+
if
|
|
250
|
+
local.get $prev_dest
|
|
251
|
+
local.get $curr
|
|
252
|
+
i32.store offset=8
|
|
253
|
+
end
|
|
254
|
+
local.get $first_final
|
|
255
|
+
i32.eqz
|
|
256
|
+
if
|
|
257
|
+
local.get $curr
|
|
258
|
+
local.set $first_final
|
|
259
|
+
end
|
|
260
|
+
br $break_chain
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
;; 3. Check if forwarded
|
|
264
|
+
local.get $curr
|
|
265
|
+
i32.load offset=0
|
|
266
|
+
local.set $size
|
|
267
|
+
|
|
268
|
+
local.get $size
|
|
269
|
+
i32.const -1
|
|
270
|
+
i32.eq
|
|
271
|
+
if
|
|
272
|
+
local.get $curr
|
|
273
|
+
i32.load offset=4
|
|
274
|
+
local.set $final_ptr
|
|
275
|
+
local.get $prev_dest
|
|
276
|
+
if
|
|
277
|
+
local.get $prev_dest
|
|
278
|
+
local.get $final_ptr
|
|
279
|
+
i32.store offset=8
|
|
280
|
+
end
|
|
281
|
+
local.get $first_final
|
|
282
|
+
i32.eqz
|
|
283
|
+
if
|
|
284
|
+
local.get $final_ptr
|
|
285
|
+
local.set $first_final
|
|
286
|
+
end
|
|
287
|
+
br $break_chain
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
;; 4. Compute final_ptr = mark + (compact_buf_free - compact_buf_start)
|
|
291
|
+
local.get $mark
|
|
292
|
+
global.get $compact_buf_free
|
|
293
|
+
global.get $compact_buf_start
|
|
294
|
+
i32.sub
|
|
295
|
+
i32.add
|
|
296
|
+
local.set $final_ptr
|
|
297
|
+
|
|
298
|
+
local.get $first_final
|
|
299
|
+
i32.eqz
|
|
300
|
+
if
|
|
301
|
+
local.get $final_ptr
|
|
302
|
+
local.set $first_final
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
;; If prev_dest != 0, patch its payload to final_ptr
|
|
306
|
+
local.get $prev_dest
|
|
307
|
+
if
|
|
308
|
+
local.get $prev_dest
|
|
309
|
+
local.get $final_ptr
|
|
310
|
+
i32.store offset=8
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
;; 5. Check if Variant (size == 12)
|
|
314
|
+
local.get $size
|
|
315
|
+
i32.const 12
|
|
316
|
+
i32.eq
|
|
317
|
+
if
|
|
318
|
+
i32.const 16
|
|
319
|
+
call $ensure_buf_capacity
|
|
320
|
+
|
|
321
|
+
local.get $curr
|
|
322
|
+
i32.load offset=4
|
|
323
|
+
local.set $tag
|
|
324
|
+
local.get $curr
|
|
325
|
+
i32.load offset=8
|
|
326
|
+
local.set $payload
|
|
327
|
+
|
|
328
|
+
local.get $curr
|
|
329
|
+
i32.const -1
|
|
330
|
+
i32.store offset=0
|
|
331
|
+
local.get $curr
|
|
332
|
+
local.get $final_ptr
|
|
333
|
+
i32.store offset=4
|
|
334
|
+
|
|
335
|
+
global.get $compact_buf_free
|
|
336
|
+
local.set $dest
|
|
337
|
+
global.get $compact_buf_free
|
|
338
|
+
i32.const 16
|
|
339
|
+
i32.add
|
|
340
|
+
global.set $compact_buf_free
|
|
341
|
+
|
|
342
|
+
local.get $dest
|
|
343
|
+
i32.const 12
|
|
344
|
+
i32.store offset=0
|
|
345
|
+
local.get $dest
|
|
346
|
+
local.get $tag
|
|
347
|
+
i32.store offset=4
|
|
348
|
+
|
|
349
|
+
local.get $dest
|
|
350
|
+
local.set $prev_dest
|
|
351
|
+
local.get $payload
|
|
352
|
+
local.set $curr
|
|
353
|
+
br $chain_loop
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
;; 6. Product handling (size != 12)
|
|
357
|
+
local.get $size
|
|
358
|
+
i32.const 7
|
|
359
|
+
i32.add
|
|
360
|
+
i32.const -8
|
|
361
|
+
i32.and
|
|
362
|
+
local.set $alignedSize
|
|
363
|
+
|
|
364
|
+
local.get $alignedSize
|
|
365
|
+
call $ensure_buf_capacity
|
|
366
|
+
|
|
367
|
+
local.get $curr
|
|
368
|
+
i32.load offset=4
|
|
369
|
+
local.set $N
|
|
370
|
+
|
|
371
|
+
local.get $curr
|
|
372
|
+
i32.const -1
|
|
373
|
+
i32.store offset=0
|
|
374
|
+
local.get $curr
|
|
375
|
+
local.get $final_ptr
|
|
376
|
+
i32.store offset=4
|
|
377
|
+
|
|
378
|
+
global.get $compact_buf_free
|
|
379
|
+
local.set $dest
|
|
380
|
+
global.get $compact_buf_free
|
|
381
|
+
local.get $alignedSize
|
|
382
|
+
i32.add
|
|
383
|
+
global.set $compact_buf_free
|
|
384
|
+
|
|
385
|
+
local.get $dest
|
|
386
|
+
local.get $size
|
|
387
|
+
i32.store offset=0
|
|
388
|
+
local.get $dest
|
|
389
|
+
local.get $N
|
|
390
|
+
i32.store offset=4
|
|
391
|
+
|
|
392
|
+
i32.const 0
|
|
393
|
+
local.set $i
|
|
394
|
+
(block $break_prod
|
|
395
|
+
(loop $loop_prod
|
|
396
|
+
local.get $i
|
|
397
|
+
local.get $N
|
|
398
|
+
i32.ge_u
|
|
399
|
+
br_if $break_prod
|
|
400
|
+
|
|
401
|
+
local.get $curr
|
|
402
|
+
i32.const 8
|
|
403
|
+
local.get $i
|
|
404
|
+
i32.const 4
|
|
405
|
+
i32.mul
|
|
406
|
+
i32.add
|
|
407
|
+
i32.add
|
|
408
|
+
i32.load
|
|
409
|
+
local.set $offsetVal
|
|
410
|
+
|
|
411
|
+
local.get $dest
|
|
412
|
+
i32.const 8
|
|
413
|
+
local.get $i
|
|
414
|
+
i32.const 4
|
|
415
|
+
i32.mul
|
|
416
|
+
i32.add
|
|
417
|
+
i32.add
|
|
418
|
+
local.get $offsetVal
|
|
419
|
+
i32.store
|
|
420
|
+
|
|
421
|
+
local.get $curr
|
|
422
|
+
local.get $offsetVal
|
|
423
|
+
i32.add
|
|
424
|
+
i32.load
|
|
425
|
+
local.set $fieldPtr
|
|
426
|
+
|
|
427
|
+
local.get $fieldPtr
|
|
428
|
+
local.get $mark
|
|
429
|
+
call $compact_obj
|
|
430
|
+
local.set $child_final
|
|
431
|
+
|
|
432
|
+
local.get $dest
|
|
433
|
+
local.get $offsetVal
|
|
434
|
+
i32.add
|
|
435
|
+
local.get $child_final
|
|
436
|
+
i32.store
|
|
437
|
+
|
|
438
|
+
local.get $i
|
|
439
|
+
i32.const 1
|
|
440
|
+
i32.add
|
|
441
|
+
local.set $i
|
|
442
|
+
br $loop_prod
|
|
443
|
+
)
|
|
444
|
+
)
|
|
445
|
+
br $break_chain
|
|
446
|
+
)
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
local.get $first_final
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
;; Finalize compaction by copying staging buffer into mark and resetting arena_free
|
|
453
|
+
(func $compact_finish (export "compact_finish") (param $mark i32)
|
|
454
|
+
(local $total_size i32)
|
|
455
|
+
global.get $compact_buf_free
|
|
456
|
+
global.get $compact_buf_start
|
|
457
|
+
i32.sub
|
|
458
|
+
local.set $total_size
|
|
459
|
+
|
|
460
|
+
local.get $total_size
|
|
461
|
+
i32.const 0
|
|
462
|
+
i32.gt_u
|
|
463
|
+
if
|
|
464
|
+
local.get $mark
|
|
465
|
+
global.get $compact_buf_start
|
|
466
|
+
local.get $total_size
|
|
467
|
+
memory.copy
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
local.get $mark
|
|
471
|
+
local.get $total_size
|
|
472
|
+
i32.add
|
|
473
|
+
global.set $arena_free
|
|
474
|
+
)
|
|
102
475
|
)
|
|
@@ -159,6 +159,7 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
159
159
|
for (const field of inputProductFields) {
|
|
160
160
|
registers.add(field.local);
|
|
161
161
|
}
|
|
162
|
+
registers.add("tail_loop_mark");
|
|
162
163
|
|
|
163
164
|
let nextUnionId = 0;
|
|
164
165
|
const tailLoopLabel = "$tail_loop";
|
|
@@ -379,6 +380,24 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
379
380
|
);
|
|
380
381
|
lines.push(branchWat);
|
|
381
382
|
}
|
|
383
|
+
lines.push(` ;; compact if arena growth exceeds 1MB`);
|
|
384
|
+
lines.push(` global.get $arena_free`);
|
|
385
|
+
lines.push(` local.get $tail_loop_mark`);
|
|
386
|
+
lines.push(` i32.sub`);
|
|
387
|
+
lines.push(` i32.const 1048576`);
|
|
388
|
+
lines.push(` i32.gt_u`);
|
|
389
|
+
lines.push(` if`);
|
|
390
|
+
lines.push(` call $compact_start`);
|
|
391
|
+
for (let i = 0; i < N; i++) {
|
|
392
|
+
const fieldTmp = `${dest}_f${i}`;
|
|
393
|
+
lines.push(` local.get $${fieldTmp}`);
|
|
394
|
+
lines.push(` local.get $tail_loop_mark`);
|
|
395
|
+
lines.push(` call $compact_obj`);
|
|
396
|
+
lines.push(` local.set $${fieldTmp}`);
|
|
397
|
+
}
|
|
398
|
+
lines.push(` local.get $tail_loop_mark`);
|
|
399
|
+
lines.push(` call $compact_finish`);
|
|
400
|
+
lines.push(` end`);
|
|
382
401
|
for (let i = 0; i < N; i++) {
|
|
383
402
|
const branch = sortedBranches[i];
|
|
384
403
|
const fieldTmp = `${dest}_f${i}`;
|
|
@@ -565,6 +584,21 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
565
584
|
registers.add(callSrc);
|
|
566
585
|
materializeInputProduct(lines, callSrc);
|
|
567
586
|
}
|
|
587
|
+
lines.push(` ;; compact if arena growth exceeds 1MB`);
|
|
588
|
+
lines.push(` global.get $arena_free`);
|
|
589
|
+
lines.push(` local.get $tail_loop_mark`);
|
|
590
|
+
lines.push(` i32.sub`);
|
|
591
|
+
lines.push(` i32.const 1048576`);
|
|
592
|
+
lines.push(` i32.gt_u`);
|
|
593
|
+
lines.push(` if`);
|
|
594
|
+
lines.push(` call $compact_start`);
|
|
595
|
+
lines.push(` local.get $${callSrc}`);
|
|
596
|
+
lines.push(` local.get $tail_loop_mark`);
|
|
597
|
+
lines.push(` call $compact_obj`);
|
|
598
|
+
lines.push(` local.set $${callSrc}`);
|
|
599
|
+
lines.push(` local.get $tail_loop_mark`);
|
|
600
|
+
lines.push(` call $compact_finish`);
|
|
601
|
+
lines.push(` end`);
|
|
568
602
|
lines.push(` local.get $${callSrc}`);
|
|
569
603
|
lines.push(` local.set $in`);
|
|
570
604
|
lines.push(initInputProductLocals());
|
|
@@ -641,6 +675,8 @@ export function lowerToWasm(relDef, name, options = {}) {
|
|
|
641
675
|
const wat = `(func $${name} (export "${name}") (param $in i32) (result i32 i32)
|
|
642
676
|
${localDecls}
|
|
643
677
|
${inputProductLocalInit}
|
|
678
|
+
global.get $arena_free
|
|
679
|
+
local.set $tail_loop_mark
|
|
644
680
|
(loop ${tailLoopLabel}
|
|
645
681
|
${profWat}${bodyWat}
|
|
646
682
|
)
|
|
@@ -31,6 +31,24 @@ const METADATA_SECTION = "k.metadata";
|
|
|
31
31
|
const ARTIFACT_FORMAT = "k-wasm";
|
|
32
32
|
const ARTIFACT_VERSION = 1;
|
|
33
33
|
|
|
34
|
+
class OutOfMemoryError extends Error {
|
|
35
|
+
constructor(message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "OutOfMemoryError";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function checkWasmOom(exports) {
|
|
42
|
+
if (exports.oom_flag && exports.oom_flag.value) {
|
|
43
|
+
const size = exports.oom_size ? exports.oom_size.value : 0;
|
|
44
|
+
const max = exports.arena_max ? exports.arena_max.value : 0;
|
|
45
|
+
const free = exports.arena_free ? exports.arena_free.value : 0;
|
|
46
|
+
throw new OutOfMemoryError(
|
|
47
|
+
`WebAssembly Out of Memory: linear memory arena exhausted (failed to allocate ${size} bytes; arena used: ${free >>> 0} bytes, arena max: ${max >>> 0} bytes)`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
34
52
|
const cleanName = (name) => "rel_" + name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
35
53
|
|
|
36
54
|
async function getWabt() {
|
|
@@ -894,9 +912,21 @@ async function instantiateWasmArtifact(wasmBuffer) {
|
|
|
894
912
|
const t1 = process.hrtime.bigint();
|
|
895
913
|
const mark = exports.arena_mark ? exports.arena_mark() : 0;
|
|
896
914
|
const arenaValues = new Map();
|
|
897
|
-
|
|
915
|
+
let ptrIn;
|
|
916
|
+
try {
|
|
917
|
+
ptrIn = writeValueToArena(exports, value, inputPattern, 0, arenaValues, tags, metadata.inputPattern);
|
|
918
|
+
} catch (error) {
|
|
919
|
+
checkWasmOom(exports);
|
|
920
|
+
throw error;
|
|
921
|
+
}
|
|
898
922
|
const t2 = process.hrtime.bigint();
|
|
899
|
-
|
|
923
|
+
let result;
|
|
924
|
+
try {
|
|
925
|
+
result = exports[metadata.entry](ptrIn);
|
|
926
|
+
} catch (error) {
|
|
927
|
+
checkWasmOom(exports);
|
|
928
|
+
throw error;
|
|
929
|
+
}
|
|
900
930
|
if (result[1] !== 1) {
|
|
901
931
|
if (exports.arena_reset) exports.arena_reset(mark);
|
|
902
932
|
throw new Error("Wasm relation execution failed (returned false)");
|
|
@@ -933,9 +963,21 @@ async function instantiateWasmArtifact(wasmBuffer) {
|
|
|
933
963
|
const t1 = process.hrtime.bigint();
|
|
934
964
|
const mark = exports.arena_mark ? exports.arena_mark() : 0;
|
|
935
965
|
const arenaValues = new Map();
|
|
936
|
-
|
|
966
|
+
let ptrIn;
|
|
967
|
+
try {
|
|
968
|
+
ptrIn = writeValueToArena(exports, inputValue, inputPattern, 0, arenaValues, tags, metadata.inputPattern);
|
|
969
|
+
} catch (error) {
|
|
970
|
+
checkWasmOom(exports);
|
|
971
|
+
throw error;
|
|
972
|
+
}
|
|
937
973
|
const t2 = process.hrtime.bigint();
|
|
938
|
-
|
|
974
|
+
let result;
|
|
975
|
+
try {
|
|
976
|
+
result = exports[metadata.entry](ptrIn);
|
|
977
|
+
} catch (error) {
|
|
978
|
+
checkWasmOom(exports);
|
|
979
|
+
throw error;
|
|
980
|
+
}
|
|
939
981
|
if (result[1] !== 1) {
|
|
940
982
|
if (exports.arena_reset) exports.arena_reset(mark);
|
|
941
983
|
return undefined;
|
|
@@ -982,6 +1024,7 @@ export {
|
|
|
982
1024
|
ARTIFACT_FORMAT,
|
|
983
1025
|
ARTIFACT_VERSION,
|
|
984
1026
|
METADATA_SECTION,
|
|
1027
|
+
OutOfMemoryError,
|
|
985
1028
|
appendCustomSection,
|
|
986
1029
|
compileWasmArtifactFromKVM,
|
|
987
1030
|
compileWasmArtifactFromObject,
|
|
@@ -998,6 +1041,7 @@ export default {
|
|
|
998
1041
|
ARTIFACT_FORMAT,
|
|
999
1042
|
ARTIFACT_VERSION,
|
|
1000
1043
|
METADATA_SECTION,
|
|
1044
|
+
OutOfMemoryError,
|
|
1001
1045
|
appendCustomSection,
|
|
1002
1046
|
compileWasmArtifactFromKVM,
|
|
1003
1047
|
compileWasmArtifactFromObject,
|
|
@@ -88,6 +88,14 @@ console.log("==> Running runtime.wat Core Allocator Tests");
|
|
|
88
88
|
const ptr3 = runtimeExports.alloc(70000);
|
|
89
89
|
assert.ok(ptr3 > 1040, "Large allocation must return a valid pointer");
|
|
90
90
|
assert.equal(ptr3 % 8, 0, "Large allocation must be 8-byte aligned");
|
|
91
|
+
|
|
92
|
+
// Test 5: Out of Memory flag recording on overflow
|
|
93
|
+
assert.equal(runtimeExports.oom_flag.value, 0, "OOM flag must be 0 initially");
|
|
94
|
+
assert.throws(() => {
|
|
95
|
+
runtimeExports.alloc(-1);
|
|
96
|
+
}, /unreachable/);
|
|
97
|
+
assert.equal(runtimeExports.oom_flag.value, 1, "OOM flag must be 1 after allocation failure");
|
|
98
|
+
assert.equal(runtimeExports.oom_size.value, -1, "OOM size must record requested size");
|
|
91
99
|
console.log("Core Allocator tests passed successfully!");
|
|
92
100
|
}
|
|
93
101
|
|
package/browser/repl-browser.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import { Value, isProduct, isVariant } from "../Value.mjs";
|
|
21
21
|
import { encodeLibrary } from "../object.mjs";
|
|
22
22
|
import { setVfsFile, getVfsFile, getAllVfsFiles } from "./shims/fs.mjs";
|
|
23
|
+
import { createOutputEntryElement } from "./tree-view.mjs";
|
|
23
24
|
|
|
24
25
|
// Make Value and helpers globally available for custom codecs
|
|
25
26
|
if (typeof window !== "undefined") {
|
|
@@ -155,7 +156,7 @@ function setStatus(busy, text = "Ready") {
|
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
|
|
158
|
-
function appendEntry({ prompt, command, outputs = [], errors = [], warnings = [], duration = null, timing = null }) {
|
|
159
|
+
function appendEntry({ prompt, command, outputs = [], errors = [], warnings = [], duration = null, timing = null, value = null }) {
|
|
159
160
|
if (!outputEl) return;
|
|
160
161
|
|
|
161
162
|
const entry = document.createElement("div");
|
|
@@ -220,11 +221,26 @@ function appendEntry({ prompt, command, outputs = [], errors = [], warnings = []
|
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
// Outputs
|
|
223
|
-
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
224
|
+
if (value && outputs.length > 0) {
|
|
225
|
+
const rawText = outputs.join("\n");
|
|
226
|
+
const allLines = outputs[0].split("\n");
|
|
227
|
+
const codecOutputs = allLines.slice(1);
|
|
228
|
+
const timingText = outputs.length > 1 ? outputs[1] : null;
|
|
229
|
+
|
|
230
|
+
const outElement = createOutputEntryElement({
|
|
231
|
+
value,
|
|
232
|
+
rawText,
|
|
233
|
+
codecOutputs,
|
|
234
|
+
timingText
|
|
235
|
+
});
|
|
236
|
+
entry.appendChild(outElement);
|
|
237
|
+
} else {
|
|
238
|
+
for (const out of outputs) {
|
|
239
|
+
const outDiv = document.createElement("div");
|
|
240
|
+
outDiv.className = "entry-line line-output";
|
|
241
|
+
outDiv.innerHTML = ansiToHtml(out);
|
|
242
|
+
entry.appendChild(outDiv);
|
|
243
|
+
}
|
|
228
244
|
}
|
|
229
245
|
|
|
230
246
|
outputEl.appendChild(entry);
|
|
@@ -298,7 +314,8 @@ export async function executeCommand(input) {
|
|
|
298
314
|
errors,
|
|
299
315
|
warnings,
|
|
300
316
|
duration,
|
|
301
|
-
timing: state.lastTiming
|
|
317
|
+
timing: state.lastTiming,
|
|
318
|
+
value: state.lastResult
|
|
302
319
|
});
|
|
303
320
|
|
|
304
321
|
// Re-create completer with updated state
|