Skip to content

Commit c17b3a9

Browse files
committed
implement static code gen
1 parent 33ed9f6 commit c17b3a9

11 files changed

Lines changed: 188 additions & 35 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.jsoniter.demo.codegen;
2+
3+
import com.jsoniter.StaticCodeGenerator;
4+
import com.jsoniter.spi.CodegenConfig;
5+
import com.jsoniter.spi.ExtensionManager;
6+
import com.jsoniter.spi.TypeLiteral;
7+
8+
import java.util.List;
9+
10+
public class DemoCodegenConfig implements CodegenConfig {
11+
12+
@Override
13+
public void beforeCodegen() {
14+
ExtensionManager.disableDynamicCodegen();
15+
}
16+
17+
@Override
18+
public TypeLiteral[] getTypeLiterals() {
19+
return new TypeLiteral[]{
20+
new TypeLiteral<List<String>>() {
21+
}
22+
};
23+
}
24+
25+
public static void main(String[] args) throws Exception {
26+
StaticCodeGenerator.main(new String[]{DemoCodegenConfig.class.getCanonicalName()});
27+
}
28+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package decoder.java.util.List_java.lang;
2+
public class String implements com.jsoniter.spi.Decoder {
3+
public static Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { if (iter.readNull()) { return null; }
4+
java.util.ArrayList col = (java.util.ArrayList)com.jsoniter.CodegenAccess.resetExistingObject(iter);
5+
if (!com.jsoniter.CodegenAccess.readArrayStart(iter)) {
6+
return col == null ? new java.util.ArrayList(0): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col);
7+
}
8+
Object a1 = iter.readString();
9+
if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') {
10+
java.util.ArrayList obj = col == null ? new java.util.ArrayList(1): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col);
11+
obj.add(a1);
12+
return obj;
13+
}
14+
Object a2 = iter.readString();
15+
if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') {
16+
java.util.ArrayList obj = col == null ? new java.util.ArrayList(2): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col);
17+
obj.add(a1);
18+
obj.add(a2);
19+
return obj;
20+
}
21+
Object a3 = iter.readString();
22+
if (com.jsoniter.CodegenAccess.nextToken(iter) != ',') {
23+
java.util.ArrayList obj = col == null ? new java.util.ArrayList(3): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col);
24+
obj.add(a1);
25+
obj.add(a2);
26+
obj.add(a3);
27+
return obj;
28+
}
29+
Object a4 = iter.readString();
30+
java.util.ArrayList obj = col == null ? new java.util.ArrayList(8): (java.util.ArrayList)com.jsoniter.CodegenAccess.reuseCollection(col);
31+
obj.add(a1);
32+
obj.add(a2);
33+
obj.add(a3);
34+
obj.add(a4);
35+
while (com.jsoniter.CodegenAccess.nextToken(iter) == ',') {
36+
obj.add(iter.readString());
37+
}
38+
return obj;
39+
}public Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException {
40+
return decode_(iter);
41+
}
42+
}

src/main/java/com/jsoniter/Codegen.java

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package com.jsoniter;
22

33
import com.jsoniter.spi.*;
4-
import javassist.ClassPool;
5-
import javassist.CtClass;
6-
import javassist.CtMethod;
7-
import javassist.CtNewMethod;
4+
import javassist.*;
85

6+
import java.io.File;
7+
import java.io.FileOutputStream;
8+
import java.io.IOException;
9+
import java.io.OutputStreamWriter;
910
import java.lang.reflect.*;
1011
import java.util.*;
1112

