Skip to content

Commit 04984c4

Browse files
committed
Issue #148 separate interpreter and codegen JtdValidator interfaces
- Interpreter module (json-java21-jtd): JtdValidator.compileInterpreter() replaces deprecated compile() - Codegen module (json-java21-jtd-codegen): new JtdValidator.compileGenerated() in own package - Generated bytecode implements json.java21.jtd.codegen.JtdValidator (not interpreter's interface) - ValidatorMain uses codegen's JtdValidator for generated validators - Jdt2Jar bundles codegen's JtdValidator.class in output JARs - All tests updated to use new method names - pom.xml: -Werror + showDeprecation across all modules - READMEs updated with clear two-path guidance How to verify: jenv local 21 && ./mvnw test -pl json-java21-jtd -am jenv local 25 && ./mvnw test -pl json-java21-jtd,json-java21-jtd-codegen -am
1 parent 6532a59 commit 04984c4

16 files changed

Lines changed: 130 additions & 115 deletions

File tree

jdt2jar/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,12 @@
6363
<artifactId>maven-compiler-plugin</artifactId>
6464
<version>3.13.0</version>
6565
<configuration>
66+
<release>24</release>
67+
<showWarnings>true</showWarnings>
68+
<showDeprecation>true</showDeprecation>
6669
<compilerArgs>
6770
<arg>-Xlint:all</arg>
71+
<arg>-Werror</arg>
6872
<arg>-Xdiags:verbose</arg>
6973
</compilerArgs>
7074
</configuration>

jdt2jar/src/main/java/json/java21/jdt2jar/Jdt2Jar.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ private static boolean shouldCopyRuntime(String path) {
162162
|| path.startsWith("jdk/sandbox/internal/util/json/")
163163
|| path.startsWith("json/java21/jtd/"))
164164
&& !path.startsWith("json/java21/jtd/codegen/")
165+
|| path.startsWith("json/java21/jtd/codegen/JtdValidator.class")
165166
|| path.startsWith("json/java21/jdt2jar/runtime/");
166167
}
167168

jdt2jar/src/main/java/json/java21/jdt2jar/runtime/ValidatorMain.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import jdk.sandbox.java.util.json.JsonString;
77
import jdk.sandbox.java.util.json.JsonValue;
88
import json.java21.jtd.JtdValidationResult;
9-
import json.java21.jtd.JtdValidator;
109

1110
import java.io.IOException;
1211
import java.nio.charset.StandardCharsets;
@@ -45,7 +44,7 @@ public static int run(String[] args) {
4544
}
4645
}
4746

