diff --git a/codegen/pom.xml b/codegen/pom.xml index f29930983b..9046fd57c8 100644 --- a/codegen/pom.xml +++ b/codegen/pom.xml @@ -65,6 +65,13 @@ commons-io test + + com.sun + tools + 1.6.0 + system + ${java.home}/../lib/tools.jar + @@ -114,6 +121,9 @@ + + true + diff --git a/codegen/src/main/java/com/alibaba/fastjson2/internal/processor/JSONCompiledAnnotationProcessor2.java b/codegen/src/main/java/com/alibaba/fastjson2/internal/processor/JSONCompiledAnnotationProcessor2.java new file mode 100644 index 0000000000..5b3b2bff72 --- /dev/null +++ b/codegen/src/main/java/com/alibaba/fastjson2/internal/processor/JSONCompiledAnnotationProcessor2.java @@ -0,0 +1,550 @@ +package com.alibaba.fastjson2.internal.processor; + +import com.alibaba.fastjson2.annotation.JSONCompiled; +import com.alibaba.fastjson2.reader.ObjectReaderAdapter; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.tools.javac.api.JavacTrees; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.TypeTag; +import com.sun.tools.javac.processing.JavacProcessingEnvironment; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.tree.TreeTranslator; +import com.sun.tools.javac.util.*; +import com.sun.tools.javac.util.List; + +import javax.annotation.processing.*; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; + +import java.io.IOException; +import java.io.Writer; +import java.util.*; +import java.util.stream.Collectors; + +import static com.alibaba.fastjson2.internal.processor.CodeGenUtils.*; +import static com.alibaba.fastjson2.internal.processor.JavacTreeUtils.*; + +@SupportedSourceVersion(SourceVersion.RELEASE_8) +@SupportedAnnotationTypes({ + "com.alibaba.fastjson2.annotation.JSONCompiled", + "com.alibaba.fastjson2.annotation.JSONBuilder", + "com.alibaba.fastjson2.annotation.JSONCreator", + "com.alibaba.fastjson2.annotation.JSONField", + "com.alibaba.fastjson2.annotation.JSONType" +}) +public class JSONCompiledAnnotationProcessor2 + extends AbstractProcessor { + private Messager messager; + private JavacTrees javacTrees; + private Names names; + private Map count; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + this.messager = processingEnv.getMessager(); + this.javacTrees = JavacTrees.instance(processingEnv); + Context context = ((JavacProcessingEnvironment) processingEnv).getContext(); + this.names = Names.instance(context); + initialize(TreeMaker.instance(context), names, processingEnv.getElementUtils()); + count = new HashMap<>(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + Analysis analysis = new Analysis(processingEnv); + Set compiledJsons = roundEnv.getElementsAnnotatedWith(analysis.jsonCompiledElement); + if (!compiledJsons.isEmpty()) { + analysis.processAnnotation(analysis.compiledJsonType, compiledJsons); + } + + Map structs = analysis.analyze(); + Set elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(JSONCompiled.class); + elementsAnnotatedWith.stream().forEach(element -> { + StructInfo info = structs.get(element.toString()); + java.util.List fields = info.getReaderAttributes(); + int fieldsSize = fields.size(); + Class superClass = getSuperClass(fields.size()); + // generate imports + Set imports = genImports(element, false, new String[]{}); + String importsStr = imports.stream().filter(t -> t.getTag().equals(JCTree.Tag.IMPORT)).map(t2 -> t2.toString()).collect(Collectors.joining()); + + JCTree tree = javacTrees.getTree(element); + pos(tree.pos); + tree.accept(new TreeTranslator() { + @Override + public void visitClassDef(JCTree.JCClassDecl beanClassDecl) { + super.visitClassDef(beanClassDecl); + String classNamePath = beanClassDecl.sym.toString(); + if (element.toString().equals(classNamePath)) { + // initialization + JCTree.JCExpression beanType = qualIdent(classNamePath); + JCTree.JCNewClass beanNew = newClass(null, null, beanType, List.nil(), null); + JCTree.JCIdent objectType = ident("Object"); + JCTree.JCVariableDecl featuresVar = defVar(Flags.PARAMETER, "features", type(TypeTag.LONG)); + + // generate inner class + JCTree.JCClassDecl innerClass = genInnerClass(beanClassDecl, superClass); + + // generate fields if necessary + final boolean generatedFields = fieldsSize < 128; + if (generatedFields) { + innerClass.defs = innerClass.defs.prependList(genFields(fields, superClass)); + } + + // generate constructor + innerClass.defs = innerClass.defs.append(genConstructor(beanType, beanNew, fields, superClass, generatedFields)); + + // generate createInstance + innerClass.defs = innerClass.defs.append(genCreateInstance(objectType, beanNew)); + + // generate readObject + innerClass.defs = innerClass.defs.append(genReadObject(objectType, classNamePath, beanNew, fields, info, featuresVar, false)); + + beanClassDecl.defs = beanClassDecl.defs.append(innerClass); + + // generate source file +// genSource(classNamePath, info, importsStr, beanClassDecl); + } + } + }); + }); + return true; + } + + private Set genImports(Element element, boolean isStatic, String... fullQualifiedNames) { + if (fullQualifiedNames == null || fullQualifiedNames.length == 0) { + messager.printMessage(Diagnostic.Kind.NOTE, "fullQualifiedName must not be null or empty"); + } + messager.printMessage(Diagnostic.Kind.NOTE, String.format("add import of %s to %s", Arrays.toString(fullQualifiedNames), element.getSimpleName())); + Set imports = new TreeSet<>(Comparator.comparing(JCTree::toString)); + CompilationUnitTree compilationUnit = javacTrees.getPath(element).getCompilationUnit(); + if (compilationUnit instanceof JCTree.JCCompilationUnit) { + JCTree.JCCompilationUnit jcCompilationUnit = (JCTree.JCCompilationUnit) compilationUnit; + imports.addAll(jcCompilationUnit.defs); + } + return imports; + } + + private List genFields(java.util.List fields, Class superClass) { + ListBuffer stmts = new ListBuffer<>(); + int fieldsSize = fields.size(); + JCTree.JCExpression fieldReaderType = qualIdent("com.alibaba.fastjson2.reader.FieldReader"); + JCTree.JCExpression objectReaderType = qualIdent("com.alibaba.fastjson2.reader.ObjectReader"); + if (superClass == ObjectReaderAdapter.class) { + for (int i = 0; i < fieldsSize; i++) { + JCTree.JCVariableDecl var = defVar(Flags.PUBLIC, + fieldReader(i), + fieldReaderType); + stmts.append(var); + } + + for (int i = 0; i < fieldsSize; i++) { + JCTree.JCVariableDecl var = defVar(Flags.PUBLIC, + fieldObjectReader(i), + objectReaderType); + stmts.append(var); + } + } + for (int i = 0; i < fieldsSize; i++) { + AttributeInfo field = fields.get(i); + String fieldType = field.type.toString(); + if (fieldType.startsWith("java.util.List<")) { + stmts.append(defVar(Flags.PRIVATE, fieldItemObjectReader(i), objectReaderType)); + } + } + return stmts.toList(); + } + + /** + * @param beanClassDecl + * @param superClass + * @return + */ + private JCTree.JCClassDecl genInnerClass(JCTree.JCClassDecl beanClassDecl, Class superClass) { + JCTree.JCClassDecl innerClass = defClass(Flags.PRIVATE, + beanClassDecl.getSimpleName() + "_FASTJOSNReader", + null, + qualIdent(superClass.getName()), + null, + null); + return innerClass; + } + + private JCTree.JCMethodDecl genConstructor(JCTree.JCExpression beanType, JCTree.JCNewClass beanNew, java.util.List fields, Class superClass, boolean generatedFields) { + JCTree.JCLiteral nullLiteral = literal(TypeTag.BOT, null); + JCTree.JCLambda lambda = lambda(List.nil(), beanNew); + JCTree.JCExpression fieldReaderType = qualIdent("com.alibaba.fastjson2.reader.FieldReader"); + ListBuffer fieldReaders = new ListBuffer<>(); + JCTree.JCExpression objectReaderCreatorType = qualIdent("com.alibaba.fastjson2.reader.ObjectReaderCreator"); + JCTree.JCFieldAccess instanceField = field(objectReaderCreatorType, "INSTANCE"); + JCTree.JCExpression beanUtilsType = qualIdent("com.alibaba.fastjson2.util.BeanUtils"); + int fieldsSize = fields.size(); + for (int i = 0; i < fieldsSize; i++) { + AttributeInfo field = fields.get(i); + JCTree.JCMethodInvocation readerMethod = null; + if (field.setMethod != null) { + JCTree.JCMethodInvocation getSetterMethod = method(field(beanUtilsType, "getSetter"), List.of(field(beanType, names._class), literal(field.setMethod.getSimpleName().toString()))); + readerMethod = method(field(instanceField, "createFieldReader"), List.of(literal(field.name), getSetterMethod)); + } else if (field.field != null) { + JCTree.JCMethodInvocation getDeclaredFieldMethod = method(field(beanUtilsType, "getDeclaredField"), List.of(field(beanType, names._class), literal(field.name))); + readerMethod = method(field(instanceField, "createFieldReader"), List.of(literal(field.name), getDeclaredFieldMethod)); + } else { + messager.printMessage(Diagnostic.Kind.WARNING, "not implemented yet"); + } + if (readerMethod != null) { + fieldReaders.append(readerMethod); + } + } + JCTree.JCNewArray fieldReadersArray = newArray(fieldReaderType, null, List.from(fieldReaders)); + JCTree.JCMethodInvocation superMethod = method( + ident(names._super), + List.of(field(beanType, names._class), nullLiteral, nullLiteral, literal(TypeTag.LONG, 0), nullLiteral, lambda, nullLiteral, fieldReadersArray)); + ListBuffer stmts = new ListBuffer<>(); + stmts.append(exec(superMethod)); + // initialize fields if necessary + if (superClass == ObjectReaderAdapter.class && generatedFields) { + stmts.appendList(genInitFields(fieldsSize, fieldReadersArray)); + } + return defMethod(Flags.PUBLIC, names.init, type(TypeTag.VOID), List.nil(), List.nil(), List.nil(), block(0, stmts.toList()), null); + } + + private List genInitFields(int fieldsSize, JCTree.JCNewArray fieldReadersArray) { + ListBuffer stmts = new ListBuffer<>(); + for (int i = 0; i < fieldsSize; i++) { + JCTree.JCIdent fieldReaderIdent = ident(fieldReader(i)); + stmts.append(exec(assign(fieldReaderIdent, indexed(fieldReadersArray, literal(TypeTag.INT, i))))); + } + return stmts.toList(); + } + + private JCTree.JCMethodDecl genCreateInstance(JCTree.JCIdent objectType, JCTree.JCNewClass beanNew) { + JCTree.JCVariableDecl featuresVar = defVar(Flags.PARAMETER, "features", type(TypeTag.LONG)); + return defMethod(Flags.PUBLIC, "createInstance", objectType, List.nil(), List.of(featuresVar), List.nil(), block(0, List.of(defReturn(beanNew))), null); + } + + private JCTree.JCMethodDecl genReadObject(JCTree.JCIdent objectType, String classNamePath, JCTree.JCNewClass beanNew, java.util.List fields, StructInfo info, JCTree.JCVariableDecl featuresVar, boolean isJsonb) { + JCTree.JCExpression jsonReaderType = qualIdent("com.alibaba.fastjson2.JSONReader"); + JCTree.JCVariableDecl jsonReaderVar = defVar(Flags.PARAMETER, "jsonReader", jsonReaderType); + JCTree.JCExpression typeType = qualIdent("java.lang.reflect.Type"); + JCTree.JCVariableDecl typeVar = defVar(Flags.PARAMETER, "type", typeType); + JCTree.JCVariableDecl fieldNameVar = defVar(Flags.PARAMETER, "fieldName", objectType); + JCTree.JCReturn nullReturn = defReturn(literal(TypeTag.BOT, null)); + ListBuffer readObjectBody = new ListBuffer<>(); + + JCTree.JCIdent jsonReaderIdent = ident(jsonReaderVar.name); + JCTree.JCMethodInvocation nextIfNullMethod = method(field(jsonReaderIdent, "nextIfNull")); + readObjectBody.append(defIf(nextIfNullMethod, block(0, List.of(nullReturn)), null)); + readObjectBody.append(exec(method(field(jsonReaderIdent, "nextIfObjectStart")))); + JCTree.JCVariableDecl objectVar = defVar(Flags.PARAMETER, "object", qualIdent(classNamePath), beanNew); + readObjectBody.append(objectVar); + + JCTree.JCLabeledStatement forLabel = label("_for", null); + JCTree.JCForLoop forLoop = forLoop(null, null, null, null); + ListBuffer forBody = new ListBuffer<>(); + JCTree.JCIf nextIfObjectEndIf = defIf(method(field(jsonReaderIdent, "nextIfObjectEnd")), block(0, List.of(defBreak(forLabel))), null); + forBody.append(nextIfObjectEndIf); + JCTree.JCFieldAccess readFieldNameHashCode = field(jsonReaderIdent, "readFieldNameHashCode"); + JCTree.JCTypeCast hashCode64Cast = cast(type(TypeTag.LONG), method(readFieldNameHashCode)); + JCTree.JCVariableDecl hashCode64Var = defVar(Flags.PARAMETER, "hashCode64", type(TypeTag.LONG), hashCode64Cast); + forBody.append(hashCode64Var); + JCTree.JCExpression hashCode64 = ident(hashCode64Var.name); + forBody.append(defIf(binary(JCTree.Tag.EQ, literal(TypeTag.LONG, 0), hashCode64), block(0, List.of(defBreak(forLabel))), null)); + JCTree.JCIdent objectIdent = ident("object"); + + int fieldsSize = fields.size(); + if (fieldsSize <= 6) { + for (int i = 0; i < fieldsSize; ++i) { + AttributeInfo field = fields.get(i); + forBody.appendList(genReadFieldValue(jsonReaderIdent, field, i, info, field.nameHashCode, hashCode64, forLabel, objectIdent, true, isJsonb)); + } + } else { + Map> map = new TreeMap(); + Map mapping = new TreeMap(); + Map mappingIndex = new TreeMap(); + + for (int i = 0; i < fieldsSize; i++) { + AttributeInfo field = fields.get(i); + long fieldNameHash = field.nameHashCode; + int hashCode32 = (int) (fieldNameHash ^ (fieldNameHash >>> 32)); + java.util.List hashCode64List = map.computeIfAbsent(hashCode32, k -> new ArrayList<>()); + hashCode64List.add(fieldNameHash); + mapping.put(fieldNameHash, field); + mappingIndex.put(fieldNameHash, i); + } + + int[] hashCode32Keys = new int[map.size()]; + { + int off = 0; + for (Integer key : map.keySet()) { + hashCode32Keys[off++] = key; + } + } + Arrays.sort(hashCode32Keys); + + JCTree.JCVariableDecl hashCode32Var = getHashCode32Var(hashCode64); + forBody.append(hashCode32Var); + JCTree.JCExpression hashCode32 = ident(hashCode32Var.name); + + ListBuffer cases = new ListBuffer<>(); + for (int i = 0; i < fieldsSize; i++) { + JCTree.JCLabeledStatement iLabel = label(i + "", null); + java.util.List hashCode64Array = map.get(hashCode32Keys[i]); + List stmts = List.nil(); + Long fieldNameHash = null; + if (hashCode64Array.size() == 1 && hashCode64Array.get(0) == hashCode32Keys[i]) { + fieldNameHash = hashCode64Array.get(0); + int index = mappingIndex.get(fieldNameHash); + AttributeInfo field = mapping.get(fieldNameHash); + stmts = stmts.appendList(genReadFieldValue(jsonReaderIdent, field, index, info, field.nameHashCode, hashCode64, forLabel, objectIdent, false, isJsonb)); + stmts.append(defContinue(forLabel)); + } else { + for (int j = 0; j < hashCode64Array.size(); ++j) { + fieldNameHash = hashCode64Array.get(j); + int index = mappingIndex.get(fieldNameHash); + AttributeInfo field = mapping.get(fieldNameHash); + List stmtsIf = genReadFieldValue(jsonReaderIdent, field, index, info, field.nameHashCode, hashCode64, forLabel, objectIdent, false, isJsonb); + stmts = stmts.append(defIf(binary(JCTree.Tag.EQ, hashCode64, literal(TypeTag.LONG, fieldNameHash)), block(0, stmtsIf), null)); + stmts.append(defContinue(forLabel)); + } + stmts.append(defBreak(iLabel)); + } + cases.append(defCase(getHashCode32Var(literal(TypeTag.LONG, fieldNameHash)).getInitializer(), stmts)); + } + forBody.append(defSwitch(hashCode32, cases.toList())); + } + + JCTree.JCFieldAccess processExtraField = field(ident(names._this), "processExtra"); + forBody.append(exec(method(processExtraField, List.of(jsonReaderIdent, objectIdent)))); + + forLoop.body = block(0, forBody.toList()); + forLabel.body = forLoop; + + readObjectBody.append(forLabel); + readObjectBody.append(defReturn(objectIdent)); + return defMethod(Flags.PUBLIC, "readObject", objectType, List.nil(), List.of(jsonReaderVar, typeVar, fieldNameVar, featuresVar), List.nil(), block(0, readObjectBody.toList()), null); + } + + private JCTree.JCVariableDecl getHashCode32Var(JCTree.JCExpression hashCode64) { + JCTree.JCBinary usrBinary = binary(JCTree.Tag.USR, hashCode64, literal(TypeTag.INT, 32)); + JCTree.JCPrimitiveTypeTree intType = type(TypeTag.INT); + JCTree.JCTypeCast hashCode32Cast = cast(intType, binary(JCTree.Tag.BITXOR, hashCode64, parens(usrBinary))); + return defVar(Flags.PARAMETER, "hashCode32", intType, hashCode32Cast); + } + + private List genReadFieldValue( + JCTree.JCIdent jsonReaderIdent, + AttributeInfo field, + int i, + StructInfo info, + long fieldNameHash, + JCTree.JCExpression readFieldNameHashCodeMethod, + JCTree.JCLabeledStatement forLabel, + JCTree.JCIdent objectIdent, + boolean isIf, + boolean isJsonb) { + ListBuffer stmts = new ListBuffer<>(); + String type = field.type.toString(); + JCTree.JCLiteral nullLiteral = literal(TypeTag.BOT, null); + JCTree.JCExpression valueExpr; + switch (type) { + case "boolean": + valueExpr = method(field(jsonReaderIdent, "readBoolValue")); + break; + case "byte": + valueExpr = method(field(jsonReaderIdent, "readInt32Value")); + break; + case "short": + JCTree.JCFieldAccess shortField = field(ident("short"), names._class); + valueExpr = method(field(jsonReaderIdent, "readInt32Value"), List.of(shortField)); + break; + case "int": + valueExpr = method(field(jsonReaderIdent, "readInt32Value")); + break; + case "long": + valueExpr = method(field(jsonReaderIdent, "readInt64Value")); + break; + case "float": + valueExpr = method(field(jsonReaderIdent, "readFloatValue")); + break; + case "double": + valueExpr = method(field(jsonReaderIdent, "readDoubleValue")); + break; + case "char": + valueExpr = method(field(jsonReaderIdent, "readCharValue")); + break; + case "int[]": + valueExpr = method(field(jsonReaderIdent, "readInt32ValueArray")); + break; + case "long[]": + valueExpr = method(field(jsonReaderIdent, "readInt64ValueArray")); + break; + case "java.lang.String": + valueExpr = method(field(jsonReaderIdent, "readString")); + break; + case "java.lang.Integer": + valueExpr = method(field(jsonReaderIdent, "readInt32")); + break; + case "java.lang.Long": + valueExpr = method(field(jsonReaderIdent, "readInt64")); + break; + case "java.lang.Float": + valueExpr = method(field(jsonReaderIdent, "readFloat")); + break; + case "java.lang.readDouble": + valueExpr = method(field(jsonReaderIdent, "readDouble")); + break; + case "java.math.BigDecimal": + valueExpr = method(field(jsonReaderIdent, "readBigDecimal")); + break; + case "java.math.BigInteger": + valueExpr = method(field(jsonReaderIdent, "readBigInteger")); + break; + case "java.util.UUID": + valueExpr = method(field(jsonReaderIdent, "readUUID")); + break; + case "java.lang.String[]": + valueExpr = method(field(jsonReaderIdent, "readStringArray")); + break; + case "java.time.LocalDate": + valueExpr = method(field(jsonReaderIdent, "readLocalDate")); + break; + case "java.time.OffsetDateTime": + valueExpr = method(field(jsonReaderIdent, "readOffsetDateTime")); + break; + default: + JCTree.JCFieldAccess fieldReaderField = field(ident(names._this), fieldReader(i)); + if (info.referenceDetect) { + ListBuffer stmts1 = new ListBuffer<>(); + JCTree.JCMethodInvocation readReferenceMethod = method(field(jsonReaderIdent, "readReference")); + JCTree.JCVariableDecl refVar = defVar(Flags.PARAMETER, "ref", ident("String"), readReferenceMethod); + stmts1.append(refVar); + JCTree.JCMethodInvocation addResolveTaskMethod = method(field(fieldReaderField, "addResolveTask"), List.of(jsonReaderIdent, objectIdent, ident(refVar.name))); + stmts1.append(exec(addResolveTaskMethod)); + stmts1.append(defContinue(forLabel)); + stmts.append(defIf(method(field(jsonReaderIdent, "isReference")), block(0, stmts1.toList()), null)); + } + + JCTree.JCExpression fieldValueType = getFieldValueType(type); + JCTree.JCVariableDecl fieldValueVar = defVar(Flags.PARAMETER, field.name, fieldValueType); + stmts.append(fieldValueVar); + + boolean list = type.startsWith("java.util.List<"); + if (list) { + String itemType = type.substring(15, type.length() - 1); + boolean itemTypeIsClass = itemType.indexOf('<') == -1; + if (itemTypeIsClass) { + JCTree.JCMethodInvocation nextIfNullMethod = method(field(jsonReaderIdent, "nextIfNull")); + stmts.append(defIf(nextIfNullMethod, + block(0, List.of(exec(assign(ident(fieldValueVar.name), nullLiteral)))), + block(0, List.of(exec(assign(ident(fieldValueVar.name), newClass(null, null, qualIdent("java.util.ArrayList"), List.nil(), null))))))); + + boolean stringItemClass = "java.lang.String".equals(itemType); + JCTree.JCIdent itemReaderIdent = ident(fieldItemObjectReader(i)); + if (!stringItemClass) { + JCTree.JCFieldAccess getItemObjectReaderField = field(fieldReaderField, "getItemObjectReader"); + JCTree.JCExpressionStatement getItemObjectReaderExec = exec(assign(itemReaderIdent, method(getItemObjectReaderField, List.of(jsonReaderIdent)))); + stmts.append(defIf(binary(JCTree.Tag.EQ, itemReaderIdent, nullLiteral), block(0, List.of(getItemObjectReaderExec)), null)); + } + + JCTree.JCMethodInvocation nextIfArrayStartMethod = method(field(jsonReaderIdent, "nextIfArrayStart")); + JCTree.JCMethodInvocation nextIfArrayEndMethod = method(field(jsonReaderIdent, "nextIfArrayEnd")); + ListBuffer whileBody = new ListBuffer<>(); + JCTree.JCExpression item; + if (stringItemClass) { + item = method(field(jsonReaderIdent, "readString")); + } else { + item = cast(qualIdent(itemType), method(field(itemReaderIdent, "readObject"), List.of(jsonReaderIdent, nullLiteral, nullLiteral, literal(TypeTag.LONG, 0)))); + } + whileBody.append(exec(method(field(ident(fieldValueVar.name), "add"), List.of(item)))); + JCTree.JCWhileLoop arrayWhile = whileLoop(unary(JCTree.Tag.NOT, nextIfArrayEndMethod), block(0, whileBody.toList())); + stmts.append(defIf(nextIfArrayStartMethod, block(0, List.of(arrayWhile)), null)); + valueExpr = ident(fieldValueVar.name); + break; + } + } + + JCTree.JCIdent objectReaderIdent = ident(fieldObjectReader(i)); + JCTree.JCMethodInvocation getObjectReaderMethod = method(field(fieldReaderField, "getObjectReader"), List.of(jsonReaderIdent)); + JCTree.JCAssign objectReaderAssign = assign(objectReaderIdent, getObjectReaderMethod); + stmts.append(defIf(binary(JCTree.Tag.EQ, objectReaderIdent, nullLiteral), block(0, List.of(exec(objectReaderAssign))), null)); + JCTree.JCMethodInvocation objectMethod = method(field(field(ident(names._this), fieldObjectReader(i)), isJsonb ? "readJSONBObject" : "readObject"), List.of(jsonReaderIdent, field(fieldReaderField, "fieldType"), literal(field.name), literal(TypeTag.LONG, 0))); + JCTree.JCTypeCast objectCast = cast(fieldValueType, objectMethod); + stmts.append(exec(assign(ident(fieldValueVar.name), objectCast))); + + valueExpr = ident(fieldValueVar.name); + break; + } + if (field.setMethod != null) { + stmts.append(exec(method(field(objectIdent, field.setMethod.getSimpleName().toString()), List.of(valueExpr)))); + } else if (field.field != null) { + stmts.append(exec(assign(field(objectIdent, field.field.getSimpleName().toString()), valueExpr))); + } else { + messager.printMessage(Diagnostic.Kind.WARNING, "not implemented yet"); + } + stmts.append(defContinue(forLabel)); + + if (isIf) { + return List.of(defIf(binary(JCTree.Tag.EQ, literal(TypeTag.LONG, fieldNameHash), readFieldNameHashCodeMethod), block(0, stmts.toList()), null)); + } else { + return stmts.toList(); + } + } + + private void genSource(String classNamePath, StructInfo info, String importsStr, JCTree.JCClassDecl beanClassDecl) { + Integer cnt = count.getOrDefault(classNamePath, -1); + String newClassNamePath = classNamePath + "_Source" + (cnt + 1); + count.put(classNamePath, cnt + 1); + try { + JavaFileObject converterFile = processingEnv.getFiler().createSourceFile(newClassNamePath, info.element); + try (Writer writer = converterFile.openWriter()) { + int idx = newClassNamePath.lastIndexOf("."); + String oldPkgPath = newClassNamePath.substring(0, idx); + String newPkgPath = oldPkgPath.toLowerCase(); + writer.write("package " + newPkgPath + ";"); + writer.write(System.lineSeparator()); + writer.write(System.lineSeparator()); + writer.write(importsStr); + java.util.List annos = beanClassDecl.mods.annotations.stream().map(a -> a.toString()).collect(Collectors.toList()); + String str = beanClassDecl.toString(); + for (String s : annos) { + str = str.replace(s, ""); + } + while (str.startsWith(System.lineSeparator())) { + str = str.replaceFirst(System.lineSeparator(), ""); + } + writer.write(System.lineSeparator()); + String newClassName = newClassNamePath.substring(idx + 1); + String oldClasName = beanClassDecl.getSimpleName().toString(); + str = str.replaceAll(" static class " + oldClasName, " class " + newClassName); + str = str.replaceAll(" " + oldClasName + " ", " " + newClassName + " "); + str = str.replaceAll("\\." + oldClasName + " ", "." + newClassName + " "); + str = str.replaceAll("\\." + oldClasName + "\\(", "." + newClassName + "("); + str = str.replaceAll("\\." + oldClasName + "\\.", "." + newClassName + "."); + str = str.replaceAll(" " + oldClasName + "\\(", " " + newClassName + "("); + str = str.replaceAll(oldPkgPath, newPkgPath); + writer.write(str); + } catch (IOException e) { + messager.printMessage(Diagnostic.Kind.ERROR, "Failed saving compiled json serialization file " + newClassNamePath); + } + } catch (IOException e) { + messager.printMessage(Diagnostic.Kind.ERROR, "Failed creating compiled json serialization file " + newClassNamePath); + } + } + + private JCTree.JCExpression getFieldValueType(String type) { + int open = type.indexOf("<"); + int close = type.indexOf(">"); + if (open == -1 && close == -1) { + return qualIdent(type); + } else { + String collection = type.substring(0, open); + String generic = type.substring(open + 1, close); + return typeApply(qualIdent(collection), List.of(qualIdent(generic))); + } + } +} diff --git a/codegen/src/main/java/com/alibaba/fastjson2/internal/processor/JavacTreeUtils.java b/codegen/src/main/java/com/alibaba/fastjson2/internal/processor/JavacTreeUtils.java new file mode 100644 index 0000000000..ae8d5778af --- /dev/null +++ b/codegen/src/main/java/com/alibaba/fastjson2/internal/processor/JavacTreeUtils.java @@ -0,0 +1,237 @@ +package com.alibaba.fastjson2.internal.processor; + +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.TypeTag; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Name; +import com.sun.tools.javac.util.Names; + +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; + +final class JavacTreeUtils { + private static TreeMaker treeMaker; + private static Names names; + private static Elements elements; + + private JavacTreeUtils() { + throw new UnsupportedOperationException("this class can not be instantiated"); + } + + static void initialize(TreeMaker _treeMaker, Names _names, Elements _elements) { + treeMaker = _treeMaker; + names = _names; + elements = _elements; + } + + static Name name(String name) { + return names.fromString(name); + } + + static JCTree.JCIdent ident(String name) { + return treeMaker.Ident(name(name)); + } + + static JCTree.JCIdent ident(Name name) { + return treeMaker.Ident(name); + } + + static JCTree.JCExpression qualIdent(String name) { + TypeElement typeElement = elements.getTypeElement(name); + if (typeElement != null) { + return treeMaker.QualIdent((Symbol) typeElement); + } else { + return ident(name(name)); + } + } + + static JCTree.JCVariableDecl defVar(long flag, String identName, JCTree.JCExpression identType) { + return defVar(flag, identName, identType, null); + } + + static JCTree.JCVariableDecl defVar(long flag, String identName, JCTree.JCExpression identType, JCTree.JCExpression init) { + return treeMaker.VarDef(modifiers(flag), name(identName), identType, init); + } + + static JCTree.JCMethodDecl defMethod(int flag, String name, JCTree.JCExpression rtnType, List typeArgs, List params, List recvArgs, JCTree.JCBlock block, JCTree.JCExpression defaultValue) { + return defMethod(flag, name(name), rtnType, typeArgs, params, recvArgs, block, defaultValue); + } + + static JCTree.JCMethodDecl defMethod(int flag, Name name, JCTree.JCExpression rtnType, List typeArgs, List params, List recvArgs, JCTree.JCBlock block, JCTree.JCExpression defaultValue) { + return treeMaker.MethodDef(modifiers(flag), name, rtnType, typeArgs, params, recvArgs, block, defaultValue); + } + + static JCTree.JCMethodInvocation method(JCTree.JCExpression method) { + return method(null, method, null); + } + + static JCTree.JCMethodInvocation method(JCTree.JCExpression method, List args) { + return method(null, method, args); + } + + static JCTree.JCMethodInvocation method(List typeArgs, JCTree.JCExpression method, List args) { + if (typeArgs == null) { + typeArgs = List.nil(); + } + if (args == null) { + args = List.nil(); + } + return treeMaker.Apply(typeArgs, method, args); + } + + static JCTree.JCFieldAccess field(JCTree.JCExpression expr, String name) { + return treeMaker.Select(expr, name(name)); + } + + static JCTree.JCFieldAccess field(JCTree.JCExpression expr, Name name) { + return treeMaker.Select(expr, name); + } + + static JCTree.JCModifiers modifiers(long flag) { + return treeMaker.Modifiers(flag); + } + + static JCTree.JCExpressionStatement exec(JCTree.JCExpression expr) { + return treeMaker.Exec(expr); + } + + static JCTree.JCAssign assign(JCTree.JCExpression expr1, JCTree.JCExpression expr2) { + return treeMaker.Assign(expr1, expr2); + } + + static JCTree.JCIf defIf(JCTree.JCExpression cond, JCTree.JCStatement thenStmt, JCTree.JCStatement elseStmt) { + return treeMaker.If(cond, thenStmt, elseStmt); + } + + static JCTree.JCBinary binary(JCTree.Tag tag, JCTree.JCExpression expr1, JCTree.JCExpression expr2) { + return treeMaker.Binary(tag, expr1, expr2); + } + + static JCTree.JCUnary unary(JCTree.Tag tag, JCTree.JCExpression expr) { + return treeMaker.Unary(tag, expr); + } + + static JCTree.JCBlock block(long pos, List stmts) { + return treeMaker.Block(pos, stmts); + } + + static JCTree.JCLiteral literal(TypeTag tag, Object object) { + return treeMaker.Literal(tag, object); + } + + static JCTree.JCLiteral literal(Object object) { + return treeMaker.Literal(object); + } + + static JCTree.JCTypeCast cast(JCTree type, JCTree.JCExpression expr) { + return treeMaker.TypeCast(type, expr); + } + + static JCTree.JCNewClass newClass(JCTree.JCExpression encl, List typeArgs, JCTree.JCExpression clazz, List args, JCTree.JCClassDecl def) { + return treeMaker.NewClass(encl, typeArgs, clazz, args, def); + } + + static JCTree.JCPrimitiveTypeTree type(TypeTag tag) { + return treeMaker.TypeIdent(tag); + } + + static JCTree.JCLabeledStatement label(String name, JCTree.JCStatement stmt) { + return treeMaker.Labelled(name(name), stmt); + } + + static JCTree.JCBreak defBreak(JCTree.JCLabeledStatement labeledStatement) { + return defBreak(labeledStatement.label); + } + + static JCTree.JCBreak defBreak(Name name) { + return treeMaker.Break(name); + } + + static JCTree.JCContinue defContinue(JCTree.JCLabeledStatement labeledStatement) { + return defContinue(labeledStatement.label); + } + + static JCTree.JCContinue defContinue(Name name) { + return treeMaker.Continue(name); + } + + static JCTree.JCForLoop forLoop(List initStmts, JCTree.JCExpression condExpr, List stepExprs, JCTree.JCStatement bodyStmt) { + if (initStmts == null) { + initStmts = List.nil(); + } + if (stepExprs == null) { + stepExprs = List.nil(); + } + return treeMaker.ForLoop(initStmts, condExpr, stepExprs, bodyStmt); + } + + static JCTree.JCCase defCase(JCTree.JCExpression matchExpr, List matchStmts) { + if (matchStmts == null) { + matchStmts = List.nil(); + } + return treeMaker.Case(matchExpr, matchStmts); + } + + static JCTree.JCSwitch defSwitch(JCTree.JCExpression selectorExpr, List cases) { + if (cases == null) { + cases = List.nil(); + } + return treeMaker.Switch(selectorExpr, cases); + } + + static JCTree.JCReturn defReturn(JCTree.JCExpression expr) { + return treeMaker.Return(expr); + } + + static JCTree.JCParens parens(JCTree.JCExpression expr) { + return treeMaker.Parens(expr); + } + + static JCTree.JCWhileLoop whileLoop(JCTree.JCExpression condExpr, JCTree.JCStatement bodyStmt) { + return treeMaker.WhileLoop(condExpr, bodyStmt); + } + + static JCTree.JCTypeApply typeApply(JCTree.JCExpression clazz, List args) { + if (args == null) { + args = List.nil(); + } + return treeMaker.TypeApply(clazz, args); + } + + static JCTree.JCArrayAccess indexed(JCTree.JCExpression indexedExpr, JCTree.JCExpression indexExpr) { + return treeMaker.Indexed(indexedExpr, indexExpr); + } + + static JCTree.JCLambda lambda(List args, JCTree body) { + if (args == null) { + args = List.nil(); + } + return treeMaker.Lambda(args, body); + } + + static JCTree.JCNewArray newArray(JCTree.JCExpression elemTypeExpr, List dimsExprs, List elemDataExprs) { + if (dimsExprs == null) { + dimsExprs = List.nil(); + } + return treeMaker.NewArray(elemTypeExpr, dimsExprs, elemDataExprs); + } + + static JCTree.JCClassDecl defClass(int flag, String name, List typeArgs, JCTree.JCExpression extendExpr, List implementExprs, List defs) { + if (typeArgs == null) { + typeArgs = List.nil(); + } + if (implementExprs == null) { + implementExprs = List.nil(); + } + if (defs == null) { + defs = List.nil(); + } + return treeMaker.ClassDef(modifiers(flag), name(name), typeArgs, extendExpr, implementExprs, defs); + } + + static void pos(int pos) { + treeMaker.pos = pos; + } +} diff --git a/codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor index 345ae45d48..03be27ebe4 100644 --- a/codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ b/codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -1 +1,2 @@ com.alibaba.fastjson2.internal.processor.JSONCompiledAnnotationProcessor +com.alibaba.fastjson2.internal.processor.JSONCompiledAnnotationProcessor2