1213
class Codegen {
14+
static boolean staticGen = false;
1315
static boolean strictMode = false;
1416
static ClassPool pool = ClassPool.getDefault();
1517

@@ -47,21 +49,17 @@ private synchronized static Decoder gen(String cacheKey, Type type) {
4749
clazz = (Class) type;
4850
}
4951
String source = genSource(cacheKey, clazz, typeArgs);
52+
source = "public static Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { "
53+
+ source + "}";
5054
if ("true".equals(System.getenv("JSONITER_DEBUG"))) {
5155
System.out.println(">>> " + cacheKey);
5256
System.out.println(source);
5357
}
5458
try {
55-
CtClass ctClass = pool.makeClass(cacheKey);
56-
ctClass.setInterfaces(new CtClass[]{pool.get(Decoder.class.getName())});
57-
CtMethod staticMethod = CtNewMethod.make(source, ctClass);
58-
ctClass.addMethod(staticMethod);
59-
CtMethod interfaceMethod = CtNewMethod.make("" +
60-
"public Object decode(com.jsoniter.JsonIterator iter) {" +
61-
"return decode_(iter);" +
62-
"}", ctClass);
63-
ctClass.addMethod(interfaceMethod);
64-
decoder = (Decoder) ctClass.toClass().newInstance();
59+
if (staticGen) {
60+
staticGen(cacheKey, source);
61+
}
62+
decoder = dynamicGen(cacheKey, source);
6563
ExtensionManager.addNewDecoder(cacheKey, decoder);
6664
return decoder;
6765
} catch (Exception e) {
@@ -71,6 +69,60 @@ private synchronized static Decoder gen(String cacheKey, Type type) {
7169
}
7270
}
7371

72+
private static void staticGen(String cacheKey, String source) throws IOException {
73+
createDir(cacheKey);
74+
String fileName = cacheKey.replace('.', '/') + ".java";
75+
FileOutputStream fileOutputStream = new FileOutputStream(fileName);
76+
try {
77+
OutputStreamWriter writer = new OutputStreamWriter(fileOutputStream);
78+
try {
79+
staticGen(cacheKey, writer, source);
80+
} finally {
81+
writer.close();
82+
}
83+
} finally {
84+
fileOutputStream.close();
85+
}
86+
}
87+
88+
private static void staticGen(String cacheKey, OutputStreamWriter writer, String source) throws IOException {
89+
String className = cacheKey.substring(cacheKey.lastIndexOf('.') + 1);
90+
String packageName = cacheKey.substring(0, cacheKey.lastIndexOf('.'));
91+
writer.write("package " + packageName + ";\n");
92+
writer.write("public class " + className + " implements com.jsoniter.spi.Decoder {\n");
93+
writer.write(source);
94+
writer.write("public Object decode(com.jsoniter.JsonIterator iter) throws java.io.IOException {\n");
95+
writer.write("return decode_(iter);\n");
96+
writer.write("}\n");
97+
writer.write("}\n");
98+
}
99+
100+
private static void createDir(String cacheKey) {
101+
String[] parts = cacheKey.split("\\.");
102+
File parent = new File(".");
103+
for (int i = 0; i < parts.length - 1; i++) {
104+
String part = parts[i];
105+
File current = new File(parent, part);
106+
current.mkdir();
107+
parent = current;
108+
}
109+
}
110+
111+
private static Decoder dynamicGen(String cacheKey, String source) throws Exception {
112+
Decoder decoder;
113+
CtClass ctClass = pool.makeClass(cacheKey);
114+
ctClass.setInterfaces(new CtClass[]{pool.get(Decoder.class.getName())});
115+
CtMethod staticMethod = CtNewMethod.make(source, ctClass);
116+
ctClass.addMethod(staticMethod);
117+
CtMethod interfaceMethod = CtNewMethod.make("" +
118+
"public Object decode(com.jsoniter.JsonIterator iter) {" +
119+
"return decode_(iter);" +
120+
"}", ctClass);
121+
ctClass.addMethod(interfaceMethod);
122+
decoder = (Decoder) ctClass.toClass().newInstance();
123+
return decoder;
124+
}
125+
74126
private static String genSource(String cacheKey, Class clazz, Type[] typeArgs) {
75127
if (CodegenImplNative.NATIVE_READS.containsKey(clazz.getName())) {
76128
return CodegenImplNative.genNative(clazz.getName());
@@ -104,4 +156,11 @@ private static String genSource(String cacheKey, Class clazz, Type[] typeArgs) {
104156
}
105157
return CodegenImplObject.genObjectUsingHash(clazz, cacheKey, desc);
106158
}
159+
160+
public static void staticGenDecoders(TypeLiteral[] typeLiterals) {
161+
staticGen = true;
162+
for (TypeLiteral typeLiteral : typeLiterals) {
163+
gen(typeLiteral.getDecoderCacheKey(), typeLiteral.getType());
164+
}
165+
}
107166
}

src/main/java/com/jsoniter/CodegenAccess.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,8 @@ private final static boolean skipWhitespacesWithoutLoadMore(JsonIterator iter) t
230230
}
231231
return true;
232232
}
233+
234+
public static void staticGenDecoders(TypeLiteral[] typeLiterals) {
235+
Codegen.staticGenDecoders(typeLiterals);
236+
}
233237
}

