Show HN: Nova JavaScript Engine

A new experimental JavaScript engine called Nova aims to rethink runtime architecture using data-oriented design and Entity Component System concepts, organizing values into type-specific vectors and referencing them by indices instead of pointers. Commenters probe how this compares to established engines like V8, especially around cache locality, garbage collection, generational behavior, and security trade-offs, and whether such layouts match real-world access patterns. The project’s author positions Nova as a long-term attempt to reach full ECMAScript compatibility without JIT compilation, using Rust and aggressive memory compaction, and potentially influencing mainstream engine design if its ideas prove performant.

Project Goals and Scope

  • Aims to be a full ECMAScript- compliant engine, not just an experiment.
  • Provides feature flags to strip out unneeded spec parts for embedders (e.g., disabling ArrayBuffers).
  • Long‑term ambitions include potential use in browsers or runtimes; near term focus is one‑shot scripts and constrained environments (e.g., microcontrollers).

Architectural Design: ECS & Data-Oriented Layout

  • Core idea: Entity‑Component‑System‑inspired, data‑oriented design.
  • Heap is split into type‑specific “vectors” (arrays) indexed by integers, not pointers: numbers, arrays, ArrayBuffers, etc. each have their own storage.
  • Objects may also be further decomposed (e.g., separate storage for properties), with some discussion of struct‑of‑arrays vs array‑of‑structs layouts.
  • Motivation: better cache locality, tighter packing, fewer alignment gaps, and index-based “pointer compression”-like behavior.

GC and Memory Management Trade-offs

  • Uses indices rather than native pointers; GC must manage dangling indexes and compaction.
  • Strategy: heap vectors stay densely packed; GC compacts by moving surviving objects down, not filling “holes” via free lists.
  • For generational GC, each vector tracks a “young generation start” index; promotion adjusts this boundary.
  • Some participants question the cost of large compactions and suggest chunking, free lists, or virtual allocation; maintainer acknowledges trade-offs and open questions.

Comparisons with V8 and Mainstream Engines

  • V8 uses multiple heap spaces (nursery/old), pointer or compressed-pointer references, and monolithic objects (with separate backing stores for some properties).
  • Commenters note V8 and others already try for cache locality (e.g., JSON array parsing), but follow an array‑of‑structs pattern.
  • Claim that Nova’s full “heap vectors + indexes” model would be nearly impossible to retrofit into existing engines without a rewrite.

Data-Oriented Design Debate

  • Several discuss whether “all numbers in a vector, all arrays in a vector” actually matches typical JS access patterns.
  • Pro‑Nova view: most performance hotspots are loops over large, linearly created datasets; data created together tends to be used together.
  • Skeptical view: general-purpose workloads often access fields within the same object (foo.name + foo.lastName), not across many objects, so ECS‑like layouts may help only for specific patterns.

Performance Expectations & Benchmarks

  • Currently no robust benchmark results; large benchmarks can stall because GC is not yet interleavable with JS execution.
  • Known current bottlenecks: property lookup (linear search on larger objects), value comparisons, slow string interning.
  • Plan is to finish interleaved GC, implement object shapes and inline property caches, then compare with V8 in non‑JIT modes.

JIT, Optimizations, and TCO

  • Engine currently has a bytecode compiler and interpreter only; no JIT.
  • Maintainer hopes JIT will remain unnecessary if key optimizations (especially property access inline caching) are implemented.
  • Cites no‑JIT modes in other engines and small‑scale timings as anecdotal support, but no hard data yet.
  • Tail‑call optimization is planned, as it is in the ECMAScript spec; some code already marked with TODOs for TCO.

Safety, Indices, and Rust

  • Using indices weakens some of Rust’s usual guarantees (e.g., referential safety), but is argued to preserve memory safety when bounds-checked.
  • Design tries to prevent type confusion by keeping tag + index together in a tagged union; changing an index’s “kind” requires unsafe operations.
  • Some worry this can still lead to silent logic errors or security issues via stale indices; mitigations are partly conceptual and partly via Rust lifetimes/ZST helpers, but not fully proven.

Miscellaneous

  • Name “Nova” is acknowledged as overused but kept for historical/bikeshedding reasons.
  • Thread includes suggestions to study related work (e.g., BIBOP, other JS and Lisp engines) and to benchmark against game‑oriented runtimes like Lua.
  • Overall tone: mixture of enthusiasm for the experimental design and skepticism about its general applicability and GC complexity.