|
| 1 | +# JTD Bytecode Codegen |
| 2 | + |
| 3 | +JTD schema-to-bytecode compiler using the JDK 24+ ClassFile API (JEP 484). Generates Java 21-compatible `.class` files at runtime for ~9x faster validation on hot paths. |
| 4 | + |
| 5 | +## Requirements |
| 6 | + |
| 7 | +- **Build**: JDK 24+ (uses `ClassFile` API, JEP 484) |
| 8 | +- **Generated bytecode**: Java 21+ (classfile version 65) |
| 9 | +- **Runtime**: JDK 24+ (the codegen itself runs at runtime to generate classes) |
| 10 | + |
| 11 | +## How It Works |
| 12 | + |
| 13 | +1. `JtdCodegen.compile(schema)` parses a JTD schema into an AST |
| 14 | +2. The emitter walks the AST and generates bytecode using `ClassFile.of().build()` |
| 15 | +3. The generated class is loaded via `MethodHandles.Lookup.defineClass()` |
| 16 | +4. The resulting `JtdValidator` validates JSON in ~9x less time than the interpreter |
| 17 | + |
| 18 | +## Usage |
| 19 | + |
| 20 | +```java |
| 21 | +import jdk.sandbox.java.util.json.*; |
| 22 | +import json.java21.jtd.JtdValidator; |
| 23 | +import json.java21.jtd.codegen.JtdCodegen; |
| 24 | + |
| 25 | +JsonValue schema = Json.parse(""" |
| 26 | + {"properties": {"name": {"type": "string"}, "age": {"type": "uint8"}}} |
| 27 | + """); |
| 28 | +JsonValue doc = Json.parse("{\"name\":\"Alice\",\"age\":30}"); |
| 29 | + |
| 30 | +// Codegen path (JDK 24+ only) |
| 31 | +JtdValidator validator = JtdCodegen.compile(schema); |
| 32 | +var result = validator.validate(doc); |
| 33 | +System.out.println(result.isValid()); // true |
| 34 | + |
| 35 | +// Falls back to interpreter automatically via JtdValidator.compile() |
| 36 | +JtdValidator interp = JtdValidator.compile(schema); |
| 37 | +``` |
| 38 | + |
| 39 | +## Performance |
| 40 | + |
| 41 | +Benchmark results (50k warmup, 200k measured iterations): |
| 42 | + |
| 43 | +| Schema | Valid doc | Invalid doc | Classfile size | |
| 44 | +|--------|-----------|-------------|----------------| |
| 45 | +| simple-type | **6.8x** faster | **2.2x** | 990 B | |
| 46 | +| enum-5 | **1.2x** | **1.5x** | 1,162 B | |
| 47 | +| nullable-int | **6.3x** | **2.2x** | 1,205 B | |
| 48 | +| properties-3 | **9.5x** | **5.3x** | 2,453 B | |
| 49 | +| props-with-optional | **12.3x** | **2.3x** | 2,112 B | |
| 50 | +| elements-of-type | **47.3x** | **3.5x** | 1,486 B | |
| 51 | +| values-of-type | **17.4x** | **4.0x** | 1,816 B | |
| 52 | +| nested-elements-of-props | **11.2x** | **4.1x** | 2,698 B | |
| 53 | +| discriminator-2-variants | **3.3x** | **3.5x** | 2,671 B | |
| 54 | +| ref-with-definitions | **1.9x** | **0.7x** | 3,008 B | |
| 55 | +| deep-nesting | **0.7x** | **0.5x** | 3,406 B | |
| 56 | +| worked-example-rfc8927 | **2.9x** | **3.6x** | 5,565 B | |
| 57 | +| **Average** | **8.7x** | **3.1x** | — | |
| 58 | + |
| 59 | +Codegen shines for array iteration (`elements-of-type` at 47x, `values-of-type` at 17x) and property validation (9-12x). It's slower on deeply nested schemas and ref-heavy schemas where bytecode overhead outweighs interpreter dispatch cost. |
| 60 | + |
| 61 | +## When to Use Codegen |
| 62 | + |
| 63 | +- **Use codegen when**: validating the same schema against many documents (e.g., event processing, API gateways) |
| 64 | +- **Use interpreter when**: one-off validation, JDK 21 runtime, or schemas dominated by deep refs/nesting |
| 65 | + |
| 66 | +## Build |
| 67 | + |
| 68 | +```bash |
| 69 | +# Automatically included on JDK 24+, skipped on JDK 21 |
| 70 | +./mvnw test -pl json-java21-jtd-codegen -am -Djava.util.logging.ConsoleHandler.level=INFO |
| 71 | +``` |
| 72 | + |
| 73 | +The parent `pom.xml` uses a profile (`jtd-codegen`) that activates on `<jdk>[24,)</jdk>`, so the module is automatically skipped when building with JDK 21. |
0 commit comments