Architecture.
JS++ now has a compact unoptimized bytecode interpreter with explicit ownership. It is not a JIT, a browser, or a Node replacement.
JavaScript source
↓
source locations + diagnostics
↓
ECMAScript lexer
↓
executable syntax
↓
bytecode compiler
↓
virtual machine
↓
values · objects · environments · built-ins
↓
host embedding boundary
Why bytecode early?
The JS3 instruction stream is inspectable with js --dump-bytecode -e '1+2' and is the same code executed through the CLI and embedding API.
A bytecode boundary makes evaluation order, control flow, calls, exceptions and stack state inspectable. It gives tests something precise to validate without committing the project to machine-code generation.
CP23 gives abrupt control flow an explicit completion record. JavaScript throw values are no longer collapsed into diagnostic strings inside VM frames, leaving handler and finally logic a stable value-preserving boundary.
CP25 adds protected bytecode regions with explicit handler, lexical-depth and stack-depth metadata. Dispatch validates that metadata and reconstructs the handler state for both local and cross-frame throws before any surface handler syntax is enabled.
JS8D connects that machinery to source-level try/catch. Catch bindings live in a dedicated lexical environment, and handler selection uses the smallest protected region containing the throwing instruction.
CP27/JS8E adds cleanup regions and resumable pending completions. Cleanup bytecode preserves a throw or return, runs once with reconstructed scope/stack state, then resumes or replaces that completion. The frontend still rejects finally until JS8F.
CP29/JS8F connects that boundary to source-level finally. Normal, return, throw, break and continue paths all cross the same cleanup mechanism; nested cleanup and completion replacement are regression-tested.
JS9A moves truthiness, primitive/number/string/property-key conversion, equality and ordering into a single engine layer used by the VM. Default object and array conversion is bounded until native prototype methods can expose user-defined hooks.
JS10A adds one intrinsic environment per runtime. Native functions and constructors are ordinary function values, follow the existing call/construct opcodes, retain prototype identity and remain rooted across evaluation and collection.
JS10B uses that layer for Object, Function, Error, TypeError and RangeError. Native call/apply re-enter the same invoker as JavaScript calls, while conversion hooks dispatch ordinary user methods before falling back to bounded defaults.
JS10G routes named values and descriptor flags for objects, arrays and functions through one internal own-property abstraction. Indexed exotic behavior remains a separate bounded layer.
JS10I completes that path through the VM: method lookup, conversion hooks, named reads, writes, getters and setters share one dispatch boundary across objects, arrays and functions. Array indices, length and function prototype remain explicit exotic operations.
JS10J carries statement source locations into every bytecode instruction. Disassembly exposes those origins and error propagation formats deterministic native and JavaScript frames with evaluation line and column information.
Values before clever representation
The first implementation should favour an understandable tagged representation. NaN boxing, inline caches, generational collection and JIT compilation are possible later optimizations, not foundation requirements.
Garbage collection after explicit roots
Tracing collection begins only after VM frames, lexical environments, objects, exception state and native handles have a coherent root graph. A collector cannot repair ambiguous ownership.
Completion is a first-class concept
Normal execution, return, throw, break and continue must remain explicit through nested control flow and finally. Encoding them as incidental jumps invites semantic defects.
Host boundary
JS++ implements JavaScript. The host decides how modules are found and whether files, timers, networking or other capabilities exist.