MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix UB revealed by loop fix

+117 -1
+1 -1
src/ant.c
··· 10919 10919 10920 10920 loop_block_clear(js, &loop_ctx); 10921 10921 v = loop_block_exec(js, &loop_ctx); 10922 - if (is_err(v)) res = v; break; 10922 + if (is_err(v)) { res = v; break; } 10923 10923 10924 10924 if (label_flags & F_CONTINUE_LABEL) { 10925 10925 if (is_this_loop_continue_target(marker_index)) {
+116
tests/bench_gc.js
··· 1 + // GC Benchmark - tests forwarding table and compaction performance 2 + 3 + function formatBytes(bytes) { 4 + if (bytes < 1024) return bytes + ' B'; 5 + if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'; 6 + return (bytes / 1024 / 1024).toFixed(2) + ' MB'; 7 + } 8 + 9 + function bench(name, fn, iterations = 1) { 10 + // Warmup 11 + fn(); 12 + Ant.gc(); 13 + 14 + let totalTime = 0; 15 + let totalFreed = 0; 16 + 17 + for (let iter = 0; iter < iterations; iter++) { 18 + fn(); // Create garbage 19 + 20 + const t0 = Date.now(); 21 + const result = Ant.gc(); 22 + const elapsed = Date.now() - t0; 23 + 24 + totalTime += elapsed; 25 + totalFreed += result.arenaFreed; 26 + } 27 + 28 + console.log(`${name}:`); 29 + console.log(` Time: ${totalTime}ms (${iterations} iterations, avg ${(totalTime / iterations).toFixed(2)}ms)`); 30 + console.log(` Freed: ${formatBytes(totalFreed)} total`); 31 + console.log(''); 32 + } 33 + 34 + // Test 1: Many small objects (stresses forwarding table with many entries) 35 + function manySmallObjects() { 36 + const objects = []; 37 + for (let i = 0; i < 1000; i++) { 38 + objects.push({ id: i, value: i * 2 }); 39 + } 40 + // Let half become garbage 41 + objects.length = 5000; 42 + } 43 + 44 + // Test 2: Deep object graph (stresses recursive copying) 45 + function deepObjectGraph() { 46 + let obj = { value: 0 }; 47 + for (let i = 1; i < 1000; i++) { 48 + obj = { value: i, child: obj }; 49 + } 50 + return obj; 51 + } 52 + 53 + // Test 3: Wide object (many properties) 54 + function wideObject() { 55 + const obj = {}; 56 + for (let i = 0; i < 1000; i++) { 57 + obj['prop' + i] = i; 58 + } 59 + return obj; 60 + } 61 + 62 + // Test 4: Array of strings (tests string copying) 63 + function stringArray() { 64 + const arr = []; 65 + for (let i = 0; i < 5000; i++) { 66 + arr.push('string_value_' + i + '_with_some_extra_content'); 67 + } 68 + arr.length = 2500; // Half become garbage 69 + } 70 + 71 + // Test 5: Mixed workload 72 + function mixedWorkload() { 73 + const data = { 74 + objects: [], 75 + strings: [], 76 + nested: null 77 + }; 78 + 79 + for (let i = 0; i < 2000; i++) { 80 + data.objects.push({ x: i, y: i * 2, name: 'obj' + i }); 81 + data.strings.push('item_' + i); 82 + } 83 + 84 + // Create some nested structure 85 + let nested = { level: 0 }; 86 + for (let i = 1; i < 100; i++) { 87 + nested = { level: i, prev: nested, data: { a: i, b: i * 2 } }; 88 + } 89 + data.nested = nested; 90 + 91 + // Make some garbage 92 + data.objects.length = 1000; 93 + data.strings.length = 1000; 94 + } 95 + 96 + // Test 6: Repeated GC cycles (tests table reuse efficiency) 97 + function repeatedGcCycles() { 98 + for (let cycle = 0; cycle < 10; cycle++) { 99 + const temp = []; 100 + for (let i = 0; i < 1000; i++) { 101 + temp.push({ cycle, i, data: 'x'.repeat(50) }); 102 + } 103 + } 104 + } 105 + 106 + console.log('=== GC Benchmark ===\n'); 107 + console.log('Initial state:', Ant.alloc(), '\n'); 108 + 109 + bench('Many small objects (10k objects, 5k garbage)', manySmallObjects, 5); 110 + bench('Deep object graph (1000 levels)', deepObjectGraph, 5); 111 + bench('Wide object (1000 properties)', wideObject, 5); 112 + bench('String array (5000 strings, 2500 garbage)', stringArray, 5); 113 + bench('Mixed workload', mixedWorkload, 5); 114 + bench('Repeated GC cycles (10 cycles x 1000 objects)', repeatedGcCycles, 3); 115 + 116 + console.log('Final state:', Ant.alloc());