Native Engine Integration: TJS in V8 or JavaScriptCore

Forward-looking exploration of what native JS engine support for TJS could unlock.


Why Native?

TJS currently transpiles to standard JavaScript. This works, but it means:

Native engine support would turn TJS's type annotations into first-class information the JIT can exploit — the same way it already uses hidden classes and speculative type profiling, but with declared types instead of observed ones.


The Big Wins

1. Function Boundary Validation (~1.5x → ~1.0x)

Today: TJS emits typeof checks, Number.isInteger() calls, and object shape validation as inline JS. The JIT treats these as generic user code.

// Current transpiler output — the JIT sees opaque code
function add(a, b) {
  __tjs.pushStack('file:1:add');
  if (typeof a !== 'number' || !Number.isInteger(a))
    return __tjs.typeError('file:1:add.a', 'integer', a);
  if (typeof b !== 'number' || !Number.isInteger(b))
    return __tjs.typeError('file:1:add.b', 'integer', b);
  try {
    return a + b;
  } finally {
    __tjs.popStack();
  }
}

Native: Type checks become bytecode-level guards — the same mechanism the JIT already uses for speculative optimization:

2. Cross-Function Type Propagation (impossible today)

Today: Each function independently validates its inputs. Even with ! (unsafe), you're making a manual decision about which checks to skip.

Native: The engine can propagate types across call boundaries. If calc(a: 0, b: 0) calls add(x: 0, y: 0), and both functions declare integer params, the engine can prove that add's guards are satisfied by calc's guards and skip them entirely.

This is the one optimization that cannot be achieved at the source level. It turns TJS's per-function overhead into per-call-chain overhead — the outermost typed function pays the cost, and everything it calls runs at full speed.

Scenario Current (transpiled) Native engine
First call Interpreted, runs typeof checks JIT with declared types, no warmup
Warmup (100-1000 calls) JIT profiles, still runs typeof checks Already optimized
Hot (10000+ calls) JIT may inline typeof but can't eliminate Guards are internal, may be elided
Cross-function calls Each function re-validates independently Types propagate — redundant guards skipped

3. Structural Equality as Engine Primitive

Today: Is() is a JS function that recursively walks two objects comparing values. Even with short-circuit optimizations, every == comparison pays function call overhead plus recursive property enumeration.

Native: The engine has internal knowledge that source-level code doesn't:

4. Inline Tests as Compilation Assertions

Today: TJS runs inline tests at transpile time by executing the compiled code via new Function(). Test results are separate from the compilation pipeline.

Native: Tests become part of the compilation contract:

5. WASM Blocks

Today: WASM blocks are compiled at transpile time and embedded as base64 in the JS output. The runtime instantiates them with WebAssembly.instantiate().

Native: The engine could compile WASM blocks as part of the same compilation pipeline — no base64 encoding/decoding, no async instantiation. The WASM code would be available synchronously, and the engine could inline across the JS/WASM boundary for small functions.


Which Engine?

JavaScriptCore (WebKit/Bun) — More Practical

V8 (Chrome/Node/Deno) — More Impact

Recommendation: Start with JSC via Bun. The integration path is natural (Bun is already the TJS runtime), the parser is more approachable, and Bun's team has institutional knowledge of JSC internals.


Scope of JSC Changes

TJS's syntax extensions are modest. The engine changes would be:

Lexer

Parser

Bytecode Compiler

JIT (DFG/FTL in JSC)

What Stays Untouched


The Bootstrapping Path

A pragmatic rollout:

  1. Phase 1: Parser only. Extend JSC's parser to understand TJS syntax, desugaring to the same JS that the transpiler produces today. Zero runtime changes. Benefit: single parse, better error messages, source maps unnecessary.

  2. Phase 2: Type guards. Replace the emitted typeof checks with bytecode-level guards. The JIT can now reason about TJS types. Benefit: warmup-free optimization, cross-function type propagation.

  3. Phase 3: Structural equality. Implement Is()/IsNot() as engine primitives using hidden class information. Benefit: O(1) common-case equality for same-shape objects.

  4. Phase 4: Inline tests. Integrate test execution into the compilation pipeline. Benefit: compile-time verification, test results as type evidence for JIT.

Each phase is independently useful and shippable.