Skip to content

Commit f629733

Browse files
committed
Merge origin/jtd-gen into jdt-transforms (resolve pom.xml module list conflict)
2 parents 5be3a28 + c1805a9 commit f629733

39 files changed

+17606
-11
lines changed

json-java21-jtd-codegen/pom.xml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
5+
http://maven.apache.org/xsd/maven-4.0.0.xsd">
6+
<modelVersion>4.0.0</modelVersion>
7+
8+
<parent>
9+
<groupId>io.github.simbo1905.json</groupId>
10+
<artifactId>parent</artifactId>
11+
<version>0.1.9</version>
12+
</parent>
13+
14+
<artifactId>java.util.json.jtd.codegen</artifactId>
15+
<packaging>jar</packaging>
16+
<name>java.util.json Java21 Backport JTD Codegen</name>
17+
<url>https://simbo1905.github.io/java.util.json.Java21/</url>
18+
<scm>
19+
<connection>scm:git:https://github.com/simbo1905/java.util.json.Java21.git</connection>
20+
<developerConnection>scm:git:git@github.com:simbo1905/java.util.json.Java21.git</developerConnection>
21+
<url>https://github.com/simbo1905/java.util.json.Java21</url>
22+
<tag>HEAD</tag>
23+
</scm>
24+
<description>Bytecode-generated JTD validators using the JDK 24+ ClassFile API.
25+
Generates Java 21 compatible classfiles for hot-path validation.
26+
Optional dependency: falls back to the interpreter path when absent.</description>
27+
28+
<properties>
29+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
30+
<maven.compiler.release>24</maven.compiler.release>
31+
</properties>
32+
33+
<dependencies>
34+
<dependency>
35+
<groupId>io.github.simbo1905.json</groupId>
36+
<artifactId>java.util.json</artifactId>
37+
<version>${project.version}</version>
38+
</dependency>
39+
<dependency>
40+
<groupId>io.github.simbo1905.json</groupId>
41+
<artifactId>java.util.json.jtd</artifactId>
42+
<version>${project.version}</version>
43+
</dependency>
44+
45+
<!-- Test dependencies -->
46+
<dependency>
47+
<groupId>org.junit.jupiter</groupId>
48+
<artifactId>junit-jupiter-api</artifactId>
49+
<scope>test</scope>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.junit.jupiter</groupId>
53+
<artifactId>junit-jupiter-engine</artifactId>
54+
<scope>test</scope>
55+
</dependency>
56+
<dependency>
57+
<groupId>org.junit.jupiter</groupId>
58+
<artifactId>junit-jupiter-params</artifactId>
59+
<scope>test</scope>
60+
</dependency>
61+
<dependency>
62+
<groupId>org.assertj</groupId>
63+
<artifactId>assertj-core</artifactId>
64+
<scope>test</scope>
65+
</dependency>
66+
</dependencies>
67+
68+
<build>
69+
<plugins>
70+
<plugin>
71+
<groupId>org.apache.maven.plugins</groupId>
72+
<artifactId>maven-compiler-plugin</artifactId>
73+
<version>3.13.0</version>
74+
<configuration>
75+
<release>24</release>
76+
<compilerArgs>
77+
<arg>-Xlint:all</arg>
78+
<arg>-Xdiags:verbose</arg>
79+
</compilerArgs>
80+
</configuration>
81+
</plugin>
82+
<plugin>
83+
<groupId>org.apache.maven.plugins</groupId>
84+
<artifactId>maven-javadoc-plugin</artifactId>
85+
<configuration>
86+
<release>24</release>
87+
<doclint>none</doclint>
88+
</configuration>
89+
</plugin>
90+
</plugins>
91+
</build>
92+
</project>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package json.java21.jtd.codegen;
2+
3+
import java.lang.constant.ClassDesc;
4+
import java.lang.constant.ConstantDescs;
5+
import java.lang.constant.MethodTypeDesc;
6+
7+
/// Shared class descriptors and method type descriptors for bytecode emission.
8+
///
9+
/// All fields are compile-time constants referencing the types the generated
10+
/// classfiles interact with at runtime (JSON API, validation result types, JDK stdlib).
11+
final class Descriptors {
12+
13+
private Descriptors() {}
14+
15+
// -- JDK types --
16+
static final ClassDesc CD_Object = ConstantDescs.CD_Object;
17+
static final ClassDesc CD_String = ConstantDescs.CD_String;
18+
static final ClassDesc CD_Math = ClassDesc.of("java.lang.Math");
19+
static final ClassDesc CD_CharSequence = ClassDesc.of("java.lang.CharSequence");
20+
static final ClassDesc CD_OffsetDateTime = ClassDesc.of("java.time.OffsetDateTime");
21+
static final ClassDesc CD_DateTimeFormatter = ClassDesc.of("java.time.format.DateTimeFormatter");
22+
static final ClassDesc CD_Pattern = ClassDesc.of("java.util.regex.Pattern");
23+
static final ClassDesc CD_Matcher = ClassDesc.of("java.util.regex.Matcher");
24+
25+
// -- Collections --
26+
static final ClassDesc CD_ArrayList = ClassDesc.of("java.util.ArrayList");
27+
static final ClassDesc CD_List = ClassDesc.of("java.util.List");
28+
static final ClassDesc CD_Map = ClassDesc.of("java.util.Map");
29+
static final ClassDesc CD_MapEntry = ClassDesc.of("java.util.Map$Entry");
30+
static final ClassDesc CD_Set = ClassDesc.of("java.util.Set");
31+
static final ClassDesc CD_Iterator = ClassDesc.of("java.util.Iterator");
32+
33+
// -- JSON API types --
34+
static final ClassDesc CD_JsonValue = ClassDesc.of("jdk.sandbox.java.util.json.JsonValue");
35+
static final ClassDesc CD_JsonObject = ClassDesc.of("jdk.sandbox.java.util.json.JsonObject");
36+
static final ClassDesc CD_JsonArray = ClassDesc.of("jdk.sandbox.java.util.json.JsonArray");
37+
static final ClassDesc CD_JsonString = ClassDesc.of("jdk.sandbox.java.util.json.JsonString");
38+
static final ClassDesc CD_JsonNumber = ClassDesc.of("jdk.sandbox.java.util.json.JsonNumber");
39+
static final ClassDesc CD_JsonBoolean = ClassDesc.of("jdk.sandbox.java.util.json.JsonBoolean");
40+
static final ClassDesc CD_JsonNull = ClassDesc.of("jdk.sandbox.java.util.json.JsonNull");
41+
42+
// -- Validation result types --
43+
static final ClassDesc CD_JtdValidationError = ClassDesc.of("json.java21.jtd.JtdValidationError");
44+
static final ClassDesc CD_JtdValidationResult = ClassDesc.of("json.java21.jtd.JtdValidationResult");
45+
static final ClassDesc CD_JtdValidator = ClassDesc.of("json.java21.jtd.JtdValidator");
46+
47+
// -- Common method type descriptors --
48+
static final MethodTypeDesc MTD_String = MethodTypeDesc.of(CD_String);
49+
static final MethodTypeDesc MTD_boolean = MethodTypeDesc.of(ConstantDescs.CD_boolean);
50+
static final MethodTypeDesc MTD_double = MethodTypeDesc.of(ConstantDescs.CD_double);
51+
static final MethodTypeDesc MTD_long = MethodTypeDesc.of(ConstantDescs.CD_long);
52+
static final MethodTypeDesc MTD_int = MethodTypeDesc.of(ConstantDescs.CD_int);
53+
static final MethodTypeDesc MTD_boolean_Object = MethodTypeDesc.of(ConstantDescs.CD_boolean, CD_Object);
54+
static final MethodTypeDesc MTD_Object_Object = MethodTypeDesc.of(CD_Object, CD_Object);
55+
static final MethodTypeDesc MTD_Object_int = MethodTypeDesc.of(CD_Object, ConstantDescs.CD_int);
56+
static final MethodTypeDesc MTD_boolean_CharSequence = MethodTypeDesc.of(ConstantDescs.CD_boolean, CD_CharSequence);
57+
static final MethodTypeDesc MTD_String_String = MethodTypeDesc.of(CD_String, CD_String);
58+
static final MethodTypeDesc MTD_String_int = MethodTypeDesc.of(CD_String, ConstantDescs.CD_int);
59+
static final MethodTypeDesc MTD_String_CharSeq_CharSeq = MethodTypeDesc.of(CD_String, CD_CharSequence, CD_CharSequence);
60+
static final MethodTypeDesc MTD_Map = MethodTypeDesc.of(CD_Map);
61+
static final MethodTypeDesc MTD_List = MethodTypeDesc.of(CD_List);
62+
static final MethodTypeDesc MTD_Set = MethodTypeDesc.of(CD_Set);
63+
static final MethodTypeDesc MTD_Iterator = MethodTypeDesc.of(CD_Iterator);
64+
static final MethodTypeDesc MTD_Object = MethodTypeDesc.of(CD_Object);
65+
static final MethodTypeDesc MTD_double_double = MethodTypeDesc.of(ConstantDescs.CD_double, ConstantDescs.CD_double);
66+
static final MethodTypeDesc MTD_Pattern_String = MethodTypeDesc.of(CD_Pattern, CD_String);
67+
static final MethodTypeDesc MTD_Matcher_CharSequence = MethodTypeDesc.of(CD_Matcher, CD_CharSequence);
68+
static final MethodTypeDesc MTD_OffsetDateTime_CharSeq_DTF = MethodTypeDesc.of(CD_OffsetDateTime, CD_CharSequence, CD_DateTimeFormatter);
69+
static final MethodTypeDesc MTD_void_String_String = MethodTypeDesc.of(ConstantDescs.CD_void, CD_String, CD_String);
70+
static final MethodTypeDesc MTD_JtdValidationResult = MethodTypeDesc.of(CD_JtdValidationResult);
71+
static final MethodTypeDesc MTD_JtdValidationResult_List = MethodTypeDesc.of(CD_JtdValidationResult, CD_List);
72+
static final MethodTypeDesc MTD_JtdValidationResult_JsonValue = MethodTypeDesc.of(CD_JtdValidationResult, CD_JsonValue);
73+
static final MethodTypeDesc MTD_void_String = MethodTypeDesc.of(ConstantDescs.CD_void, CD_String);
74+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package json.java21.jtd.codegen;
2+
3+
import java.lang.classfile.CodeBuilder;
4+
import java.lang.classfile.TypeKind;
5+
import java.lang.constant.ConstantDescs;
6+
7+
import json.java21.jtd.JtdSchema;
8+
9+
import static json.java21.jtd.codegen.Descriptors.*;
10+
11+
/// Emits bytecode for JTD Discriminator schema (tagged union).
12+
final class EmitDiscriminator {
13+
14+
private EmitDiscriminator() {}
15+
16+
static void emit(CodeBuilder cob, JtdSchema.DiscriminatorSchema d,
17+
int instSlot, int errSlot,
18+
String instPath, String schemaPath) {
19+
var end = cob.newLabel();
20+
21+
// Step 1: must be object
22+
var step1Fail = cob.newLabel();
23+
cob.aload(instSlot);
24+
cob.instanceOf(CD_JsonObject);
25+
cob.ifeq(step1Fail);
26+
27+
cob.aload(instSlot);
28+
cob.checkcast(CD_JsonObject);
29+
cob.invokeinterface(CD_JsonObject, "members", MTD_Map);
30+
int mapSlot = cob.allocateLocal(TypeKind.REFERENCE);
31+
cob.astore(mapSlot);
32+
33+
// Step 2: tag must exist
34+
var step2Fail = cob.newLabel();
35+
cob.aload(mapSlot);
36+
cob.ldc(d.discriminator());
37+
cob.invokeinterface(CD_Map, "containsKey", MTD_boolean_Object);
38+
cob.ifeq(step2Fail);
39+
40+
cob.aload(mapSlot);
41+
cob.ldc(d.discriminator());
42+
cob.invokeinterface(CD_Map, "get", MTD_Object_Object);
43+
cob.checkcast(CD_JsonValue);
44+
int tagValSlot = cob.allocateLocal(TypeKind.REFERENCE);
45+
cob.astore(tagValSlot);
46+
47+
// Step 3: tag must be string
48+
var step3Fail = cob.newLabel();
49+
cob.aload(tagValSlot);
50+
cob.instanceOf(CD_JsonString);
51+
cob.ifeq(step3Fail);
52+
53+
cob.aload(tagValSlot);
54+
cob.checkcast(CD_JsonString);
55+
cob.invokeinterface(CD_JsonString, "string", MTD_String);
56+
int tagStrSlot = cob.allocateLocal(TypeKind.REFERENCE);
57+
cob.astore(tagStrSlot);
58+
59+
// Step 4: dispatch to variants
60+
for (final var entry : d.mapping().entrySet()) {
61+
final var tagValue = entry.getKey();
62+
final var variantSchema = entry.getValue();
63+
var nextVariant = cob.newLabel();
64+
65+
cob.aload(tagStrSlot);
66+
cob.ldc(tagValue);
67+
cob.invokevirtual(CD_String, "equals", MTD_boolean_Object);
68+
cob.ifeq(nextVariant);
69+
70+
if (variantSchema instanceof JtdSchema.PropertiesSchema props) {
71+
EmitProperties.emit(cob, props, instSlot, errSlot, instPath,
72+
schemaPath + "/mapping/" + tagValue, d.discriminator());
73+
} else {
74+
EmitNode.emit(cob, variantSchema, instSlot, errSlot, instPath,
75+
schemaPath + "/mapping/" + tagValue);
76+
}
77+
cob.goto_(end);
78+
79+
cob.labelBinding(nextVariant);
80+
}
81+
82+
// Step 5: tag not in mapping
83+
EmitError.addError(cob, errSlot,
84+
instPath + "/" + d.discriminator(), schemaPath + "/mapping");
85+
cob.goto_(end);
86+
87+
// Error paths
88+
cob.labelBinding(step1Fail);
89+
EmitError.addError(cob, errSlot, instPath, schemaPath + "/discriminator");
90+
cob.goto_(end);
91+
92+
cob.labelBinding(step2Fail);
93+
EmitError.addError(cob, errSlot, instPath, schemaPath + "/discriminator");
94+
cob.goto_(end);
95+
96+
cob.labelBinding(step3Fail);
97+
EmitError.addError(cob, errSlot,
98+
instPath + "/" + d.discriminator(), schemaPath + "/discriminator");
99+
100+
cob.labelBinding(end);
101+
}
102+
103+
/// Dynamic-path variant: parent instancePath from local variable.
104+
static void emitDynamic(CodeBuilder cob, JtdSchema.DiscriminatorSchema d,
105+
int instSlot, int errSlot,
106+
int pathSlot, String schemaPath) {
107+
var end = cob.newLabel();
108+
109+
var step1Fail = cob.newLabel();
110+
cob.aload(instSlot);
111+
cob.instanceOf(CD_JsonObject);
112+
cob.ifeq(step1Fail);
113+
114+
cob.aload(instSlot);
115+
cob.checkcast(CD_JsonObject);
116+
cob.invokeinterface(CD_JsonObject, "members", MTD_Map);
117+
int mapSlot = cob.allocateLocal(TypeKind.REFERENCE);
118+
cob.astore(mapSlot);
119+
120+
var step2Fail = cob.newLabel();
121+
cob.aload(mapSlot);
122+
cob.ldc(d.discriminator());
123+
cob.invokeinterface(CD_Map, "containsKey", MTD_boolean_Object);
124+
cob.ifeq(step2Fail);
125+
126+
cob.aload(mapSlot);
127+
cob.ldc(d.discriminator());
128+
cob.invokeinterface(CD_Map, "get", MTD_Object_Object);
129+
cob.checkcast(CD_JsonValue);
130+
int tagValSlot = cob.allocateLocal(TypeKind.REFERENCE);
131+
cob.astore(tagValSlot);
132+
133+
var step3Fail = cob.newLabel();
134+
cob.aload(tagValSlot);
135+
cob.instanceOf(CD_JsonString);
136+
cob.ifeq(step3Fail);
137+
138+
cob.aload(tagValSlot);
139+
cob.checkcast(CD_JsonString);
140+
cob.invokeinterface(CD_JsonString, "string", MTD_String);
141+
int tagStrSlot = cob.allocateLocal(TypeKind.REFERENCE);
142+
cob.astore(tagStrSlot);
143+
144+
for (final var entry : d.mapping().entrySet()) {
145+
final var tagValue = entry.getKey();
146+
final var variantSchema = entry.getValue();
147+
var nextVariant = cob.newLabel();
148+
149+
cob.aload(tagStrSlot);
150+
cob.ldc(tagValue);
151+
cob.invokevirtual(CD_String, "equals", MTD_boolean_Object);
152+
cob.ifeq(nextVariant);
153+
154+
if (variantSchema instanceof JtdSchema.PropertiesSchema props) {
155+
EmitProperties.emitDynamic(cob, props, instSlot, errSlot, pathSlot,
156+
schemaPath + "/mapping/" + tagValue, d.discriminator());
157+
} else {
158+
EmitNode.emitDynamic(cob, variantSchema, instSlot, errSlot, pathSlot,
159+
schemaPath + "/mapping/" + tagValue);
160+
}
161+
cob.goto_(end);
162+
163+
cob.labelBinding(nextVariant);
164+
}
165+
166+
// tag not in mapping: error at pathSlot + "/" + discriminator
167+
cob.aload(pathSlot);
168+
cob.ldc("/" + d.discriminator());
169+
cob.invokevirtual(CD_String, "concat", MTD_String_String);
170+
int tagPathSlot = cob.allocateLocal(TypeKind.REFERENCE);
171+
cob.astore(tagPathSlot);
172+
EmitError.addErrorDynamic(cob, errSlot, tagPathSlot, schemaPath + "/mapping");
173+
cob.goto_(end);
174+
175+
cob.labelBinding(step1Fail);
176+
EmitError.addErrorDynamic(cob, errSlot, pathSlot, schemaPath + "/discriminator");
177+
cob.goto_(end);
178+
179+
cob.labelBinding(step2Fail);
180+
EmitError.addErrorDynamic(cob, errSlot, pathSlot, schemaPath + "/discriminator");
181+
cob.goto_(end);
182+
183+
cob.labelBinding(step3Fail);
184+
cob.aload(pathSlot);
185+
cob.ldc("/" + d.discriminator());
186+
cob.invokevirtual(CD_String, "concat", MTD_String_String);
187+
int tagPath2Slot = cob.allocateLocal(TypeKind.REFERENCE);
188+
cob.astore(tagPath2Slot);
189+
EmitError.addErrorDynamic(cob, errSlot, tagPath2Slot, schemaPath + "/discriminator");
190+
191+
cob.labelBinding(end);
192+
}
193+
}

0 commit comments

Comments
 (0)