48-
public static int run(JtdValidator validator, String[] args) throws IOException {
47+
public static int run(json.java21.jtd.codegen.JtdValidator validator, String[] args) throws IOException {
4948
final CliOptions options;
5049
try {
5150
options = parseArgs(args);
@@ -85,7 +84,7 @@ public static int run(JtdValidator validator, String[] args) throws IOException
8584
}
8685

8786
public static JtdValidationResult validate(String schemaJson, JsonValue instance) {
88-
return JtdValidator.compile(Json.parse(schemaJson)).validate(instance);
87+
return json.java21.jtd.JtdValidator.compileInterpreter(Json.parse(schemaJson)).validate(instance);
8988
}
9089

9190
private static RuntimeConfig loadConfig() throws IOException {
@@ -100,11 +99,11 @@ private static RuntimeConfig loadConfig() throws IOException {
10099
props.getProperty("schemaEntry", "jtd/schema.json"));
101100
}
102101

103-
private static JtdValidator instantiate(String validatorClassName, String schemaJson) {
102+
private static json.java21.jtd.codegen.JtdValidator instantiate(String validatorClassName, String schemaJson) {
104103
try {
105104
final var clazz = Class.forName(validatorClassName);
106105
final var ctor = clazz.getConstructor(String.class);
107-
return (JtdValidator) ctor.newInstance(schemaJson);
106+
return (json.java21.jtd.codegen.JtdValidator) ctor.newInstance(schemaJson);
108107
} catch (ReflectiveOperationException e) {
109108
throw new IllegalStateException("Could not instantiate validator class: " + validatorClassName, e);
110109
}

json-java21-jtd-codegen/README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,20 @@ For **infrequent validation** (config loading, startup checks, one-off validatio
2727

2828
```java
2929
import jdk.sandbox.java.util.json.*;
30-
import json.java21.jtd.JtdValidator;
31-
import json.java21.jtd.codegen.JtdCodegen;
30+
import json.java21.jtd.codegen.JtdValidator;
3231

3332
JsonValue schema = Json.parse("""
3433
{"properties": {"name": {"type": "string"}, "age": {"type": "uint8"}}}
3534
""");
3635
JsonValue doc = Json.parse("{\"name\":\"Alice\",\"age\":30}");
3736

3837
// Codegen path (JDK 24+ only)
39-
JtdValidator validator = JtdCodegen.compile(schema);
38+
JtdValidator validator = JtdValidator.compileGenerated(schema);
4039
var result = validator.validate(doc);
4140
System.out.println(result.isValid()); // true
4241

43-
// Falls back to interpreter automatically via JtdValidator.compile()
44-
JtdValidator interp = JtdValidator.compile(schema);
42+
// Falls back to interpreter automatically via JtdValidator.compileInterpreter()
43+
JtdValidator interp = json.java21.jtd.JtdValidator.compileInterpreter(schema);
4544
```
4645

4746
## Performance

json-java21-jtd-codegen/pom.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,11 @@
7373
<version>3.13.0</version>
7474
<configuration>
7575
<release>24</release>
76+
<showWarnings>true</showWarnings>
77+
<showDeprecation>true</showDeprecation>
7678
<compilerArgs>
7779
<arg>-Xlint:all</arg>
80+
<arg>-Werror</arg>
7881
<arg>-Xdiags:verbose</arg>
7982
</compilerArgs>
8083
</configuration>

json-java21-jtd-codegen/src/main/java/json/java21/jtd/codegen/Descriptors.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ private Descriptors() {}
4242
// -- Validation result types --
4343
static final ClassDesc CD_JtdValidationError = ClassDesc.of("json.java21.jtd.JtdValidationError");
4444
static final ClassDesc CD_JtdValidationResult = ClassDesc.of("json.java21.jtd.JtdValidationResult");
45-
static final ClassDesc CD_JtdValidator = ClassDesc.of("json.java21.jtd.JtdValidator");
45+
static final ClassDesc CD_JtdValidator = ClassDesc.of("json.java21.jtd.codegen.JtdValidator");
4646

4747
// -- Common method type descriptors --
4848
static final MethodTypeDesc MTD_String = MethodTypeDesc.of(CD_String);

json-java21-jtd-codegen/src/main/java/json/java21/jtd/codegen/JtdCodegen.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@
1010
import java.util.logging.Logger;
1111

1212
import jdk.sandbox.java.util.json.JsonValue;
13-
import json.java21.jtd.*;
13+
import json.java21.jtd.Jtd;
1414

1515
/// Compiles a JTD schema into a bytecode-generated [JtdValidator].
1616
///
1717
/// The generated class targets Java 21 (class file version 65) and
1818
/// contains only the checks the schema requires.
1919
///
20-
/// Entry point for the `JtdValidator.compileGenerated()` reflection call.
20+
/// Use via {@code JtdValidator.compileGenerated(schema)} in this package,
21+
/// or call {@link #compile(JsonValue)} directly.
2122
public final class JtdCodegen {
2223

2324
static final Logger LOG = Logger.getLogger(JtdCodegen.class.getName());
@@ -29,7 +30,8 @@ private JtdCodegen() {}
2930
/// Result of compilation including the validator and generated class statistics.
3031
public record CompileResult(JtdValidator validator, int classfileBytes) {}
3132

32-
/// Public factory invoked by [JtdValidator.compileGenerated] via reflection.
33+
/// Compiles a JTD schema into a bytecode-generated validator.
34+
/// Requires JDK 24+ (ClassFile API). Generated classes target Java 21 bytecode.
3335
public static JtdValidator compile(JsonValue schema) {
3436
return compileWithStats(schema).validator();
3537
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package json.java21.jtd.codegen;
2+
3+
import jdk.sandbox.java.util.json.JsonValue;
4+
import json.java21.jtd.JtdValidationResult;
5+
6+
import java.util.Objects;
7+
8+
/// Functional interface for validating a JSON instance against a JTD schema
9+
/// using bytecode-generated validators.
10+
///
11+
/// Requires JDK 24+ at build time (ClassFile API, JEP 484).
12+
/// Generated bytecode targets Java 21 (classfile version 65) so it runs on any JDK 21+ runtime.
13+
///
14+
/// For the interpreter-based validator (always available, JDK 21+), see
15+
/// {@code json.java21.jtd.JtdValidator} in the {@code json-java21-jtd} module.
16+
///
17+
/// Obtain an instance via:
18+
/// - [#compileGenerated(JsonValue)] -- bytecode-generated path, requires JDK 24+.
19+
@FunctionalInterface
20+
public interface JtdValidator {
21+
22+
/// Validates an instance against the compiled schema.
23+
///
24+
/// @param instance the JSON value to validate
25+
/// @return the validation result with RFC 8927 error pairs
26+
JtdValidationResult validate(JsonValue instance);
27+
28+
/// Compiles a JTD schema into a bytecode-generated validator.
29+
/// Requires JDK 24+ (ClassFile API). Generated classes target Java 21 bytecode.
30+
///
31+
/// @param schema the JTD schema as a parsed [JsonValue]
32+
/// @return a reusable [JtdValidator] backed by generated bytecode
33+
/// @throws IllegalArgumentException if the schema is invalid per RFC 8927
34+
static JtdValidator compileGenerated(JsonValue schema) {
35+
Objects.requireNonNull(schema, "schema must not be null");
36+
return JtdCodegen.compile(schema);
37+
}
38+
}

json-java21-jtd-codegen/src/test/java/json/java21/jtd/codegen/BenchmarkTest.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import jdk.sandbox.java.util.json.Json;
44
import jdk.sandbox.java.util.json.JsonValue;
5-
import json.java21.jtd.JtdValidator;
65
import org.junit.jupiter.api.Test;
76

87
import java.util.LinkedHashMap;
@@ -137,7 +136,7 @@ void benchmarkAll() {
137136
final var codegenResult = JtdCodegen.compileWithStats(schema);
138137
final var codegen = codegenResult.validator();
139138
final var classfileBytes = codegenResult.classfileBytes();
140-
final var interpreter = JtdValidator.compile(schema);
139+
final var interpreter = json.java21.jtd.JtdValidator.compileInterpreter(schema);
141140

142141
assertThat(codegen.validate(validDoc).isValid()).isTrue();
143142
assertThat(codegen.validate(invalidDoc).isValid()).isFalse();
@@ -149,8 +148,8 @@ void benchmarkAll() {
149148

150149
final var codegenValidNs = measure(codegen, validDoc);
151150
final var codegenInvalidNs = measure(codegen, invalidDoc);
152-
final var interpValidNs = measure(interpreter, validDoc);
153-
final var interpInvalidNs = measure(interpreter, invalidDoc);
151+
final var interpValidNs = measureInterpreter(interpreter, validDoc);
152+
final var interpInvalidNs = measureInterpreter(interpreter, invalidDoc);
154153

155154
final var speedupValid = (double) interpValidNs / codegenValidNs;
156155
final var speedupInvalid = (double) interpInvalidNs / codegenInvalidNs;
@@ -200,6 +199,16 @@ private long measure(JtdValidator validator, JsonValue doc) {
200199
return elapsed / MEASURED_ITERATIONS;
201200
}
202201

202+
private long measureInterpreter(json.java21.jtd.JtdValidator validator, JsonValue doc) {
203+
IntStream.range(0, WARMUP_ITERATIONS).forEach(_ -> validator.validate(doc));
204+
205+
final var start = System.nanoTime();
206+
IntStream.range(0, MEASURED_ITERATIONS).forEach(_ -> validator.validate(doc));
207+
final var elapsed = System.nanoTime() - start;
208+
209+
return elapsed / MEASURED_ITERATIONS;
210+
}
211+
203212
record BenchResult(int classfileBytes, int schemaJsonChars,
204213
long codegenValidNs, long interpValidNs,
205214
long codegenInvalidNs, long interpInvalidNs) {}

json-java21-jtd-codegen/src/test/java/json/java21/jtd/codegen/CrossValidationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ void interpreterAndCodegenAgree(String name, String schemaJson, String instanceJ
228228
final var schema = Json.parse(schemaJson);
229229
final var instance = Json.parse(instanceJson);
230230

231-
final var interpreter = json.java21.jtd.JtdValidator.compile(schema);
231+
final var interpreter = json.java21.jtd.JtdValidator.compileInterpreter(schema);
232232
final var codegen = JtdCodegen.compile(schema);
233233

234234
final var interpResult = interpreter.validate(instance);

0 commit comments

Comments
 (0)