src/main/java/com/jsoniter/CodegenImplArray.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ public static String genArray(Class clazz) {
1717
throw new IllegalArgumentException("nested array not supported: " + clazz.getCanonicalName());
1818
}
1919
StringBuilder lines = new StringBuilder();
20-
append(lines, "public static Object decode_(com.jsoniter.JsonIterator iter) {");
2120
append(lines, "if (iter.readNull()) { return null; }");
2221
append(lines, "if (!com.jsoniter.CodegenAccess.readArrayStart(iter)) {");
2322
append(lines, "return new {{comp}}[0];");
@@ -53,7 +52,6 @@ public static String genArray(Class clazz) {
5352
append(lines, "{{comp}}[] result = new {{comp}}[i];");
5453
append(lines, "System.arraycopy(arr, 0, result, 0, i);");
5554
append(lines, "return result;");
56-
append(lines, "}");
5755
return lines.toString().replace(
5856
"{{comp}}", compType.getCanonicalName()).replace(
5957
"{{op}}", CodegenImplNative.genReadOp(compType));
@@ -84,7 +82,6 @@ public static String genCollection(Class clazz, Type[] typeArgs) {
8482

8583
private static String genCollectionWithCapacity(Class clazz, Type compType) {
8684
StringBuilder lines = new StringBuilder();
87-
append(lines, "public static Object decode_(com.jsoniter.JsonIterator iter) {");
8885
append(lines, "if (iter.readNull()) { return null; }");
8986
append(lines, "{{clazz}} col = ({{clazz}})com.jsoniter.CodegenAccess.resetExistingObject(iter);");
9087
append(lines, "if (!com.jsoniter.CodegenAccess.readArrayStart(iter)) {");
@@ -122,15 +119,13 @@ private static String genCollectionWithCapacity(Class clazz, Type compType) {
122119
append(lines, "}");
123120
// append(lines, "if (c != ']') { com.jsoniter.CodegenAccess.reportIncompleteArray(iter); }");
124121
append(lines, "return obj;");
125-
append(lines, "}");
126122
return lines.toString().replace(
127123
"{{clazz}}", clazz.getName()).replace(
128124
"{{op}}", CodegenImplNative.genReadOp(compType));
129125
}
130126

131127
private static String genCollectionWithoutCapacity(Class clazz, Type compType) {
132128
StringBuilder lines = new StringBuilder();
133-
append(lines, "public static Object decode_(com.jsoniter.JsonIterator iter) {");
134129
append(lines, "if (iter.readNull()) { return null; }");
135130
append(lines, "{{clazz}} col = ({{clazz}})com.jsoniter.CodegenAccess.resetExistingObject(iter);");
136131
append(lines, "if (!com.jsoniter.CodegenAccess.readArrayStart(iter)) {");
@@ -168,7 +163,6 @@ private static String genCollectionWithoutCapacity(Class clazz, Type compType) {
168163
append(lines, "}");
169164
// append(lines, "if (c != ']') { com.jsoniter.CodegenAccess.reportIncompleteArray(iter); }");
170165
append(lines, "return obj;");
171-
append(lines, "}");
172166
return lines.toString().replace(
173167
"{{clazz}}", clazz.getName()).replace(
174168
"{{op}}", CodegenImplNative.genReadOp(compType));

src/main/java/com/jsoniter/CodegenImplMap.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public static String genMap(Class clazz, Type[] typeArgs) {
2626
clazz = HashMap.class;
2727
}
2828
StringBuilder lines = new StringBuilder();
29-
append(lines, "public static Object decode_(com.jsoniter.JsonIterator iter) {");
3029
append(lines, "if (iter.readNull()) { return null; }");
3130
append(lines, "{{clazz}} map = ({{clazz}})com.jsoniter.CodegenAccess.resetExistingObject(iter);");
3231
append(lines, "if (map == null) { map = new {{clazz}}(); }");
@@ -40,7 +39,6 @@ public static String genMap(Class clazz, Type[] typeArgs) {
4039
append(lines, "map.put(field, {{op}});");
4140
append(lines, "}");
4241
append(lines, "return map;");
43-
append(lines, "}");
4442
return lines.toString().replace("{{clazz}}", clazz.getName()).replace("{{op}}", CodegenImplNative.genReadOp(valueType));
4543
}
4644

src/main/java/com/jsoniter/CodegenImplNative.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,7 @@ public static String genNative(String nativeReadKey) {
5757
if (op == null) {
5858
throw new JsonException("do not know how to read: " + nativeReadKey);
5959
}
60-
StringBuilder lines = new StringBuilder();
61-
append(lines, "public static Object decode_(com.jsoniter.JsonIterator iter) {");
62-
append(lines, "return " + op + ";");
63-
append(lines, "}");
64-
return lines.toString();
60+
return "return " + op + ";";
6561
}
6662

6763
public static String genReadOp(Type type) {

src/main/java/com/jsoniter/CodegenImplObject.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,12 @@ public static String genObjectUsingSlice(Class clazz, String cacheKey, ClassDesc
3131
}
3232
}
3333
if (currentIdx > 63) {
34-
throw new JsonException("too many mandatory fields to track");
34+
throw new JsonException("too many required properties to track");
3535
}
3636
boolean hasMandatoryField = currentIdx > 0;
3737
long expectedTracker = Long.MAX_VALUE >> (63 - currentIdx);
3838
Map<Integer, Object> trieTree = buildTriTree(allBindings);
3939
StringBuilder lines = new StringBuilder();
40-
append(lines, "public static Object decode_(com.jsoniter.JsonIterator iter) {");
4140
// === if null, return null
4241
append(lines, "if (iter.readNull()) { com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; }");
4342
// === if input is empty object, return empty object
@@ -119,7 +118,6 @@ public static String genObjectUsingSlice(Class clazz, String cacheKey, ClassDesc
119118
}
120119
appendSetter(desc.setters, lines);
121120
append(lines, "return obj;");
122-
append(lines, "}");
123121
return lines.toString()
124122
.replace("{{clazz}}", clazz.getCanonicalName())
125123
.replace("{{newInst}}", genNewInstCode(clazz, desc.ctor));
@@ -134,7 +132,7 @@ private static void appendMissingMandatoryFields(StringBuilder lines, List<Bindi
134132
mask, binding.name));
135133
}
136134
}
137-
append(lines, "throw new com.jsoniter.JsonException(\"missing mandatory properties: \" + missingFields);");
135+
append(lines, "throw new com.jsoniter.JsonException(\"missing required properties: \" + missingFields);");
138136
}
139137

140138
private static void appendOnUnknownField(StringBuilder lines, ClassDescriptor desc) {
@@ -237,7 +235,6 @@ private static void addFieldDispatch(
237235
public static String genObjectUsingHash(Class clazz, String cacheKey, ClassDescriptor desc) {
238236
// TODO: when setter is single argument, decode like field
239237
StringBuilder lines = new StringBuilder();
240-
append(lines, "public static Object decode_(com.jsoniter.JsonIterator iter) {");
241238
// === if null, return null
242239
append(lines, "if (iter.readNull()) { com.jsoniter.CodegenAccess.resetExistingObject(iter); return null; }");
243240
// === if empty, return empty
@@ -315,7 +312,6 @@ public static String genObjectUsingHash(Class clazz, String cacheKey, ClassDescr
315312
}
316313
appendSetter(desc.setters, lines);
317314
append(lines, "return obj;");
318-
append(lines, "}");
319315
return lines.toString()
320316
.replace("{{clazz}}", clazz.getCanonicalName())
321317
.replace("{{newInst}}", genNewInstCode(clazz, desc.ctor));
@@ -348,12 +344,10 @@ private static void appendVarDef(StringBuilder lines, Binding parameter) {
348344

349345
public static String genObjectUsingSkip(Class clazz, ConstructorDescriptor ctor) {
350346
StringBuilder lines = new StringBuilder();
351-
append(lines, "public static Object decode_(com.jsoniter.JsonIterator iter) {");
352347
append(lines, "if (iter.readNull()) { return null; }");
353348
append(lines, "{{clazz}} obj = {{newInst}};");
354349
append(lines, "iter.skip();");
355350
append(lines, "return obj;");
356-
append(lines, "}");
357351
return lines.toString()
358352
.replace("{{clazz}}", clazz.getCanonicalName())
359353
.replace("{{newInst}}", genNewInstCode(clazz, ctor));
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.jsoniter;
2+
3+
import com.jsoniter.spi.CodegenConfig;
4+
5+
import java.io.File;
6+
7+
public class StaticCodeGenerator {
8+
public static void main(String[] args) throws Exception {
9+
String configClassName = args[0];
10+
Class<?> clazz = Class.forName(configClassName);
11+
CodegenConfig config = (CodegenConfig) clazz.newInstance();
12+
config.beforeCodegen();
13+
CodegenAccess.staticGenDecoders(config.getTypeLiterals());
14+
String configJavaFile = configClassName.replace('.', '/') + ".java";
15+
if (!new File(configJavaFile).exists()) {
16+
throw new JsonException("must execute static code generator in the java source code directory which contains: " + configJavaFile);
17+
}
18+
}
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.jsoniter.spi;
2+
3+
public interface CodegenConfig {
4+
/**
5+
* register decoder/encoder before codegen
6+
* register extension before codegen
7+
*/
8+
void beforeCodegen();
9+
10+
/**
11+
* what to codegen
12+
* @return generate encoder/decoder for the types
13+
*/
14+
TypeLiteral[] getTypeLiterals();
15+
}

0 commit comments

Comments
 (0)