Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174import { test, summary } from './helpers.js';
const HOT = 5000;
console.log('monomorphic instance methods\n');
class Point { constructor(x, y) { this.x = x; this.y = y; } sum() { return this.x + this.y; } scaledSum(k) { return (this.x + this.y) * k; } classify() { if (this.x > this.y) return 'x'; if (this.x < this.y) return 'y'; return 'eq'; }}
function driveSum(p) { let acc = 0; for (let i = 0; i < 8; i++) acc += p.sum(); return acc;}
let sumAcc = 0;for (let i = 0; i < HOT; i++) sumAcc += driveSum(new Point(3, 4));test('monomorphic method inlined returns correct sum', sumAcc, HOT * 8 * 7);
function driveArgs(p, k) { return p.scaledSum(k);}let argsAcc = 0;for (let i = 0; i < HOT; i++) argsAcc += driveArgs(new Point(2, 5), 3);test('inlined method with argument', argsAcc, HOT * (2 + 5) * 3);
function driveBranch(p) { return p.classify();}let branchOk = true;for (let i = 0; i < HOT; i++) { if (driveBranch(new Point(1, 9)) !== 'y') branchOk = false; if (driveBranch(new Point(9, 1)) !== 'x') branchOk = false; if (driveBranch(new Point(4, 4)) !== 'eq') branchOk = false;}test('inlined method with control flow', branchOk, true);
console.log('\nplain-object prototype methods\n');
const proto = { twice() { return this.v * 2; }};function driveProto(o) { return o.twice();}let protoAcc = 0;for (let i = 0; i < HOT; i++) { const o = Object.create(proto); o.v = i & 7; protoAcc += driveProto(o);}let protoRef = 0;for (let i = 0; i < HOT; i++) protoRef += (i & 7) * 2;test('prototype method devirtualized on plain object', protoAcc, protoRef);
console.log('\npolymorphic call sites fall back correctly\n');
class A { who() { return 1; }}class B { who() { return 2; }}function drivePoly(o) { return o.who();}
let polyAcc = 0;for (let i = 0; i < HOT; i++) { polyAcc += drivePoly(i % 2 === 0 ? new A() : new B());}test('polymorphic site returns per-receiver result', polyAcc, (HOT / 2) * 1 + (HOT / 2) * 2);
console.log('\nsuper method calls stay correct\n');
class Base { greet() { return 'base'; }}class Derived extends Base { greet() { return super.greet() + ':derived'; }}function driveSuper(d) { return d.greet();}let superOk = true;const d = new Derived();for (let i = 0; i < HOT; i++) { if (driveSuper(d) !== 'base:derived') superOk = false;}test('super.method() dispatch unaffected by devirtualization', superOk, true);
console.log('\narrow methods bind lexical this\n');
function makeCounter(base) { return { base, bumped: () => base + 100 };}function driveArrow(o) { return o.bumped();}let arrowAcc = 0;for (let i = 0; i < HOT; i++) arrowAcc += driveArrow(makeCounter(i & 3));let arrowRef = 0;for (let i = 0; i < HOT; i++) arrowRef += (i & 3) + 100;test('arrow-valued method uses lexical this', arrowAcc, arrowRef);
console.log('\nbound methods keep prepended arguments\n');
function addThree(a, b, c) { return a + b + c;}const holder = { f: addThree.bind(null, 10, 20) };function driveBound(o) { return o.f(5);}let boundAcc = 0;for (let i = 0; i < HOT; i++) boundAcc += driveBound(holder);test('bound method with prepended args', boundAcc, HOT * (10 + 20 + 5));
console.log('\nthrowing methods propagate\n');
class Boom { maybe(n) { if (n === 42) throw new Error('boom'); return n; }}function driveThrow(o, n) { return o.maybe(n);}const boom = new Boom();let threwAt42 = false;let normalOk = true;for (let i = 0; i < HOT; i++) { const n = i + 1000; if (driveThrow(boom, n) !== n) normalOk = false;}try { driveThrow(boom, 42);} catch (e) { threwAt42 = e.message === 'boom';}test('inlined method returns value on the hot path', normalOk, true);test('inlined method still throws when it should', threwAt42, true);
summary();