This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
qjs4j is a native Java implementation of QuickJS — a complete reimplementation of the QuickJS JavaScript engine in pure Java (JDK 17+, zero runtime dependencies). This is NOT a wrapper or binding to C code, but a faithful translation of the QuickJS C implementation into Java.
The project implements ES2024 features with full QuickJS specification compliance, including modern JavaScript features like async/await, for-of/for-await-of loops, promises, modules, generators, proxies, and typed arrays.
./gradlew build # Build
./gradlew test # All tests (excludes @Tag("performance"))
./gradlew clean test # Clean + test
./gradlew compileJava # Compile only (fast check)
./gradlew compileTestJava # Compile tests./gradlew test --tests "com.caoccao.qjs4j.core.JSContextTest" # Specific class
./gradlew test --tests "com.caoccao.qjs4j.core.JSContextTest.testEvalSimpleExpression" # Specific method
./gradlew test --tests "*AsyncTest" # Pattern match./gradlew performanceTest # JMH benchmarks (@Tag("performance"))
./gradlew test262Quick # Quick Test262 subset
./gradlew test262 # Full Test262 suite (requires ../test262, -Xmx2g)
./gradlew test262Language # Language tests onlycom.caoccao.qjs4j/
├── compilation/ ← Frontend pipeline
│ ├── ast/ Records implementing sealed interfaces (Expression, Statement, etc.)
│ ├── compiler/ Compiler (entry), BytecodeCompiler, CompilerDelegates, EmitHelpers
│ ├── lexer/ Lexer, LexerTemplateScanner
│ └── parser/ Parser, ParserDelegates, Expression*Parser, StatementParser
├── core/ ← JSContext, JSRuntime, JSGlobalObject, JSValue hierarchy
├── vm/ ← VirtualMachine, Opcode (262 opcodes), StackFrame, CallStack
├── builtins/ ← Constructor + Prototype pairs (ArrayConstructor/ArrayPrototype, etc.)
├── exceptions/ ← JSCompilerException, JSSyntaxErrorException, JSTypeErrorException,
│ JSRangeErrorException, JSVirtualMachineException, JSErrorException
├── memory/ ← GarbageCollector, HeapManager, MemoryPool, ReferenceCounter
├── regexp/ ← RegExp engine
├── unicode/ ← Unicode data and normalization
├── utils/ ← AtomTable, DtoaConverter, Float16
└── cli/ ← QuickJSInterpreter (main class), REPL
| Component | Path |
|---|---|
| Compiler entry | src/main/java/com/caoccao/qjs4j/compilation/compiler/Compiler.java |
| Lexer | src/main/java/com/caoccao/qjs4j/compilation/lexer/Lexer.java |
| Parser | src/main/java/com/caoccao/qjs4j/compilation/parser/Parser.java |
| BytecodeCompiler | src/main/java/com/caoccao/qjs4j/compilation/compiler/BytecodeCompiler.java |
| BytecodeEmitter | src/main/java/com/caoccao/qjs4j/compilation/compiler/BytecodeEmitter.java |
| VirtualMachine | src/main/java/com/caoccao/qjs4j/vm/VirtualMachine.java |
| Opcode | src/main/java/com/caoccao/qjs4j/vm/Opcode.java |
| JSContext | src/main/java/com/caoccao/qjs4j/core/JSContext.java |
| JSRuntime | src/main/java/com/caoccao/qjs4j/core/JSRuntime.java |
| JSGlobalObject | src/main/java/com/caoccao/qjs4j/core/JSGlobalObject.java |
-
QuickJS Fidelity: Follows QuickJS's design decisions closely — shape-based optimization, bytecode instruction set matching (with renumbering), stack-based VM, atom table for string interning, microtask queue for promises.
-
Pure Java: Zero native dependencies. All JavaScript semantics implemented in Java: IEEE 754 arithmetic, BigInt, SameValueZero equality, complete iterator/async iterator protocols.
-
Modern JavaScript: Full ES2015-ES2024 support — async/await, for-of/for-await-of, ES6 modules with dynamic import(), generators, Proxy/Reflect, typed arrays.
- JSRuntime: Shared runtime environment (atom table, module cache, job queue)
- JSContext: Independent execution context with its own global object, call stack, and exception state
- Multiple contexts can share a runtime (isolated globals, shared resources)
- Implements AutoCloseable for proper resource cleanup
- VirtualMachine: Stack-based bytecode interpreter
- Opcode: Enumeration of 262 bytecode instructions
- StackFrame: Call stack frame with locals, arguments, and return address
- CallStack: Value stack for operand manipulation
- Critical opcodes:
RETURN_ASYNC(async return),FOR_OF_START/NEXT(sync iteration),FOR_AWAIT_OF_START/NEXT(async iteration),CALL,NEW,GET_FIELD,SET_FIELD
- Parser: Recursive descent parser producing AST
- Lexer: Tokenizer for JavaScript source
- BytecodeCompiler: AST → bytecode transformation
- BytecodeEmitter: Bytecode generation and jump patching
- Compiler tracks context (async function, strict mode, loop depth) to emit appropriate opcodes
- Delegate pattern splits logic:
ExpressionCompiler,StatementCompiler,FunctionClassCompiler,PatternCompiler, etc.
Each built-in object has a Constructor + Prototype pair (e.g., ArrayConstructor/ArrayPrototype), following ES2024 specification.
- JSValue: Base class (sealed hierarchy) —
JSPrimitive(null, undefined, boolean, number, string, symbol, bigint) andJSObject(objects, arrays, functions, typed arrays) - JSFunction:
JSBytecodeFunction(compiled),JSNativeFunction(Java bindings),JSAsyncFunction(promise-returning)
RuntimeException
├── JSException (wraps JS error values thrown from eval)
├── JSErrorException (base for spec-defined JS errors)
│ ├── JSSyntaxErrorException
│ ├── JSTypeErrorException
│ └── JSRangeErrorException
├── JSCompilerException (compilation/parse errors)
└── JSVirtualMachineException (VM execution errors)
JDK 17+. JUnit 6 (6.0.1) + AssertJ + Javet (V8 parity comparison) + JSON-unit-assertj.
BaseJavetTest (preferred) — runs code in both V8/Javet and qjs4j, auto-tests strict mode:
public class MyFeatureTest extends BaseJavetTest {
@Test void testFeature() {
assertStringWithJavet("'hello'.toUpperCase()");
}
}Methods: assertStringWithJavet(), assertIntegerWithJavet(), assertBooleanWithJavet(), assertDoubleWithJavet(), assertErrorWithJavet(), assertUndefinedWithJavet(), assertObjectWithJavet(), assertBigIntegerWithJavet(), assertLongWithJavet(). Set moduleMode = true for ES module tests.
BaseTest — for internal assertions not needing V8 parity:
try (JSContext context = new JSContext(new JSRuntime())) {
JSValue result = context.eval("2 + 2");
assertThat(result.toString()).isEqualTo("4");
}Provides assertError(), assertSyntaxError(), assertTypeError(), awaitPromise().
For async code, always call context.processMicrotasks() to settle promises.
| Area | Location |
|---|---|
| Compiler/AST | src/test/java/com/caoccao/qjs4j/compilation/ast/ |
| Lexer | src/test/java/com/caoccao/qjs4j/compilation/lexer/ |
| Built-ins | src/test/java/com/caoccao/qjs4j/builtins/ |
| Core/runtime | src/test/java/com/caoccao/qjs4j/core/ |
| VM/opcodes | src/test/java/com/caoccao/qjs4j/vm/ |
| RegExp | src/test/java/com/caoccao/qjs4j/regexp/ |
| Test262 | src/test/java/com/caoccao/qjs4j/test262/ |
When porting QuickJS C code to Java:
- Opcode Mapping: QuickJS opcode numbers differ from qjs4j. Use semantic matching, not numeric matching.
- Stack Signatures: Preserve the stack effect
(nargs, npop, npush)from QuickJS. - Type Conversion:
JSValue*→JSValue,JSAtom(int) →StringorAtom, C unions → sealed classes /instanceof. - Error Handling: C return codes (
-1) → throwJSExceptionorJSVirtualMachineException. QuickJSJS_ThrowXxx()→ setpendingExceptionin context. - Memory Management: Java GC replaces manual memory.
JS_FreeValue()/JS_DupValue()not needed.
- Primitive Auto-Boxing: VM auto-boxes primitives (strings, numbers) when accessing properties or iterating via
toObject(). - Strict Mode Tracking: Each function has a
strictflag set by the compiler; the VM saves/restores it on function entry/exit. - Exception Propagation: Exceptions stored in
context.pendingException, checked after each opcode. Catch handlers clear the exception and jump. - Microtask Queue:
processMicrotasks()drains the queue with re-entrancy depth tracking to prevent infinite loops.
- License header: Apache 2.0 (
Copyright (c) 2025-2026. caoccao.com Sam Cao) onsrc/main/files. - Java 17+ features: Use records, sealed classes, pattern matching where appropriate.
- 4-space indentation, no tabs. Prefer
finalclasses where the project does. - AST nodes: Records with
SourceLocation locationimplementing sealed interfaces — seeIdentifier.java,ForOfStatement.java. - QuickJS alignment: When in doubt, follow QuickJS's approach (check
quickjs.c). - Null safety: Use explicit null checks, avoid returning null (return
JSUndefined.INSTANCEor throw). - Stack shapes: Document
Stack: ...in compiler/VM code.
docs/migration/TODO.md— Pending work itemsdocs/migration/FEATURES.md— Complete feature matrixdocs/migration/MIGRATION_STATUS.md— Overall migration progressdocs/migration/OPCODE_IMPLEMENTATION_STATUS.md— Bytecode instruction coveragedocs/migration/TEST262.md— ECMAScript conformance status
com.caoccao.qjs4j.cli.QuickJSInterpreter (configured as mainClass in build.gradle.kts).