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.

add tests and update meson file

+320 -5
examples/demo/fibonacci.js examples/demo/fibonacci_memo.js
+11
examples/demo/fibonacci_tco.js
··· 1 + function fib(n, a = 0, b = 1) { 2 + if (n === 0) return a; 3 + return fib(n - 1, b, a + b); 4 + } 5 + 6 + const start = performance.now(); 7 + const result = fib(30); 8 + const end = performance.now(); 9 + 10 + console.log(`fibonacci(30) = ${result}`); 11 + console.log(`Time: ${(end - start).toFixed(2)} ms`);
examples/demo/tco_fib.js examples/demo/fibonacci_recursive.js
+141
examples/spec/private_classes.js
··· 1 + import { test, summary } from './helpers.js'; 2 + 3 + console.log('Private Class Tests\n'); 4 + 5 + // Private instance fields 6 + class Counter { 7 + #count = 0; 8 + increment() { this.#count++; } 9 + get value() { return this.#count; } 10 + } 11 + const counter = new Counter(); 12 + test('private field initial', counter.value, 0); 13 + counter.increment(); 14 + counter.increment(); 15 + test('private field after increment', counter.value, 2); 16 + 17 + // Private field with constructor 18 + class Box { 19 + #content; 20 + constructor(val) { this.#content = val; } 21 + unwrap() { return this.#content; } 22 + } 23 + test('private field via constructor', new Box(42).unwrap(), 42); 24 + test('private field string', new Box('hello').unwrap(), 'hello'); 25 + 26 + // Private field does not collide with public 27 + class Dual { 28 + #x = 'private'; 29 + x = 'public'; 30 + getPrivate() { return this.#x; } 31 + getPublic() { return this.x; } 32 + } 33 + const dual = new Dual(); 34 + test('private vs public field', dual.getPrivate(), 'private'); 35 + test('public field unaffected', dual.getPublic(), 'public'); 36 + 37 + // Private instance methods 38 + class Formatter { 39 + #prefix; 40 + constructor(prefix) { this.#prefix = prefix; } 41 + #format(str) { return this.#prefix + ': ' + str; } 42 + display(str) { return this.#format(str); } 43 + } 44 + test('private method', new Formatter('LOG').display('test'), 'LOG: test'); 45 + 46 + // Private method does not collide with public 47 + class Methods { 48 + #x() { return 'private'; } 49 + x() { return this.#x(); } 50 + } 51 + test('private method call', new Methods().x(), 'private'); 52 + 53 + // Private getters and setters 54 + class Temperature { 55 + #celsius = 0; 56 + get #c() { return this.#celsius; } 57 + set #c(val) { this.#celsius = val; } 58 + get fahrenheit() { return this.#c * 9 / 5 + 32; } 59 + set fahrenheit(val) { this.#c = (val - 32) * 5 / 9; } 60 + } 61 + const temp = new Temperature(); 62 + test('private getter initial', temp.fahrenheit, 32); 63 + temp.fahrenheit = 212; 64 + test('private setter', temp.fahrenheit, 212); 65 + 66 + // Private static methods 67 + class MathHelper { 68 + static #square(n) { return n * n; } 69 + static calc(n) { return MathHelper.#square(n); } 70 + } 71 + test('private static method', MathHelper.calc(5), 25); 72 + 73 + // Private static accessors 74 + class Config { 75 + static #data = null; 76 + static get #config() { return Config.#data || 'default'; } 77 + static set #config(val) { Config.#data = val; } 78 + static get() { return Config.#config; } 79 + static set(val) { Config.#config = val; } 80 + } 81 + test('private static getter default', Config.get(), 'default'); 82 + Config.set('custom'); 83 + test('private static setter', Config.get(), 'custom'); 84 + 85 + // Private static fields 86 + class IdGen { 87 + static #next = 1; 88 + static generate() { return IdGen.#next++; } 89 + } 90 + test('private static field 1', IdGen.generate(), 1); 91 + test('private static field 2', IdGen.generate(), 2); 92 + test('private static field 3', IdGen.generate(), 3); 93 + 94 + // Computed instance fields 95 + class Computed { 96 + ['x'] = 10; 97 + ['y'] = 20; 98 + } 99 + const comp = new Computed(); 100 + test('computed instance field x', comp.x, 10); 101 + test('computed instance field y', comp.y, 20); 102 + 103 + // Computed static fields 104 + class ComputedStatic { 105 + static ['a'] = 100; 106 + static ['b'] = 200; 107 + } 108 + test('computed static field a', ComputedStatic.a, 100); 109 + test('computed static field b', ComputedStatic.b, 200); 110 + 111 + // Static initialization blocks 112 + let staticVal = 0; 113 + class WithStaticBlock { 114 + static { staticVal = 42; } 115 + } 116 + test('static init block', staticVal, 42); 117 + 118 + let staticSum = 0; 119 + class MultiBlock { 120 + static { staticSum += 10; } 121 + static { staticSum += 20; } 122 + } 123 + test('multiple static blocks', staticSum, 30); 124 + 125 + // Inheritance with private fields 126 + class Animal { 127 + #name; 128 + constructor(name) { this.#name = name; } 129 + getName() { return this.#name; } 130 + } 131 + class Dog extends Animal { 132 + #breed; 133 + constructor(name, breed) { 134 + super(name); 135 + this.#breed = breed; 136 + } 137 + describe() { return this.getName() + ' (' + this.#breed + ')'; } 138 + } 139 + test('inherited private fields', new Dog('Rex', 'Labrador').describe(), 'Rex (Labrador)'); 140 + 141 + summary();
+73
examples/spec/tco.js
··· 1 + import { test, summary } from './helpers.js'; 2 + 3 + console.log('Tail Call Optimization Tests\n'); 4 + 5 + function factorial(n, acc = 1) { 6 + if (n <= 1) return acc; 7 + return factorial(n - 1, n * acc); 8 + } 9 + test('tail factorial(10)', factorial(10), 3628800); 10 + test('tail factorial(20)', factorial(20), 2432902008176640000); 11 + 12 + function countDown(n) { 13 + if (n <= 0) return 'done'; 14 + return countDown(n - 1); 15 + } 16 + test('deep tail recursion', countDown(100000), 'done'); 17 + 18 + function sum(n, acc = 0) { 19 + if (n <= 0) return acc; 20 + return sum(n - 1, acc + n); 21 + } 22 + test('tail sum(100000)', sum(100000), 5000050000); 23 + 24 + function fib(n, a = 0, b = 1) { 25 + if (n === 0) return a; 26 + if (n === 1) return b; 27 + return fib(n - 1, b, a + b); 28 + } 29 + test('tail fib(10)', fib(10), 55); 30 + test('tail fib(30)', fib(30), 832040); 31 + 32 + function isEven(n) { 33 + if (n === 0) return true; 34 + return isOdd(n - 1); 35 + } 36 + function isOdd(n) { 37 + if (n === 0) return false; 38 + return isEven(n - 1); 39 + } 40 + test('mutual isEven(10)', isEven(10), true); 41 + test('mutual isOdd(11)', isOdd(11), true); 42 + test('mutual isEven(10000)', isEven(10000), true); 43 + 44 + function normalFib(n) { 45 + if (n <= 1) return n; 46 + return normalFib(n - 1) + normalFib(n - 2); 47 + } 48 + test('non-tail recursion', normalFib(10), 55); 49 + 50 + function normalFactorial(n) { 51 + if (n <= 1) return 1; 52 + return n * normalFactorial(n - 1); 53 + } 54 + test('non-tail factorial', normalFactorial(5), 120); 55 + 56 + function tailMultiArg(a, b, c) { 57 + if (a <= 0) return b + c; 58 + return tailMultiArg(a - 1, b + 1, c + 2); 59 + } 60 + test('tail multi-arg', tailMultiArg(1000, 0, 0), 3000); 61 + 62 + function ternaryTail(n) { 63 + return n <= 0 ? 'done' : ternaryTail(n - 1); 64 + } 65 + test('ternary tail', ternaryTail(50000), 'done'); 66 + 67 + function lastElement(arr, i = 0) { 68 + if (i >= arr.length - 1) return arr[i]; 69 + return lastElement(arr, i + 1); 70 + } 71 + test('tail lastElement', lastElement([1, 2, 3, 4, 5]), 5); 72 + 73 + summary();
+17 -4
include/internal.h
··· 12 12 extern UT_array *saved_scope_stack; 13 13 14 14 struct for_let_ctx { 15 - const char *var_name; // interned variable name 16 - jsoff_t var_len; // length of var name 17 - jsoff_t prop_off; // offset of var property in loop scope 18 - jsval_t body_scope; // loop body scope for capturing block-scoped vars 15 + const char *var_name; 16 + jsoff_t var_len; 17 + jsoff_t prop_off; 18 + jsval_t body_scope; 19 19 }; 20 20 21 21 struct js { ··· 66 66 int parse_depth; // recursion depth of parser (for stack overflow protection) 67 67 bool skip_func_hoist; // skip function declaration hoisting (pre-computed) 68 68 bool fatal_error; // fatal error that should bypass promise rejection handling 69 + bool tail_ctx; // true when evaluating a return expression (for TCO) 70 + bool has_tail_calls; // cached: current function body has tail-call-eligible returns 71 + 72 + struct { 73 + jsval_t func; 74 + jsval_t closure_scope; 75 + const char *code_str; 76 + jsoff_t fnlen; 77 + jsval_t *args; 78 + int argc; 79 + bool pending; 80 + } tc; 69 81 70 82 struct for_let_ctx *for_let_stack; 71 83 int for_let_stack_len; ··· 120 132 121 133 #define TYPE_FLAG(t) (1u << (t)) 122 134 #define T_EMPTY (NANBOX_PREFIX | ((jsval_t)T_FFI << NANBOX_TYPE_SHIFT) | 0xDEADULL) 135 + #define T_TAILCALL (NANBOX_PREFIX | ((jsval_t)T_FFI << NANBOX_TYPE_SHIFT) | 0x7A1CULL) 123 136 124 137 #define T_SPECIAL_OBJECT_MASK (TYPE_FLAG(T_OBJ) | TYPE_FLAG(T_ARR)) 125 138 #define T_NEEDS_PROTO_FALLBACK (TYPE_FLAG(T_FUNC) | TYPE_FLAG(T_ARR) | TYPE_FLAG(T_PROMISE))
+3 -1
meson/version/meson.build
··· 1 + fs = import('fs') 1 2 git_hash = run_command('git', 'rev-parse', '--short', 'HEAD', check: false).stdout().strip() 2 3 git_hash = git_hash != '' ? git_hash : 'unknown' 3 4 4 5 timestamp_opt = get_option('build_timestamp') 5 6 timestamp = timestamp_opt != '' ? timestamp_opt : run_command('date', '+%s', check: true).stdout().strip() 6 7 7 - ant_version = '0.5.3.' + timestamp + '-g' + git_hash 8 + ant_base_version = fs.read(meson.project_source_root() / 'meson' / 'ant.version').strip() 9 + ant_version = ant_base_version + '.' + timestamp + '-g' + git_hash 8 10 cmd_cc = meson.get_compiler('c') 9 11 10 12 target_triple = run_command(cmd_cc.cmd_array(), '-dumpmachine', check: true).stdout().strip()
+75
tests/test_tco.js
··· 1 + // Test tail call optimization 2 + 3 + // 1. Basic tail-recursive factorial 4 + function factorial(n, acc = 1) { 5 + if (n <= 1) return acc; 6 + return factorial(n - 1, n * acc); 7 + } 8 + console.log('factorial(10):', factorial(10)); // 3628800 9 + console.log('factorial(20):', factorial(20)); // 2432902008176640000 10 + 11 + // 2. Deep tail recursion - would stack overflow without TCO 12 + function countDown(n) { 13 + if (n <= 0) return 'done'; 14 + return countDown(n - 1); 15 + } 16 + console.log('countDown(100000):', countDown(100000)); // done 17 + 18 + // 3. Tail-recursive sum 19 + function sum(n, acc = 0) { 20 + if (n <= 0) return acc; 21 + return sum(n - 1, acc + n); 22 + } 23 + console.log('sum(100000):', sum(100000)); // 5000050000 24 + 25 + // 4. Tail-recursive fibonacci (iterative via accumulator) 26 + function fib(n, a = 0, b = 1) { 27 + if (n === 0) return a; 28 + if (n === 1) return b; 29 + return fib(n - 1, b, a + b); 30 + } 31 + console.log('fib(10):', fib(10)); // 55 32 + console.log('fib(30):', fib(30)); // 832040 33 + 34 + // 5. Mutual tail recursion 35 + function isEven(n) { 36 + if (n === 0) return true; 37 + return isOdd(n - 1); 38 + } 39 + function isOdd(n) { 40 + if (n === 0) return false; 41 + return isEven(n - 1); 42 + } 43 + console.log('isEven(10):', isEven(10)); // true 44 + console.log('isOdd(11):', isOdd(11)); // true 45 + console.log('isEven(10000):', isEven(10000)); // true 46 + 47 + // 6. Non-tail calls should still work correctly 48 + function normalFib(n) { 49 + if (n <= 1) return n; 50 + return normalFib(n - 1) + normalFib(n - 2); 51 + } 52 + console.log('normalFib(10):', normalFib(10)); // 55 53 + 54 + // 7. Tail call with different argument count 55 + function tailMultiArg(a, b, c) { 56 + if (a <= 0) return b + c; 57 + return tailMultiArg(a - 1, b + 1, c + 2); 58 + } 59 + console.log('tailMultiArg(1000, 0, 0):', tailMultiArg(1000, 0, 0)); // 1000 + 2000 = 3000 60 + 61 + // 8. Tail position in ternary 62 + function ternaryTail(n) { 63 + return n <= 0 ? 'done' : ternaryTail(n - 1); 64 + } 65 + console.log('ternaryTail(50000):', ternaryTail(50000)); // done 66 + 67 + // 9. Tail position in logical expressions should NOT be optimized 68 + // (the result goes through boolean coercion, not a true tail call) 69 + function lastElement(arr, i = 0) { 70 + if (i >= arr.length - 1) return arr[i]; 71 + return lastElement(arr, i + 1); 72 + } 73 + console.log('lastElement([1,2,3,4,5]):', lastElement([1, 2, 3, 4, 5])); // 5 74 + 75 + console.log('\nAll TCO tests passed!');