All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.fastjson2.internal.processor.JSONCompiledAnnotationProcessor Maven / Gradle / Ivy

package com.alibaba.fastjson2.internal.processor;

import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.annotation.JSONCompiled;
import com.alibaba.fastjson2.internal.codegen.Label;
import com.alibaba.fastjson2.internal.graalmeta.ReflectionMetadata;
import com.alibaba.fastjson2.reader.FieldReader;
import com.alibaba.fastjson2.reader.ObjectReader;
import com.alibaba.fastjson2.reader.ObjectReaderAdapter;
import com.alibaba.fastjson2.reader.ObjectReaders;
import com.alibaba.fastjson2.writer.FieldWriter;
import com.alibaba.fastjson2.writer.ObjectWriterAdapter;
import com.alibaba.fastjson2.writer.ObjectWriters;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.processing.JavacFiler;
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.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Names;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

import java.io.*;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

import static com.alibaba.fastjson2.JSONWriter.Feature.*;
import static com.alibaba.fastjson2.internal.processor.CodeGenUtils.*;
import static com.alibaba.fastjson2.internal.processor.JavacTreeUtils.*;
import static com.alibaba.fastjson2.internal.processor.JavacTreeUtils.qualIdent;
import static com.alibaba.fastjson2.util.JDKUtils.*;

@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 JSONCompiledAnnotationProcessor
        extends AbstractProcessor {
    private Messager messager;
    private JavacTrees javacTrees;
    private Names names;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        ProcessingEnvironment unwrappedProcessingEnv = unwrapProcessingEnv(processingEnv);
        super.init(unwrappedProcessingEnv);
        this.messager = unwrappedProcessingEnv.getMessager();
        this.javacTrees = JavacTrees.instance(unwrappedProcessingEnv);
        Context context = ((JavacProcessingEnvironment) unwrappedProcessingEnv).getContext();
        this.names = Names.instance(context);
        initialize(TreeMaker.instance(context), names, unwrappedProcessingEnv.getElementUtils());
    }

    @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.jsonCompiledDeclaredType, compiledJsons);
        }

        Map structs = analysis.analyze();
        Set elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(JSONCompiled.class);
        Set reflects = new HashSet<>(elementsAnnotatedWith.size() * 2);
        elementsAnnotatedWith.forEach(element -> {
            StructInfo structInfo = structs.get(element.toString());
            java.util.List attributeInfos = structInfo.getReaderAttributes();
            int fieldsSize = attributeInfos.size();
            Class readSuperClass = getReadSuperClass(fieldsSize);
            Class writeSuperClass = getWriteSuperClass(fieldsSize);

            JCTree tree = javacTrees.getTree(element);
            pos(tree.pos);
            tree.accept(new TreeTranslator() {
                @Override
                public void visitClassDef(JCTree.JCClassDecl beanClassDecl) {
                    super.visitClassDef(beanClassDecl);
                    String beanClassFQN = beanClassDecl.sym.toString();
                    if (element.toString().equals(beanClassFQN)) {
                        // initialization
                        String innerReadClassFQN = findConverterName(structInfo, "_FASTJSONReader");
                        String innerReadClassName = innerReadClassFQN.substring(innerReadClassFQN.lastIndexOf('.') + 1);
                        JCTree.JCExpression beanType = null;
                        if (beanClassFQN.contains(".")) {
                            if (element instanceof Symbol.ClassSymbol) {
                                Symbol.ClassSymbol classSymbol = (Symbol.ClassSymbol) element;
                                String owner = classSymbol.owner.toString();
                                int dotIdx = owner.indexOf(".");
                                beanType = field(dotIdx == -1 ? ident(owner) : qualIdent(owner),
                                        beanClassFQN.substring(beanClassFQN.lastIndexOf(".") + 1));
                            }
                        } else {
                            int dotIdx = beanClassFQN.indexOf(".");
                            beanType = dotIdx == -1 ? ident(beanClassFQN) : qualIdent(beanClassFQN);
                        }
                        JCTree.JCNewClass beanNew = newClass(null, null, beanType, null, null);
                        JCTree.JCIdent objectType = ident("Object");

                        // add deserializer class
                        addInnerClassIfAbsent(beanClassDecl, beanClassFQN, innerReadClassName, "deserializer");

                        // generate inner read class
                        JCTree.JCClassDecl innerReadClass = genInnerClass(innerReadClassName, readSuperClass);

                        // generate fields if necessary
                        final boolean generatedFields = fieldsSize < 128;
                        if (generatedFields) {
                            innerReadClass.defs = innerReadClass.defs.prependList(genFields(attributeInfos, readSuperClass));
                        }

                        // generate constructor
                        innerReadClass.defs = innerReadClass.defs.append(genReadConstructor(beanType, beanNew, attributeInfos, readSuperClass, generatedFields));

                        // generate createInstance
                        innerReadClass.defs = innerReadClass.defs.append(genCreateInstance(objectType, beanNew));

                        // generate readObject
                        innerReadClass.defs = innerReadClass.defs.append(
                                genReadObject(objectType, beanType, beanNew, attributeInfos, structInfo, false));

                        // generate readJSONBObject
                        innerReadClass.defs = innerReadClass.defs.append(
                                genReadObject(objectType, beanType, beanNew, attributeInfos, structInfo, true));

                        // link with inner class
                        beanClassDecl.defs = beanClassDecl.defs.append(innerReadClass);

                        // initialization
                        String innerWriteClassFQN = findConverterName(structInfo, "_FASTJSONWriter");
                        String innerWriteClassName = innerWriteClassFQN.substring(innerWriteClassFQN.lastIndexOf('.') + 1);

                        // add serializer class
                        addInnerClassIfAbsent(beanClassDecl, beanClassFQN, innerWriteClassName, "serializer");

                        // generate inner read class
                        JCTree.JCClassDecl innerWriteClass = genInnerClass(innerWriteClassName, writeSuperClass);

                        // generate fields if necessary
                        if (generatedFields) {
                            innerWriteClass.defs = innerWriteClass.defs.prependList(genFields(attributeInfos, writeSuperClass));
                        }

                        // generate constructor
                        innerWriteClass.defs = innerWriteClass.defs.append(genWriteConstructor(beanType, beanNew, attributeInfos, writeSuperClass, generatedFields));

                        // generate write
                        innerWriteClass.defs = innerWriteClass.defs.append(genWrite(objectType, beanType, beanNew, attributeInfos, structInfo, false));

                        // link with inner class
                        beanClassDecl.defs = beanClassDecl.defs.append(innerWriteClass);

                        // generate source if debug is true
                        if (isDebug(beanClassDecl)) {
                            messager.printMessage(Diagnostic.Kind.WARNING, "Whoops! You have changed the debug of " +
                                    "JSONCompiled from false to true for " + structInfo.binaryName + ", which means the " +
                                    "additional source file being generated. It's usually not recommended to enable debug " +
                                    "until you are in developer mode");
                            genSource(structInfo, javacTrees.getPath(element));
                        }

                        // generate reflect-config.json only for graal
                        if (GRAAL) {
                            ReflectionMetadata readMeta = new ReflectionMetadata();
                            readMeta.setName(innerReadClassFQN);
                            readMeta.setAllPublicConstructors(true);
                            reflects.add(readMeta);
                            ReflectionMetadata writeMeta = new ReflectionMetadata();
                            writeMeta.setName(innerWriteClassFQN);
                            writeMeta.setAllPublicConstructors(true);
                            reflects.add(writeMeta);
                        }
                    }
                }
            });
        });
        if (!reflects.isEmpty()) {
            genReflect(reflects);
        }
        return true;
    }

    private void addInnerClassIfAbsent(JCTree.JCClassDecl beanClassDecl,
                                       String beanClassFQN,
                                       String innerClassName,
                                       String key) {
        JCTree.JCExpression jsonTypeIdent = qualIdent("com.alibaba.fastjson2.annotation.JSONType");
        List annotations = beanClassDecl.mods.annotations;
        Optional jsonTypeAnnoOpt = annotations.stream()
                .filter(a -> a.getAnnotationType().type.tsym.toString().equals(jsonTypeIdent.type.tsym.toString()))
                .findAny();
        String beanClassName = beanClassFQN.substring(beanClassFQN.lastIndexOf('.') + 1);
        JCTree.JCIdent lhs = ident(key);
        JCTree.JCFieldAccess rhs = field(field(ident(beanClassName), innerClassName), names._class);
        if (jsonTypeAnnoOpt.isPresent()) {
            JCTree.JCAnnotation jsonTypeAnno = jsonTypeAnnoOpt.get();
            Optional jsonTypeAsgOpt = jsonTypeAnno.args.stream()
                    .map(a -> (JCTree.JCAssign) a)
                    .filter(a2 -> key.equals(a2.lhs.toString()))
                    .findAny();
            if (jsonTypeAsgOpt.isPresent()) {
                JCTree.JCAssign deserializerAssign = jsonTypeAsgOpt.get();
                if ("Void.class".equals(deserializerAssign.rhs.toString())) {
                    deserializerAssign.rhs = rhs;
                }
            } else {
                jsonTypeAnno.args = jsonTypeAnno.args.prepend(assign(lhs, rhs));
            }
        } else {
            JCTree.JCAnnotation jsonTypeAnno = annotation(jsonTypeIdent, List.of(assign(lhs, rhs)));
            beanClassDecl.mods.annotations = annotations.prepend(jsonTypeAnno);
        }
    }

    private JCTree.JCClassDecl genInnerClass(String className, Class superClass) {
        if (ObjectReaderAdapter.class.isAssignableFrom(superClass)) {
            return defClass(Flags.PUBLIC | Flags.STATIC | Flags.FINAL,
                    className,
                    null,
                    qualIdent(superClass.getName()),
                    null,
                    null);
        } else if (ObjectWriterAdapter.class.isAssignableFrom(superClass)) {
            return defClass(Flags.PUBLIC | Flags.STATIC | Flags.FINAL,
                    className,
                    null,
                    qualIdent(superClass.getName()),
                    List.of(qualIdent("com.alibaba.fastjson2.writer.ObjectWriter")),
                    null);
        } else {
            return null;
        }
    }

    private List genFields(java.util.List attributeInfos, Class superClass) {
        ListBuffer stmts = new ListBuffer<>();
        int fieldsSize = attributeInfos.size();
        if (ObjectReaderAdapter.class.isAssignableFrom(superClass)) {
            JCTree.JCExpression fieldReaderType = qualIdent(FieldReader.class.getName());
            JCTree.JCExpression objectReaderType = qualIdent(ObjectReader.class.getName());
            if (superClass == ObjectReaderAdapter.class) {
                for (int i = 0; i < fieldsSize; ++i) {
                    stmts.append(defVar(Flags.PRIVATE, fieldReader(i), fieldReaderType));
                }

                for (int i = 0; i < fieldsSize; i++) {
                    stmts.append(defVar(Flags.PRIVATE, fieldObjectReader(i), objectReaderType));
                }
            }

            for (int i = 0; i < fieldsSize; ++i) {
                AttributeInfo field = attributeInfos.get(i);
                String fieldType = field.type.toString();
                if (fieldType.startsWith("java.util.List<") || fieldType.startsWith("java.util.Map fields,
            Class superClass,
            boolean generatedFields) {
        JCTree.JCMemberReference lambda = constructorRef(beanType);
        JCTree.JCExpression fieldReaderType = qualIdent(FieldReader.class.getName());
        ListBuffer fieldReaders = new ListBuffer<>();
        JCTree.JCExpression objectReadersType = qualIdent(ObjectReaders.class.getName());
        int fieldsSize = fields.size();
        for (int i = 0; i < fieldsSize; ++i) {
            JCTree.JCMethodInvocation readerMethod = genFieldReader(beanType, fields.get(i), objectReadersType);
            if (readerMethod != null) {
                fieldReaders.append(readerMethod);
            }
        }
        JCTree.JCNewArray fieldReadersArray = newArray(fieldReaderType, null, List.from(fieldReaders));
        long features = 0L;
        JCTree.JCMethodInvocation superMethod = method(
                ident(names._super),
                List.of(
                        field(beanType, names._class),
                        defNull(),
                        defNull(),
                        literal(features),
                        lambda,
                        defNull(),
                        fieldReadersArray
                )
        );
        ListBuffer stmts = new ListBuffer<>();
        stmts.append(exec(superMethod));
        // initialize fields if necessary
        if (superClass == ObjectReaderAdapter.class && generatedFields) {
            stmts.appendList(genInitFields(fieldsSize, superClass));
        }
        return defMethod(Flags.PUBLIC, names.init, type(TypeTag.VOID), null, null, null, block(stmts.toList()), null);
    }

    private JCTree.JCMethodDecl genWriteConstructor(
            JCTree.JCExpression beanType,
            JCTree.JCNewClass beanNew,
            java.util.List fields,
            Class superClass,
            boolean generatedFields) {
        JCTree.JCExpression fieldWriterType = qualIdent(FieldWriter.class.getName());
        ListBuffer fieldWriters = new ListBuffer<>();
        JCTree.JCExpression objectWritersType = qualIdent(ObjectWriters.class.getName());
        int fieldsSize = fields.size();
        for (int i = 0; i < fieldsSize; ++i) {
            JCTree.JCMethodInvocation writerMethod = null;
            AttributeInfo attributeInfo = fields.get(i);
            if (attributeInfo.getMethod != null) {
                writerMethod = writerMethod(beanType, objectWritersType, attributeInfo, true);
            } else if (attributeInfo.field != null) {
                String fieldName = attributeInfo.field.getSimpleName().toString();
                if (fieldName.equals(attributeInfo.name)) {
                    writerMethod = writerMethod(beanType, objectWritersType, attributeInfo, false);
                } else {
                    writerMethod = writerMethod(beanType, objectWritersType, attributeInfo, false);
                }
            } else {
                messager.printMessage(Diagnostic.Kind.WARNING, "not implemented yet");
            }
            if (writerMethod != null) {
                fieldWriters.append(writerMethod);
            }
        }
        JCTree.JCNewArray fieldReadersArray = newArray(fieldWriterType, null, List.from(fieldWriters));
        JCTree.JCMethodInvocation fieldReadersList = method(qualIdent("java.util.Arrays"), "asList", fieldReadersArray);
        JCTree.JCMethodInvocation superMethod = method(
                ident(names._super),
                List.of(
                        field(beanType, names._class),
                        defNull(),
                        defNull(),
                        literal(0L),
                        fieldReadersList
                )
        );
        ListBuffer stmts = new ListBuffer<>();
        stmts.append(exec(superMethod));
        // initialize fields if necessary
        if (superClass == ObjectWriterAdapter.class && generatedFields) {
            stmts.appendList(genInitFields(fields.size(), superClass));
        }
        return defMethod(Flags.PUBLIC, names.init, type(TypeTag.VOID), null, null, null, block(stmts.toList()), null);
    }

    private JCTree.JCMethodInvocation writerMethod(
            JCTree.JCExpression beanType,
            JCTree.JCExpression objectWritersType,
            AttributeInfo attributeInfo,
            boolean isMethodReference) {
        String type = attributeInfo.type.toString();
        if ("boolean".equals(type)
                || "char".equals(type)
                || "byte".equals(type)
                || "short".equals(type)
                || "int".equals(type)
                || "long".equals(type)
                || "float".equals(type)
                || "double".equals(type)) {
            if (isMethodReference) {
                String methodName = attributeInfo.getMethod.getSimpleName().toString();
                JCTree.JCMemberReference memberedReference = methodRef(beanType, methodName);
                return method(objectWritersType, "fieldWriter", literal(attributeInfo.name), memberedReference);
            } else {
                String fieldName = attributeInfo.field.getSimpleName().toString();
                JCTree.JCLambda lambda = lambda(
                        defVar("bean", beanType),
                        field(ident("bean"), fieldName)
                );
                return method(objectWritersType, "fieldWriter", literal(fieldName), lambda);
            }
        } else if ("java.lang.Class".equals(type)) {
            if (isMethodReference) {
                String methodName = attributeInfo.getMethod.getSimpleName().toString();
                JCTree.JCMemberReference memberedReference = methodRef(beanType, methodName);
                return method(
                        objectWritersType,
                        "fieldWriter",
                        literal(attributeInfo.name),
                        field(beanType, names._class),
                        memberedReference
                );
            } else {
                String fieldName = attributeInfo.field.getSimpleName().toString();
                JCTree.JCLambda lambda = lambda(
                        defVar("bean", beanType),
                        field(ident("bean"), fieldName)
                );
                return method(
                        objectWritersType,
                        "fieldWriter",
                        literal(fieldName),
                        field(beanType, names._class),
                        lambda
                );
            }
        } else if ("java.lang.Type".equals(type)) {
            if (isMethodReference) {
                String methodName = attributeInfo.getMethod.getSimpleName().toString();
                JCTree.JCMemberReference memberedReference = methodRef(beanType, methodName);
                return method(
                        objectWritersType,
                        "fieldWriter",
                        literal(attributeInfo.name),
                        field(beanType, names._class),
                        field(beanType, names._class),
                        memberedReference
                );
            } else {
                String fieldName = attributeInfo.field.getSimpleName().toString();
                JCTree.JCLambda lambda = lambda(
                        List.of(
                                defVar("bean", beanType)
                        ),
                        field(ident("bean"), fieldName)
                );
                return method(
                        objectWritersType,
                        "fieldWriter",
                        literal(fieldName),
                        field(beanType, names._class),
                        field(beanType, names._class),
                        lambda
                );
            }
        } else if ("java.util.List".equals(type)) {
            if (isMethodReference) {
                String methodName = attributeInfo.getMethod.getSimpleName().toString();
                JCTree.JCMemberReference memberedReference = methodRef(beanType, methodName);
                return method(objectWritersType, "fieldWriterListString", literal(attributeInfo.name), memberedReference);
            } else {
                String fieldName = attributeInfo.field.getSimpleName().toString();
                JCTree.JCLambda lambda = lambda(
                        defVar("bean", beanType),
                        field(ident("bean"), fieldName)
                );
                return method(objectWritersType, "fieldWriterListString", literal(fieldName), lambda);
            }
        } else if (type.startsWith("java.util.List")
                || type.startsWith("java.util.ArrayList")
                || type.startsWith("java.util.LinkedList")
                || type.startsWith("java.util.concurrent.CopyOnWriteArrayList")) {
            String itemType;
            if (type.contains("<")) {
                itemType = type.substring(type.indexOf("<") + 1, type.length() - 1);
            } else {
                itemType = type;
            }
            JCTree.JCFieldAccess itemTypeClass = field(qualIdent(itemType), names._class);
            if (isMethodReference) {
                String methodName = attributeInfo.getMethod.getSimpleName().toString();
                JCTree.JCMemberReference memberedReference = methodRef(beanType, methodName);
                return method(objectWritersType, "fieldWriterList", literal(attributeInfo.name), itemTypeClass, memberedReference);
            } else {
                String fieldName = attributeInfo.field.getSimpleName().toString();
                JCTree.JCLambda lambda = lambda(
                        defVar("bean", beanType),
                        field(ident("bean"), fieldName)
                );
                return method(objectWritersType, "fieldWriterList", literal(fieldName), itemTypeClass, lambda);
            }
        } else {
            JCTree.JCFieldAccess fieldClass;
            if (type.contains("[")) {
                fieldClass = field(arrayIdentType(type), names._class);
            } else {
                int idx = type.indexOf("<");
                if (idx != -1) {
                    type = type.substring(0, idx);
                }
                fieldClass = field(identType(type), names._class);
            }
            if (isMethodReference) {
                String methodName = attributeInfo.getMethod.getSimpleName().toString();
                JCTree.JCMemberReference memberedReference = methodRef(beanType, methodName);
                return method(objectWritersType, "fieldWriter", literal(attributeInfo.name), fieldClass, memberedReference);
            } else {
                String fieldName = attributeInfo.field.getSimpleName().toString();
                JCTree.JCLambda lambda = lambda(
                        defVar("bean", beanType),
                        field(ident("bean"), fieldName)
                );
                return method(objectWritersType, "fieldWriter", literal(fieldName), fieldClass, lambda);
            }
        }
    }

    private JCTree.JCMethodInvocation genFieldReader(
            JCTree.JCExpression beanType,
            AttributeInfo attributeInfo,
            JCTree.JCExpression objectReadersType
    ) {
        if (attributeInfo.setMethod == null && attributeInfo.field == null) {
            messager.printMessage(Diagnostic.Kind.WARNING, "not implemented yet");
            return null;
        }
        JCTree.JCMethodInvocation readerMethod;
        String fieldType = attributeInfo.type.toString();
        JCTree.JCLiteral fieldName = literal(attributeInfo.name);
        JCTree.JCExpression lambda = null;
        if (attributeInfo.setMethod != null) {
            lambda = methodRef(beanType, attributeInfo.setMethod.getSimpleName().toString());
        }

        JCTree.JCExpression identType;
        String methodName = null;
        switch (fieldType) {
            case "byte":
                methodName = "fieldReaderByte";
                identType = type(TypeTag.BYTE);
                break;
            case "short":
                identType = type(TypeTag.SHORT);
                methodName = "fieldReaderShort";
                break;
            case "int":
                identType = type(TypeTag.INT);
                methodName = "fieldReaderInt";
                break;
            case "long":
                identType = type(TypeTag.LONG);
                methodName = "fieldReaderLong";
                break;
            case "char":
                identType = type(TypeTag.CHAR);
                methodName = "fieldReaderChar";
                break;
            case "float":
                identType = type(TypeTag.FLOAT);
                methodName = "fieldReaderFloat";
                break;
            case "double":
                identType = type(TypeTag.DOUBLE);
                methodName = "fieldReaderDouble";
                break;
            case "boolean":
                identType = type(TypeTag.BOOLEAN);
                methodName = "fieldReaderBool";
                break;
            case "java.lang.String":
                identType = qualIdent("java.lang.String");
                methodName = "fieldReaderString";
                break;
            default:
                identType = getFieldValueType(fieldType);
                break;
        }

        if (lambda == null) {
            JCTree.JCVariableDecl object = defVar("o", beanType);
            JCTree.JCExpression valueType = identType;
            if (fieldType.startsWith("java.util.Map<")) {
                valueType = qualIdent("java.util.Map");
            }
            JCTree.JCVariableDecl fieldValue = defVar("v", valueType);
            List args = List.of(object, fieldValue);

            lambda = lambda(
                    args,
                    assign(
                            field(
                                    ident("o"),
                                    attributeInfo.field.getSimpleName().toString()),
                            ident("v")));
        }

        if (methodName != null) {
            return method(objectReadersType, methodName, fieldName, lambda);
        }

        readerMethod = genFieldReaderList(objectReadersType, fieldType, fieldName, lambda);
        if (readerMethod != null) {
            return readerMethod;
        }

        readerMethod = genFieldReaderMap(objectReadersType, fieldType, fieldName, lambda);
        if (readerMethod != null) {
            return readerMethod;
        }

        if (fieldType.indexOf('<') == -1) {
            readerMethod = method(
                    objectReadersType,
                    "fieldReader",
                    fieldName,
                    field(getFieldValueType(fieldType), names._class),
                    lambda
            );
        } else {
            JCTree.JCFieldAccess fieldClassRef = field(beanType, names._class);
            String fieldReaderMethodName;
            List args;
            if (attributeInfo.setMethod != null) {
                fieldReaderMethodName = "fieldReaderWithMethod";
                args = List.of(literal(attributeInfo.name), fieldClassRef, literal(methodName));
            } else {
                JCTree.JCLiteral fieldNameLiteral = literal(attributeInfo.field.getSimpleName().toString());
                if (fieldName.toString().equals(attributeInfo.name)) {
                    args = List.of(fieldNameLiteral, fieldClassRef);
                } else {
                    args = List.of(literal(attributeInfo.name), fieldClassRef, fieldNameLiteral);
                }
                fieldReaderMethodName = "fieldReaderWithField";
            }
            readerMethod = method(objectReadersType, fieldReaderMethodName, args);
        }
        return readerMethod;
    }

    private JCTree.JCMethodInvocation genFieldReaderList(
            JCTree.JCExpression objectReadersType,
            String fieldType,
            JCTree.JCLiteral fieldName,
            JCTree.JCExpression lambda
    ) {
        String listType = null, itemType = null;
        String[] listTypes = {"java.util.List", "java.util.ArrayList"};
        for (String item : listTypes) {
            if (fieldType.startsWith(item)) {
                if (fieldType.length() != item.length()
                        && fieldType.charAt(item.length()) == '<'
                ) {
                    int lastIndex = fieldType.lastIndexOf('>');
                    String temp = fieldType.substring(item.length() + 1, lastIndex);
                    if (temp.indexOf(',') == -1 && temp.indexOf('?') == -1) {
                        listType = item;
                        itemType = temp;
                    }
                    break;
                }
            }
        }

        if (listType == null) {
            return null;
        }

        JCTree.JCMethodInvocation readerMethod;
        if ("java.lang.String".equals(itemType) && "java.util.List".equals(listType)) {
            readerMethod = method(
                    objectReadersType,
                    "fieldReaderListStr",
                    fieldName,
                    lambda
            );
        } else {
            readerMethod = method(
                    objectReadersType,
                    "fieldReaderList",
                    fieldName,
                    field(getFieldValueType(itemType), names._class),
                    constructorRef(qualIdent("java.util.ArrayList")),
                    lambda
            );
        }

        return readerMethod;
    }

    private JCTree.JCMethodInvocation genFieldReaderMap(
            JCTree.JCExpression objectReadersType,
            String fieldType,
            JCTree.JCLiteral fieldName,
            JCTree.JCExpression lambda
    ) {
        String mapType = null, keyType = null, valueType = null;
        String[] mapTypes = {"java.util.Map", "java.util.HashMap", "java.util.TreeMap"};
        for (String item : mapTypes) {
            if (fieldType.startsWith(item)) {
                if (fieldType.length() != item.length()
                        && fieldType.charAt(item.length()) == '<'
                ) {
                    int lastIndex = fieldType.lastIndexOf('>');
                    String keyValueType = fieldType.substring(item.length() + 1, lastIndex);
                    if (keyValueType.indexOf('<') == -1 && keyValueType.indexOf('?') == -1) {
                        int commaIndex = keyValueType.indexOf(',');
                        if (commaIndex == -1) {
                            continue;
                        }

                        keyType = keyValueType.substring(0, commaIndex);
                        valueType = keyValueType.substring(commaIndex + 1);

                        if (keyType.indexOf('<') != -1
                                || keyType.indexOf('?') != -1
                                || valueType.indexOf('<') != -1
                                || valueType.indexOf('?') != -1
                        ) {
                            keyType = null;
                            valueType = null;
                            continue;
                        }

                        mapType = item;
                    }
                    break;
                }
            }
        }

        if (mapType == null) {
            return null;
        }

        return method(
                objectReadersType,
                "fieldReaderMap",
                fieldName,
                field(getFieldValueType(mapType), names._class),
                field(getFieldValueType(keyType), names._class),
                field(getFieldValueType(valueType), names._class),
                lambda
        );
    }

    private List genInitFields(int fieldsSize, Class superClass) {
        ListBuffer stmts = new ListBuffer<>();
        if (ObjectReaderAdapter.class.isAssignableFrom(superClass)) {
            JCTree.JCFieldAccess fieldReaders = field(ident(names._this), "fieldReaders");
            for (int i = 0; i < fieldsSize; ++i) {
                stmts.append(exec(assign(ident(fieldReader(i)), indexed(fieldReaders, literal(i)))));
            }
        } else if (ObjectWriterAdapter.class.isAssignableFrom(superClass)) {
            JCTree.JCFieldAccess fieldWriters = field(ident(names._this), "fieldWriterArray");
            for (int i = 0; i < fieldsSize; ++i) {
                stmts.append(exec(assign(ident(fieldWriter(i)), cast(qualIdent(FieldWriter.class.getName()), indexed(fieldWriters, literal(i))))));
            }
        }
        return stmts.toList();
    }

    private JCTree.JCMethodDecl genCreateInstance(JCTree.JCIdent objectType, JCTree.JCNewClass beanNew) {
        JCTree.JCVariableDecl featuresVar = defVar("features", TypeTag.LONG);
        return defMethod(Flags.PUBLIC, "createInstance", objectType, List.of(featuresVar), block(defReturn(beanNew)));
    }

    private JCTree.JCMethodDecl genReadObject(
            JCTree.JCIdent objectType,
            JCTree.JCExpression beanType,
            JCTree.JCNewClass beanNew,
            java.util.List attributeInfos,
            StructInfo structInfo,
            boolean isJsonb
    ) {
        int fieldNameLengthMin = 0, fieldNameLengthMax = 0;
        for (int i = 0; i < attributeInfos.size(); ++i) {
            String fieldName = attributeInfos.get(i).name;

            int fieldNameLength = fieldName.getBytes(StandardCharsets.UTF_8).length;
            if (i == 0) {
                fieldNameLengthMin = fieldNameLength;
                fieldNameLengthMax = fieldNameLength;
            } else {
                fieldNameLengthMin = Math.min(fieldNameLength, fieldNameLengthMin);
                fieldNameLengthMax = Math.max(fieldNameLength, fieldNameLengthMax);
            }
        }

        JCTree.JCVariableDecl jsonReaderVar = defVar("jsonReader", qualIdent(JSONReader.class.getName()));
        JCTree.JCIdent jsonReaderIdent = ident(jsonReaderVar);
        JCTree.JCVariableDecl fieldTypeVar = defVar("fieldType", qualIdent(Type.class.getName()));
        JCTree.JCVariableDecl fieldNameVar = defVar("fieldName", objectType);
        JCTree.JCVariableDecl featuresVar = defVar("features", TypeTag.LONG);

        JCTree.JCReturn nullReturn = defReturn(defNull());

        ListBuffer readObjectBody = new ListBuffer<>();

        JCTree.JCMethodInvocation nextIfNullMethod = method(jsonReaderIdent, "nextIfNull");
        readObjectBody.append(defIf(nextIfNullMethod, nullReturn));

        readObjectBody.append(exec(method(jsonReaderIdent, "nextIfObjectStart")));

        JCTree.JCVariableDecl features2Var = defVar(
                Flags.PARAMETER,
                "features2",
                TypeTag.LONG,
                bitOr(
                        ident("features"),
                        field(ident(names._this), "features")
                )
        );
        readObjectBody.append(features2Var);

        JCTree.JCVariableDecl objectVar = defVar("object", beanType, beanNew);
        JCTree.JCIdent objectIdent = ident(objectVar);
        readObjectBody.append(objectVar);

        int fieldsSize = attributeInfos.size();

        JCTree.JCLabeledStatement loopLabel = label("_while");
        JCTree.JCWhileLoop loopHead = whileLoop(unary(JCTree.Tag.NOT, method(jsonReaderIdent, "nextIfObjectEnd")), null);
        ListBuffer loopBody = new ListBuffer<>();

        boolean switchGen = false;
        if (fieldNameLengthMin >= 2 && fieldNameLengthMax <= 43) {
            loopBody.appendList(genRead243(attributeInfos, jsonReaderIdent, structInfo, loopLabel, objectIdent, beanType, isJsonb));
            switchGen = true;
        }

        JCTree.JCFieldAccess readFieldNameHashCode = field(jsonReaderIdent, "readFieldNameHashCode");
        JCTree.JCVariableDecl hashCode64Var = defVar("hashCode64", TypeTag.LONG, method(readFieldNameHashCode));
        JCTree.JCExpression hashCode64 = ident(hashCode64Var);
        loopBody.append(hashCode64Var);

        if (switchGen && !isJsonb) {
            loopBody.append(
                    exec(method(
                            field(ident(names._this), "readFieldValue"),
                            List.of(hashCode64, jsonReaderIdent, ident(features2Var), ident(objectVar))
                    )));
        } else {
            if (fieldsSize <= 6) {
                for (int i = 0; i < fieldsSize; ++i) {
                    AttributeInfo attributeInfo = attributeInfos.get(i);
                    List readFieldValueStmts = genReadFieldValue(
                            attributeInfo,
                            jsonReaderIdent,
                            i,
                            structInfo,
                            loopLabel,
                            objectIdent,
                            beanType,
                            isJsonb);
                    loopBody.appendList(List.of(defIf(eq(hashCode64, attributeInfo.nameHashCode), block(readFieldValueStmts))));
                }
            } else {
                Map> map = new TreeMap<>();
                Map mapping = new TreeMap<>();
                Map mappingIndex = new TreeMap<>();
                for (int i = 0; i < fieldsSize; ++i) {
                    AttributeInfo attr = attributeInfos.get(i);
                    long fieldNameHash = attr.nameHashCode;
                    int hashCode32 = (int) (fieldNameHash ^ (fieldNameHash >>> 32));
                    java.util.List hashCode64List = map.computeIfAbsent(hashCode32, k -> new ArrayList<>());
                    hashCode64List.add(fieldNameHash);
                    mapping.put(fieldNameHash, attr);
                    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);
                loopBody.append(hashCode32Var);
                JCTree.JCExpression hashCode32 = ident(hashCode32Var);

                JCTree.JCLabeledStatement switchLabel = label("_switch");
                ListBuffer cases = new ListBuffer<>();
                for (int i = 0; i < hashCode32Keys.length; ++i) {
                    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 attributeInfo = mapping.get(fieldNameHash);
                        stmts = stmts.appendList(genReadFieldValue(
                                attributeInfo,
                                jsonReaderIdent,
                                index,
                                structInfo,
                                loopLabel,
                                objectIdent,
                                beanType,
                                isJsonb));
                        stmts.append(defContinue(loopLabel));
                    } 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(
                                    field,
                                    jsonReaderIdent,
                                    index,
                                    structInfo,
                                    loopLabel,
                                    objectIdent,
                                    beanType,
                                    isJsonb);
                            stmts = stmts.append(defIf(eq(hashCode64, fieldNameHash), block(stmtsIf)));
                            stmts.append(defContinue(loopLabel));
                        }
                        stmts.append(defBreak(switchLabel));
                    }
                    cases.append(defCase(getHashCode32Var(literal(fieldNameHash)).getInitializer(), stmts));
                }
                switchLabel.body = defSwitch(hashCode32, cases.toList());
                loopBody.append(switchLabel);
            }

            loopBody.append(
                    defIf(
                            method(
                                    ident(names._this),
                                    "readFieldValueWithLCase",
                                    jsonReaderIdent,
                                    objectIdent,
                                    ident(hashCode64Var),
                                    ident(features2Var)
                            ),
                            defContinue(loopLabel)
                    )
            );
            JCTree.JCFieldAccess processExtraField = field(ident(names._this), "processExtra");
            loopBody.append(exec(method(processExtraField, jsonReaderIdent, objectIdent, ident(features2Var))));
        }

        loopHead.body = block(loopBody.toList());
        loopLabel.body = loopHead;
        readObjectBody.append(loopLabel);

        if (!isJsonb) {
            readObjectBody.append(exec(method(jsonReaderIdent, "nextIfComma")));
        }

        readObjectBody.append(defReturn(objectIdent));

        return defMethod(
                Flags.PUBLIC,
                isJsonb ? "readJSONBObject" : "readObject",
                objectType,
                List.of(jsonReaderVar, fieldTypeVar, fieldNameVar, featuresVar),
                block(readObjectBody.toList())
        );
    }

    private JCTree.JCMethodDecl genWrite(
            JCTree.JCIdent objectType,
            JCTree.JCExpression beanType,
            JCTree.JCNewClass beanNew,
            java.util.List attributeInfos,
            StructInfo structInfo,
            boolean jsonb
    ) {
        int fieldNameLengthMin = 0, fieldNameLengthMax = 0;
        for (int i = 0; i < attributeInfos.size(); ++i) {
            String fieldName = attributeInfos.get(i).name;

            int fieldNameLength = fieldName.getBytes(StandardCharsets.UTF_8).length;
            if (i == 0) {
                fieldNameLengthMin = fieldNameLength;
                fieldNameLengthMax = fieldNameLength;
            } else {
                fieldNameLengthMin = Math.min(fieldNameLength, fieldNameLengthMin);
                fieldNameLengthMax = Math.max(fieldNameLength, fieldNameLengthMax);
            }
        }

        ListBuffer writeBody = new ListBuffer<>();
        MethodWriterContext mwc = new MethodWriterContext(beanType, objectType, jsonb);
        mwc.genVariantsMethodBefore(writeBody);

        writeBody.append(
                defIfReturn(
                        ne(bitAnd(mwc.contextFeatures, IgnoreErrorGetter.mask | UnquoteFieldName.mask), 0L),
                        exec(method(
                                names._super,
                                "write",
                                mwc.jsonWriter,
                                mwc.object,
                                mwc.fieldName,
                                mwc.fieldType,
                                mwc.features
                        ))
                )
        );

        writeBody.append(
                defIfReturn(
                        mwc.jsonWriterField("jsonb"),
                        defIf(
                                ne(bitAnd(mwc.contextFeatures, literal(BeanToArray.mask)), 0L),
                                block(exec(method(
                                        names._this,
                                        "writeArrayMappingJSONB",
                                        mwc.jsonWriter,
                                        mwc.object,
                                        mwc.fieldName,
                                        mwc.fieldType,
                                        mwc.features))
                                ),
                                block(exec(method(
                                        names._this,
                                        "writeJSONB",
                                        mwc.jsonWriter,
                                        mwc.object,
                                        mwc.fieldName,
                                        mwc.fieldType,
                                        mwc.features))
                                )
                        )
                )
        );

        writeBody.append(
                defIfReturn(
                        ne(bitAnd(mwc.contextFeatures, BeanToArray.mask), 0L),
                        exec(method(
                                names._this,
                                "writeArrayMapping",
                                mwc.jsonWriter,
                                mwc.object,
                                mwc.fieldName,
                                mwc.fieldType,
                                mwc.features))
                )
        );

        writeBody.append(
                defIfReturn(
                        method(names._this, "hasFilter", mwc.jsonWriter),
                        exec(
                                method(
                                        names._this,
                                        "writeWithFilter",
                                        mwc.jsonWriter,
                                        mwc.object,
                                        mwc.fieldName,
                                        mwc.fieldType,
                                        mwc.features
                                )
                        )
                )
        );

        writeBody.append(
                defIfReturn(
                        ne(bitAnd(mwc.contextFeatures, IgnoreNoneSerializable.mask), 0L),
                        exec(mwc.jsonWriterMethod("writeNull"))
                )
        );

        writeBody.append(
                defIfReturn(
                        ne(bitAnd(mwc.contextFeatures, ErrorOnNoneSerializable.mask), 0L),
                        exec(method(names._this, "errorOnNoneSerializable"))
                )
        );

        writeBody.append(exec(mwc.jsonWriterMethod("startObject")));
        JCTree.JCVariableDecl var7Var = defVar("var7", true);
        writeBody.append(var7Var);
        writeBody.append(
                defIf(
                        and(
                                notNull(mwc.object),
                                and(
                                        ne(method(mwc.object, "getClass"), mwc.fieldType),
                                        method(
                                                mwc.jsonWriter,
                                                "isWriteTypeInfo",
                                                mwc.object,
                                                mwc.fieldType,
                                                mwc.features
                                        )
                                )
                        ),
                        block(exec(
                                assign(
                                        ident(var7Var),
                                        bitXor(
                                                method(names._this, "writeTypeInfo", mwc.jsonWriter),
                                                literal(true)
                                        )
                                )
                        )),
                        null));

        for (int i = 0; i < attributeInfos.size(); ++i) {
            AttributeInfo attributeInfo = attributeInfos.get(i);
            writeBody.appendList(
                    genWriteField(
                            mwc,
                            attributeInfo,
                            i
                    )
            );
        }

        writeBody.append(exec(mwc.jsonWriterMethod("endObject")));

        return defMethod(
                Flags.PUBLIC,
                "write",
                type(TypeTag.VOID),
                List.of(
                        defVar("jsonWriter", qualIdent(JSONWriter.class.getName())),
                        defVar("object", objectType),
                        defVar("fieldName", objectType),
                        defVar("fieldType", qualIdent(Type.class.getName())),
                        defVar("features", TypeTag.LONG)
                ),
                block(writeBody.toList())
        );
    }

    private JCTree.JCStatement genWriteFieldName(MethodWriterContext mwc, AttributeInfo attributeInfo, int i) {
        String methodName;
        if (!mwc.jsonb) {
            byte[] fieldNameUTF8 = attributeInfo.name.getBytes(StandardCharsets.UTF_8);

            boolean asciiName = true;
            for (int j = 0; j < fieldNameUTF8.length; j++) {
                if (fieldNameUTF8[j] < 0) {
                    asciiName = false;
                    break;
                }
            }

            int length = fieldNameUTF8.length;
            if (length >= 2 && length <= 16 && asciiName) {
                long name1 = 0;
                int name12 = 0;
                long name1SQ = 0;
                int name1SQ2 = 0;
                byte[] bytes = new byte[8];
                switch (length) {
                    case 2:
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 2);
                        bytes[3] = '"';
                        bytes[4] = ':';
                        methodName = "writeName2Raw";
                        break;
                    case 3:
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 3);
                        bytes[4] = '"';
                        bytes[5] = ':';
                        methodName = "writeName3Raw";
                        break;
                    case 4:
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 4);
                        bytes[5] = '"';
                        bytes[6] = ':';
                        methodName = "writeName4Raw";
                        break;
                    case 5:
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 5);
                        bytes[6] = '"';
                        bytes[7] = ':';
                        methodName = "writeName5Raw";
                        break;
                    case 6:
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 6);
                        bytes[7] = '"';
                        methodName = "writeName6Raw";
                        break;
                    case 7:
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 7);
                        methodName = "writeName7Raw";
                        break;
                    case 8: {
                        bytes = fieldNameUTF8;
                        methodName = "writeName8Raw";
                        break;
                    }
                    case 9: {
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 7);
                        byte[] name1Bytes = new byte[4];
                        name1Bytes[0] = fieldNameUTF8[7];
                        name1Bytes[1] = fieldNameUTF8[8];
                        name1Bytes[2] = '"';
                        name1Bytes[3] = ':';
                        name12 = UNSAFE.getInt(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        name1Bytes[2] = '\'';
                        name1SQ2 = UNSAFE.getInt(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        methodName = "writeName9Raw";
                        break;
                    }
                    case 10: {
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 7);
                        byte[] name1Bytes = new byte[8];
                        name1Bytes[0] = fieldNameUTF8[7];
                        name1Bytes[1] = fieldNameUTF8[8];
                        name1Bytes[2] = fieldNameUTF8[9];
                        name1Bytes[3] = '"';
                        name1Bytes[4] = ':';
                        name1 = UNSAFE.getLong(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        name1Bytes[3] = '\'';
                        name1SQ = UNSAFE.getLong(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        methodName = "writeName10Raw";
                        break;
                    }
                    case 11: {
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 7);
                        byte[] name1Bytes = new byte[8];
                        name1Bytes[0] = fieldNameUTF8[7];
                        name1Bytes[1] = fieldNameUTF8[8];
                        name1Bytes[2] = fieldNameUTF8[9];
                        name1Bytes[3] = fieldNameUTF8[10];
                        name1Bytes[4] = '"';
                        name1Bytes[5] = ':';

                        name1 = UNSAFE.getLong(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        name1Bytes[4] = '\'';
                        name1SQ = UNSAFE.getLong(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        methodName = "writeName11Raw";
                        break;
                    }
                    case 12: {
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 7);
                        byte[] name1Bytes = new byte[8];
                        name1Bytes[0] = fieldNameUTF8[7];
                        name1Bytes[1] = fieldNameUTF8[8];
                        name1Bytes[2] = fieldNameUTF8[9];
                        name1Bytes[3] = fieldNameUTF8[10];
                        name1Bytes[4] = fieldNameUTF8[11];
                        name1Bytes[5] = '"';
                        name1Bytes[6] = ':';

                        name1 = UNSAFE.getLong(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        name1Bytes[5] = '\'';
                        name1SQ = UNSAFE.getLong(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        methodName = "writeName12Raw";
                        break;
                    }
                    case 13: {
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 7);
                        byte[] name1Bytes = new byte[8];
                        name1Bytes[0] = fieldNameUTF8[7];
                        name1Bytes[1] = fieldNameUTF8[8];
                        name1Bytes[2] = fieldNameUTF8[9];
                        name1Bytes[3] = fieldNameUTF8[10];
                        name1Bytes[4] = fieldNameUTF8[11];
                        name1Bytes[5] = fieldNameUTF8[12];
                        name1Bytes[6] = '"';
                        name1Bytes[7] = ':';

                        name1 = UNSAFE.getLong(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        name1Bytes[6] = '\'';
                        name1SQ = UNSAFE.getLong(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        methodName = "writeName13Raw";
                        break;
                    }
                    case 14: {
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 7);
                        byte[] name1Bytes = new byte[8];
                        name1Bytes[0] = fieldNameUTF8[7];
                        name1Bytes[1] = fieldNameUTF8[8];
                        name1Bytes[2] = fieldNameUTF8[9];
                        name1Bytes[3] = fieldNameUTF8[10];
                        name1Bytes[4] = fieldNameUTF8[11];
                        name1Bytes[5] = fieldNameUTF8[12];
                        name1Bytes[6] = fieldNameUTF8[13];
                        name1Bytes[7] = '"';

                        name1 = UNSAFE.getLong(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        name1Bytes[7] = '\'';
                        name1SQ = UNSAFE.getLong(name1Bytes, ARRAY_BYTE_BASE_OFFSET);

                        methodName = "writeName14Raw";
                        break;
                    }
                    case 15: {
                        bytes[0] = '"';
                        System.arraycopy(fieldNameUTF8, 0, bytes, 1, 7);
                        name1 = UNSAFE.getLong(fieldNameUTF8, ARRAY_BYTE_BASE_OFFSET + 7);
                        name1SQ = name1;
                        methodName = "writeName15Raw";
                        break;
                    }
                    case 16: {
                        System.arraycopy(fieldNameUTF8, 0, bytes, 0, 8);
                        name1 = UNSAFE.getLong(fieldNameUTF8, ARRAY_BYTE_BASE_OFFSET + 8);
                        name1SQ = name1;
                        methodName = "writeName16Raw";
                        break;
                    }
                    default:
                        throw new IllegalStateException("length : " + length);
                }
                long nameIn64DoubleQuote = UNSAFE.getLong(bytes, ARRAY_BYTE_BASE_OFFSET);
                for (int j = 0; j < bytes.length; j++) {
                    if (bytes[j] == '"') {
                        bytes[j] = '\'';
                    }
                }
                long nameIn64SingleQuote = UNSAFE.getLong(bytes, ARRAY_BYTE_BASE_OFFSET);
                JCTree.JCConditional ternary = ternary(mwc.nameDirect, literal(nameIn64DoubleQuote), literal(nameIn64SingleQuote));
                if (length == 9) {
                    ListBuffer stmts = new ListBuffer<>();
                    JCTree.JCConditional ternary2 = ternary(mwc.nameDirect, name12, name1SQ2);
                    JCTree.JCVariableDecl name1Var = defVar("name1", TypeTag.INT, ternary2);
                    stmts.append(defVar("name1", TypeTag.INT, ternary2));
                    stmts.append(exec(mwc.jsonWriterMethod(methodName, ternary, name1Var)));
                    return block(stmts.toList());
                } else if (length > 9) {
                    JCTree.JCConditional ternary2 = ternary(mwc.nameDirect, name1, name1SQ);
                    return exec(mwc.jsonWriterMethod(methodName, ternary, ternary2));
                } else {
                    return exec(mwc.jsonWriterMethod(methodName, ternary));
                }
            }
        } else {
            byte[] fieldNameUTF8 = JSONB.toBytes(attributeInfo.name);
            int length = fieldNameUTF8.length;
            byte[] bytes = Arrays.copyOf(fieldNameUTF8, 16);
            switch (length) {
                case 2:
                    methodName = "writeName2Raw";
                    break;
                case 3:
                    methodName = "writeName3Raw";
                    break;
                case 4:
                    methodName = "writeName4Raw";
                    break;
                case 5:
                    methodName = "writeName5Raw";
                    break;
                case 6:
                    methodName = "writeName6Raw";
                    break;
                case 7:
                    methodName = "writeName7Raw";
                    break;
                case 8:
                    methodName = "writeName8Raw";
                    break;
                case 9:
                    methodName = "writeName9Raw";
                    break;
                case 10:
                    methodName = "writeName10Raw";
                    break;
                case 11:
                    methodName = "writeName11Raw";
                    break;
                case 12:
                    methodName = "writeName12Raw";
                    break;
                case 13:
                    methodName = "writeName13Raw";
                    break;
                case 14:
                    methodName = "writeName14Raw";
                    break;
                case 15:
                    methodName = "writeName15Raw";
                    break;
                case 16:
                    methodName = "writeName16Raw";
                    break;
                default:
                    break;
            }
        }
        return exec(method(field(names._this, fieldWriter(i)), mwc.jsonb ? "writeFieldNameJSONB" : "writeFieldName", mwc.jsonWriter));
    }

    private ListBuffer genWriteField(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        String type = attributeInfo.type.toString();
        if ("boolean".equals(type)) {
            return genWriteFieldValueBooleanV(mwc, attributeInfo, i);
        } else if ("boolean[]".equals(type)
                || "byte[]".equals(type)
                || "char[]".equals(type)
                || "short[]".equals(type)
                || "float[]".equals(type)
                || "double[]".equals(type)) {
            return stmts.appendList(genWriteFieldValueArray(mwc, attributeInfo, i));
        } else if ("int".equals(type)) {
            return stmts.appendList(genWriteFieldValueInt32V(mwc, attributeInfo, i));
        } else if ("char".equals(type)
                || "byte".equals(type)
                || "int".equals(type)
                || "short".equals(type)
                || "float".equals(type)
                || "double".equals(type)) {
            stmts.append(genWriteFieldName(mwc, attributeInfo, i));
            return stmts.appendList(genWriteFieldValue(mwc, attributeInfo, i, null));
        } else if ("int[]".equals(type)) {
            return stmts.appendList(genWriteFieldValueIntVA(mwc, attributeInfo, i));
        } else if ("long".equals(type)) {
            return stmts.appendList(genWriteFieldValueInt64V(mwc, attributeInfo, i));
        } else if ("long[]".equals(type)) {
            return stmts.appendList(genWriteFieldValueInt64VA(mwc, attributeInfo, i));
        } else if ("java.lang.Integer".equals(type)) {
            return stmts.appendList(genWriteInt32(mwc, attributeInfo, i));
        } else if ("java.lang.Long".equals(type)) {
            return stmts.appendList(genWriteInt64(mwc, attributeInfo, i));
        } else if ("java.lang.Float".equals(type)) {
            return stmts.appendList(genWriteFloat(mwc, attributeInfo, i));
        } else if ("java.lang.Double".equals(type)) {
            return stmts.appendList(genWriteDouble(mwc, attributeInfo, i));
        } else if ("java.math.BigDecimal".equals(type)) {
            return stmts.appendList(genWriteBigDecimal(mwc, attributeInfo, i));
        } else if ("java.lang.Boolean".equals(type)) {
            return stmts.appendList(genWriteBoolean(mwc, attributeInfo, i));
        } else if ("java.lang.String".equals(type)) {
            return stmts.appendList(genWriteFieldValueString(mwc, attributeInfo, i));
        } else if (attributeInfo.type instanceof com.sun.tools.javac.code.Type.ClassType
                && ((com.sun.tools.javac.code.Type.ClassType) attributeInfo.type).supertype_field != null
                && "java.lang.Enum".equals(((com.sun.tools.javac.code.Type.ClassType) attributeInfo.type).supertype_field.tsym.toString())) {
            return stmts.appendList(genWriteFieldValueEnum(mwc, attributeInfo, i));
        } else if ("java.util.Date".equals(type)) {
            return stmts.appendList(genWriteFieldValueDate(mwc, attributeInfo, i));
        } else if (type.contains("java.util.List")) {
            return stmts.appendList(genWriteFieldValueList(mwc, attributeInfo, i));
        } else {
            return stmts.appendList(genWriteFieldValueObject(mwc, attributeInfo, i));
        }
    }

    private ListBuffer genWriteFieldValue(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i,
            JCTree.JCExpression fieldValue
    ) {
        ListBuffer stmts = new ListBuffer<>();
        String type = attributeInfo.type.toString();
        if (fieldValue == null) {
            fieldValue = genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType);
        }
        List fieldValueExprs = List.of(fieldValue);
        JCTree.JCBinary writeAsStringBinary = ne(
                bitAnd(
                        field(field(ident(names._this), fieldWriter(i)), "features"),
                        WriteNonStringValueAsString.mask
                ),
                0
        );
        if ("boolean".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeBool", fieldValueExprs)));
        } else if ("char".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeChar", fieldValueExprs)));
        } else if ("byte".equals(type)) {
            stmts.append(defIf(writeAsStringBinary,
                    block(exec(mwc.jsonWriterMethod("writeString", fieldValueExprs))),
                    block(exec(mwc.jsonWriterMethod("writeInt8", fieldValueExprs)))));
        } else if ("short".equals(type)) {
            stmts.append(defIf(writeAsStringBinary,
                    block(exec(mwc.jsonWriterMethod("writeString", fieldValueExprs))),
                    block(exec(mwc.jsonWriterMethod("writeInt16", fieldValueExprs)))));
        } else if ("int".equals(type)) {
            stmts.append(defIf(writeAsStringBinary,
                    block(exec(mwc.jsonWriterMethod("writeString", fieldValueExprs))),
                    block(exec(mwc.jsonWriterMethod("writeInt32", fieldValueExprs)))));
        } else if ("Integer".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeInt32", fieldValueExprs)));
        } else if ("long".equals(type)) {
            stmts.append(defIf(writeAsStringBinary,
                    block(exec(mwc.jsonWriterMethod("writeString", fieldValueExprs))),
                    block(exec(mwc.jsonWriterMethod("writeInt64", fieldValueExprs)))));
        } else if ("Long".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeInt64", fieldValueExprs)));
        } else if ("float".equals(type)) {
            stmts.append(defIf(writeAsStringBinary,
                    block(exec(mwc.jsonWriterMethod("writeString", fieldValueExprs))),
                    block(exec(mwc.jsonWriterMethod("writeFloat", fieldValueExprs)))));
        } else if ("double".equals(type)) {
            stmts.append(defIf(writeAsStringBinary,
                    block(exec(mwc.jsonWriterMethod("writeString", fieldValueExprs))),
                    block(exec(mwc.jsonWriterMethod("writeDouble", fieldValueExprs)))));
        } else if ("boolean[]".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeBool", fieldValueExprs)));
        } else if ("char[]".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeString", fieldValueExprs)));
        } else if ("byte[]".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeBinary", fieldValueExprs)));
        } else if ("short[]".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeInt16", fieldValueExprs)));
        } else if ("int[]".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeInt32", fieldValueExprs)));
        } else if ("long[]".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeInt64", fieldValueExprs)));
        } else if ("float[]".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeFloat", fieldValueExprs)));
        } else if ("double[]".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeDouble", fieldValueExprs)));
        } else if ("BigDecimal".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeDecimal", fieldValueExprs)));
        } else if ("Enum".equals(type)) {
            stmts.append(exec(mwc.jsonWriterMethod("writeEnum", fieldValueExprs)));
        } else {
            throw new UnsupportedOperationException();
        }
        return stmts;
    }

    private ListBuffer genWriteFieldValueBooleanV(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        JCTree.JCVariableDecl boolVar = defVar("bool" + i, TypeTag.BOOLEAN, genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(boolVar);
        stmts.append(defIf(unary(JCTree.Tag.NOT, ident(boolVar)), block(List.nil())));
        stmts.append(exec(method(field(names._this, fieldWriter(i)), "writeBool", mwc.jsonWriter, boolVar)));
        return stmts;
    }

    private ListBuffer genWriteFieldValueArray(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        String type = attributeInfo.type.toString();
        String methodName;
        ListBuffer stmts = new ListBuffer<>();
        List fieldValueExprs = List.of(mwc.jsonWriter, genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        if ("char[]".equals(type)) {
            methodName = "writeString";
        } else if ("boolean[]".equals(type)) {
            methodName = "writeBool";
        } else if ("byte[]".equals(type)) {
            methodName = "writeBinary";
        } else if ("short[]".equals(type)) {
            methodName = "writeInt16";
        } else if ("float[]".equals(type)) {
            methodName = "writeFloat";
        } else if ("double[]".equals(type)) {
            methodName = "writeDouble";
        } else if ("enum".equals(type)) {
            methodName = "writeEnumJSONB";
        } else {
            throw new UnsupportedOperationException();
        }
        return stmts.append(exec(method(field(names._this, fieldWriter(i)), methodName, fieldValueExprs)));
    }

    private ListBuffer genWriteFieldValueInt32V(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        JCTree.JCVariableDecl int32Var = defVar("int32" + i, TypeTag.INT, genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(int32Var);
        ListBuffer ifStmts = new ListBuffer<>();
        ifStmts.append(genWriteFieldName(mwc, attributeInfo, i));
        ifStmts.appendList(
                genWriteFieldValue(mwc, attributeInfo, i, ident(int32Var)));
        stmts.append(defIf(
                or(ne(int32Var, 0), not(mwc.notWriteDefaultValue)),
                block(ifStmts.toList())));
        return stmts;
    }

    private ListBuffer genWriteFieldValueIntVA(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        String elemType = ((ArrayType) attributeInfo.type).getComponentType().toString();
        JCTree.JCVariableDecl intArrayVar = defVar("intArray" + i, arrayIdentType(elemType), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(intArrayVar);

        ListBuffer notZeroStmts = new ListBuffer<>();
        notZeroStmts.append(genWriteFieldName(mwc, attributeInfo, i));
        notZeroStmts.append(exec(mwc.jsonWriterMethod("writeArrayNull")));

        ListBuffer nullStmts = new ListBuffer<>();
        nullStmts.append(defIf(mwc.writeNulls, block(notZeroStmts.toList())));

        ListBuffer notNullStmts = new ListBuffer<>();
        notNullStmts.append(genWriteFieldName(mwc, attributeInfo, i));
        notNullStmts.append(exec(mwc.jsonWriterMethod("writeInt32", intArrayVar)));

        stmts.append(defIf(eq(intArrayVar, defNull()), block(nullStmts.toList()), block(notNullStmts.toList())));
        return stmts;
    }

    private ListBuffer genWriteFieldValueInt64V(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        JCTree.JCVariableDecl int64Var = defVar("int64" + i, TypeTag.LONG, genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(int64Var);
        ListBuffer ifStmts = new ListBuffer<>();
        ifStmts.append(genWriteFieldName(mwc, attributeInfo, i));
        ifStmts.appendList(genWriteFieldValue(mwc, attributeInfo, i, ident(int64Var)));
        stmts.append(defIf(or(ne(int64Var, 0), not(mwc.notWriteDefaultValue)),
                block(ifStmts.toList()),
                null));
        return stmts;
    }

    private ListBuffer genWriteFieldValueInt64VA(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        String elemType = ((ArrayType) attributeInfo.type).getComponentType().toString();
        JCTree.JCVariableDecl longArrayVar = defVar("longArray" + i, arrayIdentType(elemType), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(longArrayVar);

        ListBuffer notZeroStmts = new ListBuffer<>();
        notZeroStmts.append(genWriteFieldName(mwc, attributeInfo, i));
        notZeroStmts.append(exec(mwc.jsonWriterMethod("writeArrayNull")));

        ListBuffer nullStmts = new ListBuffer<>();
        nullStmts.append(defIf(mwc.writeNulls, block(notZeroStmts.toList())));

        ListBuffer notNullStmts = new ListBuffer<>();
        notNullStmts.append(genWriteFieldName(mwc, attributeInfo, i));
        notNullStmts.append(exec(mwc.jsonWriterMethod("writeInt64", longArrayVar)));

        stmts.append(defIf(eq(longArrayVar, defNull()), block(nullStmts.toList()), block(notNullStmts.toList())));
        return stmts;
    }

    private ListBuffer genWriteBigDecimal(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();

        String type = attributeInfo.type.toString();
        JCTree.JCVariableDecl fieldValue = defVar("bigDecimal" + i, qualIdent(type), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(fieldValue);

        stmts.append(
                defIf(
                        or(notNull(fieldValue), isEnable(mwc.contextFeatures, WriteNulls)),
                        block(
                                genWriteFieldName(mwc, attributeInfo, i),
                                exec(mwc.jsonWriterMethod("writeDecimal", fieldValue))
                        )
                )
        );
        return stmts;
    }

    private ListBuffer genWriteBoolean(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();

        String type = attributeInfo.type.toString();
        JCTree.JCVariableDecl fieldValue = defVar("Boolean" + i, qualIdent(type), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(fieldValue);

        stmts.append(
                defIf(
                        or(notNull(fieldValue), isEnable(mwc.contextFeatures, WriteNulls)),
                        block(
                                genWriteFieldName(mwc, attributeInfo, i),
                                defIf(
                                        notNull(fieldValue),
                                    block(
                                        exec(mwc.jsonWriterMethod("writeBool", fieldValue))
                                    ),
                                    block(
                                            exec(mwc.jsonWriterMethod("writeNull"))
                                    )
                                )
                        )
                )
        );
        return stmts;
    }

    private ListBuffer genWriteInt32(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();

        String type = attributeInfo.type.toString();
        JCTree.JCVariableDecl integerVar = defVar("integer" + i, qualIdent(type), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(integerVar);

        stmts.append(
                defIf(
                        ne(integerVar, defNull()),
                        block(
                                genWriteFieldName(mwc, attributeInfo, i),
                                exec(mwc.jsonWriterMethod("writeInt32", integerVar))
                        ),
                        defIf(
                                isEnable(mwc.contextFeatures, WriteNulls, NullAsDefaultValue, WriteNullNumberAsZero),
                                block(
                                        genWriteFieldName(mwc, attributeInfo, i),
                                        exec(mwc.jsonWriterMethod("writeNumberNull"))
                                )
                        )
                )
        );
        return stmts;
    }

    private ListBuffer genWriteInt64(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();

        String type = attributeInfo.type.toString();
        JCTree.JCVariableDecl integerVar = defVar("long" + i, qualIdent(type), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(integerVar);

        stmts.append(
                defIf(
                        ne(integerVar, defNull()),
                        block(
                                genWriteFieldName(mwc, attributeInfo, i),
                                exec(mwc.jsonWriterMethod("writeInt64", integerVar))
                        ),
                        defIf(
                                isEnable(mwc.contextFeatures, WriteNulls, NullAsDefaultValue, WriteNullNumberAsZero),
                                block(
                                        genWriteFieldName(mwc, attributeInfo, i),
                                        exec(mwc.jsonWriterMethod("writeInt64Null"))
                                )
                        )
                )
        );
        return stmts;
    }

    private ListBuffer genWriteFloat(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        String type = attributeInfo.type.toString();
        JCTree.JCVariableDecl floatVar = defVar("float" + i, qualIdent(type), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(floatVar);

        ListBuffer notZeroStmts = new ListBuffer<>();
        notZeroStmts.append(genWriteFieldName(mwc, attributeInfo, i));
        notZeroStmts.append(exec(mwc.jsonWriterMethod("writeNumberNull")));

        ListBuffer nullStmts = new ListBuffer<>();
        JCTree.JCExpression writeAsStringBinary = literal(BrowserCompatible.mask | WriteBooleanAsNumber.mask | WriteNullStringAsEmpty.mask);
        nullStmts.append(defIf(ne(bitAnd(mwc.contextFeatures, writeAsStringBinary), 0), block(notZeroStmts.toList())));

        ListBuffer notNullStmts = new ListBuffer<>();
        notNullStmts.append(exec(method(field(names._this, fieldWriter(i)), "writeFloat", mwc.jsonWriter, floatVar)));

        stmts.append(defIf(eq(floatVar, defNull()), block(nullStmts.toList()), block(notNullStmts.toList())));
        return stmts;
    }

    private ListBuffer genWriteDouble(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        String type = attributeInfo.type.toString();
        JCTree.JCVariableDecl doubleVar = defVar("double" + i, qualIdent(type), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(doubleVar);

        ListBuffer notZeroStmts = new ListBuffer<>();
        notZeroStmts.append(genWriteFieldName(mwc, attributeInfo, i));
        notZeroStmts.append(exec(mwc.jsonWriterMethod("writeNumberNull")));

        ListBuffer nullStmts = new ListBuffer<>();
        JCTree.JCExpression writeAsStringBinary = literal(BrowserCompatible.mask | WriteBooleanAsNumber.mask | WriteNullStringAsEmpty.mask);
        nullStmts.append(defIf(ne(bitAnd(mwc.contextFeatures, writeAsStringBinary), 0), block(notZeroStmts.toList())));

        ListBuffer notNullStmts = new ListBuffer<>();
        notNullStmts.append(exec(method(field(names._this, fieldWriter(i)), "writeDouble", mwc.jsonWriter, doubleVar)));

        stmts.append(defIf(eq(doubleVar, defNull()), block(nullStmts.toList()), block(notNullStmts.toList())));
        return stmts;
    }

    private ListBuffer genWriteFieldValueString(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        final long writeNullFeatures = WriteNulls.mask | NullAsDefaultValue.mask | WriteNullStringAsEmpty.mask;
        final long writeNullAsEmptyFeatures = JSONWriter.Feature.NullAsDefaultValue.mask | JSONWriter.Feature.WriteNullStringAsEmpty.mask;

        JCTree.JCIdent fieldValue = ident("string" + i);
        ListBuffer stmts = new ListBuffer<>();
        stmts.append(genWriteFieldName(mwc, attributeInfo, i));
        stmts.append(
                defVar(
                        fieldValue.name,
                        qualIdent(attributeInfo.type.toString()),
                        genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType)
                )
        );
        stmts.append(
                defIf(
                        notNull(fieldValue),
                        block(exec(mwc.jsonWriterMethod("writeString", fieldValue))),
                        block(defIf(
                                ne(bitAnd(mwc.contextFeatures, writeNullFeatures), 0L),
                                defIf(
                                        ne(bitAnd(mwc.contextFeatures, writeNullAsEmptyFeatures), 0L),
                                        block(exec(mwc.jsonWriterMethod("writeString", ""))),
                                        block(exec(mwc.jsonWriterMethod("writeStringNull")))
                                )
                        ))
                )
        );
        return stmts;
    }

    private ListBuffer genWriteFieldValueEnum(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        String type = attributeInfo.type.toString();
        JCTree.JCVariableDecl enumVar = defVar("enum" + i, qualIdent(type), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(enumVar);
        stmts.append(
                defIf(
                        ne(enumVar, defNull()),
                        block(
                                method(
                                        field(names._this, fieldWriter(i)),
                                        "writeEnum",
                                        mwc.jsonWriter,
                                        ident(enumVar)
                                )
                        ),
                        defIf(
                                mwc.writeNulls,
                                block(
                                        genWriteFieldName(mwc, attributeInfo, i),
                                        exec(mwc.jsonWriterMethod("writeNull"))
                                )
                        )
                )
        );
        return stmts;
    }

    private ListBuffer genWriteFieldValueDate(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        String type = attributeInfo.type.toString();
        JCTree.JCVariableDecl dateVar = defVar("date" + i, qualIdent(type), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(dateVar);
        stmts.append(
                defIf(
                        ne(dateVar, defNull()),
                        block(
                                exec(method(
                                        field(names._this, fieldWriter(i)),
                                        "writeDate",
                                        mwc.jsonWriter,
                                        method(dateVar, "getTime")
                                ))
                        ),
                        defIf(
                                mwc.writeNulls,
                                block(
                                        genWriteFieldName(mwc, attributeInfo, i),
                                        exec(mwc.jsonWriterMethod("writeNull"))
                                )
                        )
                )
        );
        return stmts;
    }

    private ListBuffer genWriteFieldValueList(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        JCTree.JCVariableDecl listStrVar = defVar("listStr" + i, ident("String"), defNull());
        stmts.append(listStrVar);
        JCTree.JCVariableDecl listVar = defVar("list" + i, qualIdent("java.util.List"), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
        stmts.append(listVar);

        JCTree.JCLabeledStatement label = label("listLabel" + i);
        ListBuffer notNullStmts = new ListBuffer<>();
        ListBuffer labelStmts = new ListBuffer<>();

        ListBuffer notZeroStmts = new ListBuffer<>();
        notZeroStmts.append(defIf(eq(mwc.object, listVar),
                block(exec(mwc.jsonWriterMethod("writeReference", "..")),
                        defBreak(label))));

        notZeroStmts.append(exec(assign(listStrVar, mwc.jsonWriterMethod("setPath", field(ident(names._this), fieldWriter(i)), listVar))));

        notZeroStmts.append(defIf(ne(listStrVar, defNull()),
                block(exec(mwc.jsonWriterMethod("writeReference", listStrVar)),
                        exec(mwc.jsonWriterMethod("popPath", listVar)),
                        defBreak(label))));

        labelStmts.append(defIf(ne(bitAnd(mwc.contextFeatures, ReferenceDetection.mask), 0L), block(notZeroStmts.toList())));

        JCTree.JCBinary binary = eq(bitAnd(mwc.contextFeatures, NotWriteEmptyArray), 0L);
        JCTree.JCUnary unary = unary(JCTree.Tag.NOT, method(listVar, "isEmpty"));

        ListBuffer notEmptyStmts = new ListBuffer<>();
        notEmptyStmts.append(genWriteFieldName(mwc, attributeInfo, i));
        String type = attributeInfo.type.toString();
        if ("java.util.List".equals(type)) {
            notEmptyStmts.append(exec(mwc.jsonWriterMethod("writeString", listVar)));
        } else {
            notEmptyStmts.append(
                    exec(method(
                            field(names._this, fieldWriter(i)),
                            "writeListValue",
                            mwc.jsonWriter,
                            listVar
                    ))
            );
        }
        notEmptyStmts.append(exec(mwc.jsonWriterMethod("popPath", listVar)));
        labelStmts.append(defIf(or(binary, unary), block(notEmptyStmts.toList())));

        JCTree.JCExpression writeAsStringBinary = literal(WriteNulls.mask | NullAsDefaultValue.mask | WriteNullListAsEmpty.mask);
        JCTree.JCIf notNUllIf = defIf(ne(bitAnd(mwc.contextFeatures, writeAsStringBinary), 0L),
                block(
                        genWriteFieldName(mwc, attributeInfo, i),
                        exec(mwc.jsonWriterMethod("writeArrayNull"))
                )
        );

        label.body = block(labelStmts.toList());
        notNullStmts.append(label);
        stmts.append(defIf(ne(listVar, defNull()), block(notNullStmts.toList()), block(notNUllIf)));
        return stmts;
    }

    private JCTree.JCExpression genWriteFieldValue(AttributeInfo attributeInfo,
                                                   JCTree.JCIdent objectIdent,
                                                   JCTree.JCExpression beanType) {
        if (attributeInfo.getMethod != null) {
            return method(cast(beanType, objectIdent), attributeInfo.getMethod.getSimpleName().toString());
        } else {
            String name = attributeInfo.name;
            VariableElement field = attributeInfo.field;
            if (field instanceof Symbol.VarSymbol) {
                Name symbolName = ((Symbol.VarSymbol) field).name;
                if (symbolName != null) {
                    String fieldName = symbolName.toString();
                    if (!name.equals(fieldName)) {
                        name = fieldName;
                    }
                }
            }
            return field(cast(beanType, objectIdent), name);
        }
    }

    private ListBuffer genWriteFieldValueObject(
            MethodWriterContext mwc,
            AttributeInfo attributeInfo,
            int i
    ) {
        ListBuffer stmts = new ListBuffer<>();
        String type = attributeInfo.type.toString();
        if (type.contains("[")) {
            JCTree.JCVariableDecl objectStrVar = defVar("objectStr" + i, ident("String"), defNull());
            stmts.append(objectStrVar);
            JCTree.JCVariableDecl objectIntVar = defVar("objectInt" + i, 0);
            stmts.append(objectIntVar);
            JCTree.JCLabeledStatement outerLabel = label("objectOuterLabel" + i);
            ListBuffer outerLabelStmts = new ListBuffer<>();
            String elemType = ((ArrayType) attributeInfo.type).getComponentType().toString();
            JCTree.JCVariableDecl objectVar = defVar("object" + i, arrayIdentType(elemType), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
            outerLabelStmts.append(objectVar);

            JCTree.JCLabeledStatement innerLabel = label("objectInnerLabel" + i);
            ListBuffer innerLabelStmts = new ListBuffer<>();

            ListBuffer notNullStmts = new ListBuffer<>();
            JCTree.JCVariableDecl objectLongVar = defVar("objectLong" + i, TypeTag.LONG, bitAnd(mwc.contextFeatures, ReferenceDetection.mask));
            notNullStmts.append(objectLongVar);
            notNullStmts.append(exec(
                    assign(
                            objectIntVar,
                            ternary(
                                    eq(objectLongVar, 0),
                                    literal(0),
                                    ternary(lt(objectLongVar, 0), -1, 1)
                            )
                    )
            ));
            notNullStmts.append(defIf(eq(objectIntVar, 0), defBreak(innerLabel)));

            ListBuffer eqStmts = new ListBuffer<>();
            eqStmts.append(genWriteFieldName(mwc, attributeInfo, i));
            eqStmts.append(exec(mwc.jsonWriterMethod("writeReference", literal(".."))));
            eqStmts.append(defBreak(outerLabel));
            notNullStmts.append(defIf(eq(mwc.object, objectVar), block(eqStmts.toList())));

            notNullStmts.append(exec(
                    assign(
                            objectStrVar,
                            method(
                                    mwc.jsonWriter,
                                    "setPath",
                                    field(names._this, fieldWriter(i)),
                                    objectVar
                            )
                    )
            ));
            notNullStmts.append(defIf(eq(objectStrVar, defNull()), block(defBreak(innerLabel))));

            notNullStmts.append(genWriteFieldName(mwc, attributeInfo, i));
            notNullStmts.append(exec(mwc.jsonWriterMethod("writeReference", "..")));

            innerLabelStmts.append(defIf(ne(ident(objectVar), defNull()), block(notNullStmts.toList())));

            ListBuffer notZeroStmts = new ListBuffer<>();
            notZeroStmts.append(genWriteFieldName(mwc, attributeInfo, i));
            notZeroStmts.append(exec(mwc.jsonWriterMethod("writeArrayNull")));
            JCTree.JCExpression writeAsStringBinary = literal(WriteNulls.mask | NullAsDefaultValue.mask | WriteNullListAsEmpty.mask);
            innerLabelStmts.append(defIf(ne(bitAnd(mwc.contextFeatures, writeAsStringBinary), 0), block(notZeroStmts.toList())));

            innerLabelStmts.append(defBreak(outerLabel));

            innerLabel.body = block(innerLabelStmts.toList());
            outerLabelStmts.append(innerLabel);

            ListBuffer notEmptyArrayStmts = new ListBuffer<>();
            notEmptyArrayStmts.append(genWriteFieldName(mwc, attributeInfo, i));
            notEmptyArrayStmts.append(
                    exec(method(
                            method(field(names._this, fieldWriter(i)), "getObjectWriter", mwc.jsonWriter, field(arrayIdentType(type), names._class)),
                            "write",
                            mwc.jsonWriter,
                            ident(objectVar),
                            literal(attributeInfo),
                            method(objectVar, "getClass"),
                            literal(0L)
                    ))
            );
            notEmptyArrayStmts.append(
                    defIf(
                            ne(objectIntVar, 0),
                            exec(mwc.jsonWriterMethod("popPath", objectVar))
                    )
            );
            JCTree.JCBinary binary1 = eq(bitAnd(mwc.contextFeatures, NotWriteEmptyArray.mask), 0);
            JCTree.JCBinary binary2 = ne(field(objectVar, "length"), 0);
            outerLabelStmts.append(defIf(and(binary1, binary2), block(notEmptyArrayStmts.toList())));

            outerLabel.body = block(outerLabelStmts.toList());
            stmts.append(outerLabel);
        } else if (type.contains("java.util.Map<")) {
            JCTree.JCLabeledStatement outerLabel = label("objectOuterLabel" + i);
            ListBuffer outerLabelStmts = new ListBuffer<>();

            JCTree.JCVariableDecl objectStrVar = defVar("objectStr" + i, ident("String"), defNull());
            outerLabelStmts.append(objectStrVar);

            JCTree.JCVariableDecl objectVar = defVar("object" + i, getFieldValueType(type), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
            outerLabelStmts.append(objectVar);
            JCTree.JCVariableDecl objectIntVar = defVar("objectInt" + i, TypeTag.INT, literal(0));
            outerLabelStmts.append(objectIntVar);

            JCTree.JCLabeledStatement innerLabel = label("objectInnerLabel" + i);
            ListBuffer notNullStmts = new ListBuffer<>();
            notNullStmts.append(defIf(mwc.jsonWriterMethod("isIgnoreNoneSerializable", objectVar), block(defBreak(outerLabel))));

            JCTree.JCVariableDecl objectLongVar = defVar("objectLong" + i, TypeTag.LONG, bitAnd(mwc.contextFeatures, ReferenceDetection.mask));
            notNullStmts.append(objectLongVar);
            notNullStmts.append(exec(
                    assign(
                            objectIntVar,
                            ternary(
                                    eq(objectLongVar, 0),
                                    literal(0),
                                    ternary(lt(objectLongVar, 0), -1, 1)
                            )
                    )
            ));
            notNullStmts.append(defIf(eq(objectIntVar, 0), defBreak(innerLabel)));

            notNullStmts.append(defIf(eq(mwc.object, objectVar),
                    block(genWriteFieldName(mwc, attributeInfo, i),
                            exec(mwc.jsonWriterMethod("writeReference", literal(".."))),
                            defBreak(outerLabel)), null));

            notNullStmts.append(exec(assign(objectStrVar, mwc.jsonWriterMethod("setPath", field(ident(names._this), fieldWriter(i)), ident(objectVar)))));
            notNullStmts.append(defIf(eq(objectStrVar, defNull()), defBreak(innerLabel)));

            notNullStmts.append(genWriteFieldName(mwc, attributeInfo, i));
            notNullStmts.append(exec(mwc.jsonWriterMethod("writeReference", objectStrVar)));
            notNullStmts.append(exec(mwc.jsonWriterMethod("popPath", objectVar)));

            ListBuffer innerLabelStmts = new ListBuffer<>();
            innerLabelStmts.append(defIf(ne(ident(objectVar), defNull()), block(notNullStmts.toList())));

            JCTree.JCBinary binary = ne(bitAnd(mwc.contextFeatures, WriteMapNullValue.mask), 0);
            innerLabelStmts.append(
                    defIf(
                            binary,
                            block(
                                    genWriteFieldName(mwc, attributeInfo, i),
                                    exec(mwc.jsonWriterMethod("writeNull"))
                            )
                    )
            );
            innerLabelStmts.append(defBreak(outerLabel));
            innerLabel.body = block(innerLabelStmts.toList());

            outerLabelStmts.append(innerLabel);
            outerLabelStmts.append(genWriteFieldName(mwc, attributeInfo, i));
            outerLabelStmts.append(
                    exec(method(
                            method(
                                    field(names._this, fieldWriter(i)),
                                    "getObjectWriter",
                                    mwc.jsonWriter,
                                    method(objectVar, "getClass")
                            ),
                            "write",
                            mwc.jsonWriter, ident(objectVar),
                            literal(attributeInfo.name),
                            field(field(names._this, fieldWriter(i)), "fieldType"),
                            literal(0L))
                    )
            );
            outerLabelStmts.append(
                    defIf(
                            ne(objectIntVar, 0),
                            exec(mwc.jsonWriterMethod("popPath", objectVar))
                    )
            );

            outerLabel.body = block(outerLabelStmts.toList());
            stmts.append(outerLabel);
        } else {
            String WRITE_NULL_METHOD;
            if ("AtomicLongArray".equals(type)
                    || "AtomicIntegerArray".equals(type)
                    || "Collection.class.isAssignableFrom".equals(type)
                    || "isArray".equals(type)) {
                WRITE_NULL_METHOD = "writeArrayNull";
            } else if ("java.lang.Number".equals(type)) {
                WRITE_NULL_METHOD = "writeNumberNull";
            } else if ("Boolean".equals(type)) {
                WRITE_NULL_METHOD = "writeBooleanNull";
            } else if ("String".equals(type)
                    || "Appendable".equals(type)
                    || "StringBuffer".equals(type)
                    || "StringBuilder".equals(type)) {
                WRITE_NULL_METHOD = "writeStringNull";
            } else {
                WRITE_NULL_METHOD = "writeNull";
            }

            JCTree.JCLabeledStatement outerLabel = label("objectOuterLabel" + i);
            ListBuffer outerLabelStmts = new ListBuffer<>();

            JCTree.JCVariableDecl objectStrVar = defVar("objectStr" + i, ident("String"), defNull());
            outerLabelStmts.append(objectStrVar);

            JCTree.JCVariableDecl objectVar = defVar("object" + i, getFieldValueType(type), genWriteFieldValue(attributeInfo, mwc.object, mwc.beanType));
            outerLabelStmts.append(objectVar);
            JCTree.JCVariableDecl objectIntVar = defVar("objectInt" + i, TypeTag.INT, literal(0));
            outerLabelStmts.append(objectIntVar);

            JCTree.JCLabeledStatement innerLabel = label("objectInnerLabel" + i);
            ListBuffer notNullStmts = new ListBuffer<>();

            boolean noneSerializable = false;
            if (attributeInfo.type instanceof com.sun.tools.javac.code.Type.ClassType) {
                List interfacesField = ((com.sun.tools.javac.code.Type.ClassType) attributeInfo.type).interfaces_field;
                if (interfacesField == null || interfacesField.stream().noneMatch(f -> "java.io.Serializable".equals(f.toString()))) {
                    noneSerializable = true;
                }
            }

            if (noneSerializable) {
                notNullStmts.append(defIf(mwc.jsonWriterMethod("isIgnoreNoneSerializable", objectVar), defBreak(outerLabel)));
            }

            JCTree.JCVariableDecl objectLongVar = defVar("objectLong" + i, TypeTag.LONG, bitAnd(mwc.contextFeatures, ReferenceDetection.mask));
            notNullStmts.append(objectLongVar);
            notNullStmts.append(exec(assign(objectIntVar, ternary(eq(objectLongVar, 0), literal(0), ternary(lt(objectLongVar, 0), -1, 1)))));
            notNullStmts.append(defIf(eq(objectIntVar, 0), defBreak(innerLabel)));

            notNullStmts.append(defIf(eq(mwc.object, ident(objectVar)),
                    block(genWriteFieldName(mwc, attributeInfo, i),
                            exec(mwc.jsonWriterMethod("writeReference", "..")),
                            defBreak(outerLabel))));

            notNullStmts.append(
                    exec(
                            assign(
                                    objectStrVar,
                                    method(
                                            mwc.jsonWriter,
                                            "setPath",
                                            field(names._this, fieldWriter(i)),
                                            objectVar
                                    )
                            )
                    )
            );
            notNullStmts.append(defIf(eq(objectStrVar, defNull()), defBreak(innerLabel)));

            notNullStmts.append(genWriteFieldName(mwc, attributeInfo, i));
            notNullStmts.append(exec(mwc.jsonWriterMethod("writeReference", objectStrVar)));
            notNullStmts.append(exec(mwc.jsonWriterMethod("popPath", objectVar)));

            ListBuffer innerLabelStmts = new ListBuffer<>();
            innerLabelStmts.append(defIf(ne(objectVar, defNull()), block(notNullStmts.toList())));

            JCTree.JCBinary binary = ne(bitAnd(mwc.contextFeatures, WriteMapNullValue.mask), 0);
            innerLabelStmts.append(defIf(binary,
                    block(genWriteFieldName(mwc, attributeInfo, i),
                            exec(mwc.jsonWriterMethod(WRITE_NULL_METHOD))),
                    null));
            innerLabelStmts.append(defBreak(outerLabel));
            innerLabel.body = block(innerLabelStmts.toList());

            outerLabelStmts.append(innerLabel);
            outerLabelStmts.append(genWriteFieldName(mwc, attributeInfo, i));
            outerLabelStmts.append(
                    exec(method(
                            method(
                                    field(names._this, fieldWriter(i)),
                                    "getObjectWriter",
                                    mwc.jsonWriter,
                                    method(objectVar, "getClass")
                            ),
                            "write",
                            mwc.jsonWriter,
                            ident(objectVar),
                            literal(attributeInfo.name),
                            field(field(ident(names._this), fieldWriter(i)), "fieldType"),
                            literal(0L))
                    )
            );
            outerLabelStmts.append(
                    defIf(
                            ne(objectIntVar, 0),
                            exec(mwc.jsonWriterMethod("popPath", objectVar))
                    )
            );

            outerLabel.body = block(outerLabelStmts.toList());
            stmts.append(outerLabel);
        }
        return stmts;
    }

    private JCTree.JCVariableDecl getHashCode32Var(JCTree.JCExpression hashCode64) {
        JCTree.JCBinary usrBinary = binary(JCTree.Tag.USR, hashCode64, literal(32));
        JCTree.JCPrimitiveTypeTree intType = type(TypeTag.INT);
        JCTree.JCTypeCast hashCode32Cast = cast(intType, bitXor(hashCode64, parens(usrBinary)));
        return defVar("hashCode32", intType, hashCode32Cast);
    }

    private List genReadFieldValue(
            AttributeInfo attributeInfo,
            JCTree.JCIdent jsonReaderIdent,
            int i,
            StructInfo structInfo,
            JCTree.JCLabeledStatement loopLabel,
            JCTree.JCIdent objectIdent,
            JCTree.JCExpression beanType,
            boolean isJsonb) {
        JCTree.JCExpression valueExpr = null;
        ListBuffer stmts = new ListBuffer<>();
        String type = attributeInfo.type.toString();

        boolean referenceDetect = structInfo.referenceDetect;
        if (referenceDetect) {
            referenceDetect = isReference(type);
        }

        JCTree.JCFieldAccess fieldReaderField = field(ident(names._this), fieldReader(i));
        if (referenceDetect) {
            ListBuffer thenStmts = new ListBuffer<>();
            JCTree.JCMethodInvocation readReferenceMethod = method(jsonReaderIdent, "readReference");
            JCTree.JCVariableDecl refVar = defVar("ref", ident("String"), readReferenceMethod);
            thenStmts.append(refVar);
            JCTree.JCMethodInvocation addResolveTaskMethod = method(fieldReaderField, "addResolveTask", jsonReaderIdent, objectIdent, ident(refVar));
            thenStmts.append(exec(addResolveTaskMethod));
            thenStmts.append(defContinue(loopLabel));
            stmts.append(defIf(method(jsonReaderIdent, "isReference"), block(thenStmts.toList())));
        }

        String readDirectMethod = getReadDirectMethod(type);
        if (readDirectMethod != null) {
            valueExpr = method(jsonReaderIdent, readDirectMethod);
        } else {
            JCTree.JCExpression fieldValueType = getFieldValueType(type);
            JCTree.JCVariableDecl fieldValueVar = defVar(attributeInfo.name, fieldValueType);
            stmts.append(fieldValueVar);

            if (type.startsWith("java.util.List<")) {
                valueExpr = genFieldValueList(
                        type,
                        attributeInfo,
                        jsonReaderIdent,
                        fieldValueVar,
                        loopLabel,
                        stmts,
                        i,
                        referenceDetect,
                        fieldReaderField,
                        beanType,
                        isJsonb);
            } else if (type.startsWith("java.util.Map genRead243(
            java.util.List attributeInfos,
            JCTree.JCIdent jsonReaderIdent,
            StructInfo structInfo,
            JCTree.JCLabeledStatement loopLabel,
            JCTree.JCIdent objectIdent,
            JCTree.JCExpression beanType,
            boolean isJsonb) {
        IdentityHashMap readerIndexMap = new IdentityHashMap<>();
        Map> name0Map = new TreeMap<>();
        for (int i = 0; i < attributeInfos.size(); ++i) {
            AttributeInfo field = attributeInfos.get(i);
            readerIndexMap.put(field, i);

            byte[] fieldName = field.name.getBytes(StandardCharsets.UTF_8);
            byte[] name0Bytes = new byte[4];
            name0Bytes[0] = '"';
            if (fieldName.length == 2) {
                System.arraycopy(fieldName, 0, name0Bytes, 1, 2);
                name0Bytes[3] = '"';
            } else {
                System.arraycopy(fieldName, 0, name0Bytes, 1, 3);
            }

            int name0 = UNSAFE.getInt(name0Bytes, ARRAY_BYTE_BASE_OFFSET);

            java.util.List fieldReaders = name0Map.get(name0);
            if (fieldReaders == null) {
                fieldReaders = new ArrayList<>();
                name0Map.put(name0, fieldReaders);
            }
            fieldReaders.add(field);
        }

        int[] switchKeys = new int[name0Map.size()];
        Label[] labels = new Label[name0Map.size()];
        {
            Iterator it = name0Map.keySet().iterator();
            for (int i = 0; i < labels.length; i++) {
                labels[i] = new Label();
                switchKeys[i] = it.next();
            }
        }

        List stmts = List.nil();
        ListBuffer cases = new ListBuffer<>();

        JCTree.JCLabeledStatement switchLabel = label("_switch2");
        for (int i = 0; i < labels.length; i++) {
            int name0 = switchKeys[i];
            java.util.List fieldReaders = name0Map.get(name0);
            ListBuffer caseStmts = new ListBuffer<>();
            for (int j = 0; j < fieldReaders.size(); j++) {
                AttributeInfo fieldReader = fieldReaders.get(j);
                int fieldReaderIndex = readerIndexMap.get(fieldReader);
                byte[] fieldName = fieldReader.name.getBytes(StandardCharsets.UTF_8);
                int fieldNameLength = fieldName.length;
                JCTree.JCMethodInvocation nextIfMethod;
                switch (fieldNameLength) {
                    case 2:
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match2");
                        break;
                    case 3:
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match3");
                        break;
                    case 4:
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match4", literal(fieldName[3]));
                        break;
                    case 5: {
                        byte[] bytes4 = new byte[4];
                        bytes4[0] = fieldName[3];
                        bytes4[1] = fieldName[4];
                        bytes4[2] = '"';
                        bytes4[3] = ':';
                        int name1 = UNSAFE.getInt(bytes4, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match5", literal(name1));
                        break;
                    }
                    case 6: {
                        byte[] bytes4 = new byte[4];
                        bytes4[0] = fieldName[3];
                        bytes4[1] = fieldName[4];
                        bytes4[2] = fieldName[5];
                        bytes4[3] = '"';
                        int name1 = UNSAFE.getInt(bytes4, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match6", literal(name1));
                        break;
                    }
                    case 7: {
                        int name1 = UNSAFE.getInt(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match7", literal(name1));
                        break;
                    }
                    case 8: {
                        int name1 = UNSAFE.getInt(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match8", literal(name1), literal(fieldName[7]));
                        break;
                    }
                    case 9: {
                        byte[] bytes8 = new byte[8];
                        System.arraycopy(fieldName, 3, bytes8, 0, 6);
                        bytes8[6] = '"';
                        bytes8[7] = ':';
                        long name1 = UNSAFE.getLong(bytes8, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match9", literal(name1));
                        break;
                    }
                    case 10: {
                        byte[] bytes8 = new byte[8];
                        System.arraycopy(fieldName, 3, bytes8, 0, 7);
                        bytes8[7] = '"';
                        long name1 = UNSAFE.getLong(bytes8, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match10", literal(name1));
                        break;
                    }
                    case 11: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match11", literal(name1));
                        break;
                    }
                    case 12: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match12", literal(name1), literal(fieldName[11]));
                        break;
                    }
                    case 13: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        byte[] bytes4 = new byte[4];
                        bytes4[0] = fieldName[11];
                        bytes4[1] = fieldName[12];
                        bytes4[2] = '"';
                        bytes4[3] = ':';
                        int name2 = UNSAFE.getInt(bytes4, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match13", literal(name1), literal(name2));
                        break;
                    }
                    case 14: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        byte[] bytes4 = new byte[4];
                        bytes4[0] = fieldName[11];
                        bytes4[1] = fieldName[12];
                        bytes4[2] = fieldName[13];
                        bytes4[3] = '"';
                        int name2 = UNSAFE.getInt(bytes4, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match14", name1, name2);
                        break;
                    }
                    case 15: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        int name2 = UNSAFE.getInt(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match15", name1, name2);
                        break;
                    }
                    case 16: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        int name2 = UNSAFE.getInt(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match16", name1, name2, fieldName[15]);
                        break;
                    }
                    case 17: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        byte[] bytes8 = new byte[8];
                        System.arraycopy(fieldName, 11, bytes8, 0, 6);
                        bytes8[6] = '"';
                        bytes8[7] = ':';
                        long name2 = UNSAFE.getLong(bytes8, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match17", name1, name2);
                        break;
                    }
                    case 18: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        byte[] bytes8 = new byte[8];
                        System.arraycopy(fieldName, 11, bytes8, 0, 7);
                        bytes8[7] = '"';
                        long name2 = UNSAFE.getLong(bytes8, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match18", name1, name2);
                        break;
                    }
                    case 19: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match19", name1, name2);
                        break;
                    }
                    case 20: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match20", name1, name2, fieldName[19]);
                        break;
                    }
                    case 21: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        byte[] bytes4 = new byte[4];
                        bytes4[0] = fieldName[19];
                        bytes4[1] = fieldName[20];
                        bytes4[2] = '"';
                        bytes4[3] = ':';
                        int name3 = UNSAFE.getInt(bytes4, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match21", name1, name2, name3);
                        break;
                    }
                    case 22: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        byte[] bytes4 = new byte[4];
                        bytes4[0] = fieldName[19];
                        bytes4[1] = fieldName[20];
                        bytes4[2] = fieldName[21];
                        bytes4[3] = '"';
                        int name3 = UNSAFE.getInt(bytes4, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match22", name1, name2, name3);
                        break;
                    }
                    case 23: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        int name3 = UNSAFE.getInt(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match23", name1, name2, name3);
                        break;
                    }
                    case 24: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        int name3 = UNSAFE.getInt(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match24", name1, name2, name3, fieldName[23]);
                        break;
                    }
                    case 25: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        byte[] bytes8 = new byte[8];
                        System.arraycopy(fieldName, 19, bytes8, 0, 6);
                        bytes8[6] = '"';
                        bytes8[7] = ':';
                        long name3 = UNSAFE.getLong(bytes8, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match25", name1, name2, name3);
                        break;
                    }
                    case 26: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        byte[] bytes8 = new byte[8];
                        System.arraycopy(fieldName, 19, bytes8, 0, 7);
                        bytes8[7] = '"';
                        long name3 = UNSAFE.getLong(bytes8, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match26", name1, name2, name3);
                        break;
                    }
                    case 27: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match27", name1, name2, name3);
                        break;
                    }
                    case 28: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match28", name1, name2, name3, fieldName[27]);
                        break;
                    }
                    case 29: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        byte[] bytes4 = new byte[4];
                        bytes4[0] = fieldName[27];
                        bytes4[1] = fieldName[28];
                        bytes4[2] = '"';
                        bytes4[3] = ':';
                        int name4 = UNSAFE.getInt(bytes4, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match29", name1, name2, name3, name4);
                        break;
                    }
                    case 30: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        byte[] bytes4 = new byte[4];
                        bytes4[0] = fieldName[27];
                        bytes4[1] = fieldName[28];
                        bytes4[2] = fieldName[29];
                        bytes4[3] = '"';
                        int name4 = UNSAFE.getInt(bytes4, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match30", name1, name2, name3, name4);
                        break;
                    }
                    case 31: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        int name4 = UNSAFE.getInt(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match31", name1, name2, name3, name4);
                        break;
                    }
                    case 32: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        int name4 = UNSAFE.getInt(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match32", name1, name2, name3, name4, fieldName[31]);
                        break;
                    }
                    case 33: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        byte[] bytes8 = new byte[8];
                        System.arraycopy(fieldName, 27, bytes8, 0, 6);
                        bytes8[6] = '"';
                        bytes8[7] = ':';
                        long name4 = UNSAFE.getLong(bytes8, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match33", name1, name2, name3, name4);
                        break;
                    }
                    case 34: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        byte[] bytes8 = new byte[8];
                        System.arraycopy(fieldName, 27, bytes8, 0, 7);
                        bytes8[7] = '"';
                        long name4 = UNSAFE.getLong(bytes8, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match34", name1, name2, name3, name4);
                        break;
                    }
                    case 35: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        long name4 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match35", name1, name2, name3, name4);
                        break;
                    }
                    case 36: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        long name4 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match36", name1, name2, name3, name4, fieldName[35]);
                        break;
                    }
                    case 37: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        long name4 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);
                        byte[] bytes4 = new byte[4];
                        bytes4[0] = fieldName[35];
                        bytes4[1] = fieldName[36];
                        bytes4[2] = '"';
                        bytes4[3] = ':';
                        int name5 = UNSAFE.getInt(bytes4, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match37", name1, name2, name3, name4, name5);
                        break;
                    }
                    case 38: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        long name4 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);
                        byte[] bytes4 = new byte[4];
                        bytes4[0] = fieldName[35];
                        bytes4[1] = fieldName[36];
                        bytes4[2] = fieldName[37];
                        bytes4[3] = '"';
                        int name5 = UNSAFE.getInt(bytes4, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match38", name1, name2, name3, name4, name5);
                        break;
                    }
                    case 39: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        long name4 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);
                        int name5 = UNSAFE.getInt(fieldName, ARRAY_BYTE_BASE_OFFSET + 35);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match39", name1, name2, name3, name4, name5);
                        break;
                    }
                    case 40: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        long name4 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);
                        int name5 = UNSAFE.getInt(fieldName, ARRAY_BYTE_BASE_OFFSET + 35);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match40", name1, name2, name3, name4, name5, fieldName[39]);
                        break;
                    }
                    case 41: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        long name4 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);

                        byte[] bytes8 = new byte[8];
                        System.arraycopy(fieldName, 35, bytes8, 0, 6);
                        bytes8[6] = '"';
                        bytes8[7] = ':';
                        long name5 = UNSAFE.getLong(bytes8, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match41", name1, name2, name3, name4, name5);
                        break;
                    }
                    case 42: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        long name4 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);
                        byte[] bytes8 = new byte[8];
                        System.arraycopy(fieldName, 35, bytes8, 0, 7);
                        bytes8[7] = '"';
                        long name5 = UNSAFE.getLong(bytes8, ARRAY_BYTE_BASE_OFFSET);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match42", name1, name2, name3, name4, name5);
                        break;
                    }
                    case 43: {
                        long name1 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 3);
                        long name2 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 11);
                        long name3 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 19);
                        long name4 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 27);
                        long name5 = UNSAFE.getLong(fieldName, ARRAY_BYTE_BASE_OFFSET + 35);
                        nextIfMethod = method(jsonReaderIdent, "nextIfName4Match43", name1, name2, name3, name4, name5);
                        break;
                    }
                    default:
                        throw new IllegalStateException("fieldNameLength " + fieldNameLength);
                }
                List readFieldValueStmts = genReadFieldValue(
                        fieldReader,
                        jsonReaderIdent,
                        fieldReaderIndex,
                        structInfo,
                        loopLabel,
                        objectIdent,
                        beanType,
                        isJsonb);
                caseStmts.append(defIf(nextIfMethod, block(readFieldValueStmts)));
            }
            caseStmts.append(defBreak(switchLabel));
            cases.append(defCase(literal(name0), caseStmts.toList()));
        }
        switchLabel.body = defSwitch(method(jsonReaderIdent, "getRawInt"), cases.toList());
        stmts = stmts.append(switchLabel);
        return stmts;
    }

    private JCTree.JCExpression genFieldValueList(
            String type,
            AttributeInfo attributeInfo,
            JCTree.JCIdent jsonReaderIdent,
            JCTree.JCVariableDecl fieldValueVar,
            JCTree.JCLabeledStatement loopLabel,
            ListBuffer stmts,
            int i,
            boolean referenceDetect,
            JCTree.JCFieldAccess fieldReaderField,
            JCTree.JCExpression beanType,
            boolean isJsonb) {
        String itemType = type.substring(15, type.length() - 1);
        boolean itemTypeIsClass = itemType.indexOf('<') == -1;
        if (itemTypeIsClass) {
            JCTree.JCMethodInvocation nextIfNullMethod = method(jsonReaderIdent, "nextIfNull");
            stmts.append(defIf(nextIfNullMethod,
                    block(exec(assign(fieldValueVar, defNull()))),
                    block(exec(assign(fieldValueVar, newClass(null, null, qualIdent("java.util.ArrayList"), null, null))))));

            String readDirectMethod = getReadDirectMethod(itemType);
            JCTree.JCIdent itemReaderIdent = ident(fieldItemObjectReader(i));
            if (readDirectMethod == null) {
                JCTree.JCFieldAccess getItemObjectReaderField = field(fieldReaderField, "getItemObjectReader");
                JCTree.JCExpressionStatement getItemObjectReaderExec = exec(assign(itemReaderIdent, method(getItemObjectReaderField, jsonReaderIdent)));
                stmts.append(defIf(eq(itemReaderIdent, defNull()), block(getItemObjectReaderExec)));
            }

            if (referenceDetect) {
                referenceDetect = isReference(itemType);
            }

            JCTree.JCVariableDecl for_iVar;
            if ("i".equals(attributeInfo.name)) {
                for_iVar = defVar("j", TypeTag.INT, literal(0));
            } else {
                for_iVar = defVar("i", TypeTag.INT, literal(0));
            }
            JCTree.JCMethodInvocation nextIfArrayStartMethod = method(jsonReaderIdent, "nextIfArrayStart");
            JCTree.JCMethodInvocation nextIfArrayEndMethod = method(jsonReaderIdent, "nextIfArrayEnd");
            ListBuffer whileStmts = new ListBuffer<>();
            JCTree.JCExpression item;
            if (readDirectMethod != null) {
                item = method(jsonReaderIdent, readDirectMethod);
            } else {
                item = cast(
                        qualIdent(itemType),
                        method(
                                itemReaderIdent,
                                isJsonb ? "readJSONBObject" : "readObject",
                                jsonReaderIdent, defNull(),
                                defNull(),
                                literal(0L)
                        )
                );
            }

            if (referenceDetect) {
                JCTree.JCVariableDecl listItemVar = defVar(attributeInfo.name + "_item", getFieldValueType(itemType), item);
                ListBuffer isReferenceStmts = new ListBuffer<>();
                JCTree.JCMethodInvocation readReferenceMethod = method(jsonReaderIdent, "readReference");
                JCTree.JCVariableDecl refVar = defVar("ref", ident("String"), readReferenceMethod);
                isReferenceStmts.append(refVar);
                JCTree.JCMethodInvocation addResolveTaskMethod = method(
                        jsonReaderIdent,
                        "addResolveTask",
                        ident(fieldValueVar),
                        ident(for_iVar),
                        method(
                                qualIdent("com.alibaba.fastjson2.JSONPath"),
                                "of",
                                ident(refVar)
                        )
                );
                isReferenceStmts.append(exec(addResolveTaskMethod));
                isReferenceStmts.append(exec(method(fieldValueVar, "add", defNull())));
                isReferenceStmts.append(defContinue(loopLabel));
                whileStmts.append(defIf(method(jsonReaderIdent, "isReference"), block(isReferenceStmts.toList())));
                whileStmts.append(listItemVar);
                item = ident(listItemVar);
            }

            whileStmts.append(exec(method(fieldValueVar, "add", item)));

            ListBuffer condStmts = new ListBuffer<>();
            if (referenceDetect) {
                condStmts.append(forLoop(
                        List.of(for_iVar),
                        unary(JCTree.Tag.NOT, nextIfArrayEndMethod),
                        List.of(exec(unary(JCTree.Tag.PREINC, ident(for_iVar)))),
                        block(whileStmts.toList())));
            } else {
                condStmts.append(whileLoop(unary(JCTree.Tag.NOT, nextIfArrayEndMethod), block(whileStmts.toList())));
            }

            stmts.append(defIf(nextIfArrayStartMethod, block(condStmts.toList())));
            return ident(fieldValueVar.name);
        }
        return null;
    }

    private JCTree.JCExpression genFieldValueMap(
            String type,
            AttributeInfo attributeInfo,
            JCTree.JCIdent jsonReaderIdent,
            JCTree.JCVariableDecl fieldValueVar,
            JCTree.JCLabeledStatement loopLabel,
            ListBuffer stmts,
            int i,
            boolean referenceDetect,
            boolean isJsonb
    ) {
        String itemType = type.substring(31, type.length() - 1);
        JCTree.JCMethodInvocation nextIfNullMethod = method(jsonReaderIdent, "nextIfNull");

        boolean readDirect = supportReadDirect(itemType);

        ListBuffer elseStmts = new ListBuffer<>();
        JCTree.JCIdent itemReaderIdent = ident(fieldItemObjectReader(i));
        if (!readDirect) {
            JCTree.JCFieldAccess getObjectReaderField = field(jsonReaderIdent, "getObjectReader");
            JCTree.JCExpressionStatement getItemObjectReaderExec = exec(
                    assign(
                            itemReaderIdent,
                            method(getObjectReaderField, field(qualIdent(itemType), names._class))
                    )
            );
            elseStmts.append(defIf(eq(itemReaderIdent, defNull()), block(getItemObjectReaderExec)));
        }

        elseStmts.append(exec(assign(fieldValueVar, newClass(null, null, qualIdent("java.util.HashMap"), null, null))));

        JCTree.JCMethodInvocation nextIfObjectStartMethod = method(jsonReaderIdent, "nextIfObjectStart");
        elseStmts.append(exec(nextIfObjectStartMethod));

        JCTree.JCMethodInvocation nextIfObjectEndMethod = method(jsonReaderIdent, "nextIfObjectEnd");
        ListBuffer whileStmts = new ListBuffer<>();

        if (referenceDetect) {
            referenceDetect = isReference(itemType);
        }

        JCTree.JCExpression mapEntryValueExpr;
        if (readDirect) {
            mapEntryValueExpr = cast(qualIdent(itemType), method(jsonReaderIdent, getReadDirectMethod(itemType)));
        } else {
            mapEntryValueExpr = cast(
                    qualIdent(itemType),
                    method(
                            itemReaderIdent,
                            isJsonb ? "readJSONBObject" : "readObject",
                            jsonReaderIdent,
                            field(qualIdent(itemType), names._class),
                            literal(attributeInfo.name), ident("features")
                    )
            );
        }

        JCTree.JCExpression mapEntryKeyExpr = method(jsonReaderIdent, "readFieldName");

        if (referenceDetect) {
            JCTree.JCVariableDecl mapKey = defVar(attributeInfo.name + "_key", ident("String"), mapEntryKeyExpr);
            whileStmts.append(mapKey);
            JCTree.JCVariableDecl mapValue = defVar(attributeInfo.name + "_value", ident("String"));
            whileStmts.append(mapValue);

            ListBuffer isReferenceStmts = new ListBuffer<>();
            JCTree.JCMethodInvocation readReferenceMethod = method(jsonReaderIdent, "readReference");
            JCTree.JCVariableDecl refVar = defVar("ref", ident("String"), readReferenceMethod);
            isReferenceStmts.append(refVar);
            JCTree.JCMethodInvocation addResolveTaskMethod = method(
                    jsonReaderIdent,
                    "addResolveTask",
                    ident(fieldValueVar),
                    ident(mapKey),
                    method(qualIdent("com.alibaba.fastjson2.JSONPath"), "of", ident(refVar))
            );
            isReferenceStmts.append(exec(addResolveTaskMethod));
            whileStmts.append(
                    defIf(
                            method(jsonReaderIdent, "isReference"),
                            block(isReferenceStmts.toList()),
                            exec(method(fieldValueVar, "put", mapEntryKeyExpr, mapEntryValueExpr))
                    )
            );
        } else {
            whileStmts.append(exec(method(fieldValueVar, "put", mapEntryKeyExpr, mapEntryValueExpr)));
        }

        elseStmts.append(whileLoop(unary(JCTree.Tag.NOT, nextIfObjectEndMethod), block(whileStmts.toList())));

        elseStmts.append(exec(method(jsonReaderIdent, "nextIfComma")));

        stmts.append(defIf(nextIfNullMethod,
                block(exec(assign(fieldValueVar, defNull()))),
                block(elseStmts.toList())));

        return ident(fieldValueVar);
    }

    private void genSource(StructInfo structInfo, TreePath treePath) {
        Filer filer = processingEnv.getFiler();
        String fullQualifiedName = structInfo.binaryName;
        JavaFileObject converterFile = null;
        try {
            if (treePath.getCompilationUnit() instanceof JCTree.JCCompilationUnit) {
                JCTree.JCCompilationUnit compilationUnit = (JCTree.JCCompilationUnit) treePath.getCompilationUnit();
                converterFile = filer.createSourceFile(fullQualifiedName, structInfo.element);
                String fileName = converterFile.getName() + ".txt";
                Path filePath = Paths.get(fileName);
                String dirName = fileName.substring(0, fileName.lastIndexOf(File.separator));
                Path dirPath = Paths.get(dirName);
                if (!Files.exists(dirPath)) {
                    Files.createDirectories(dirPath);
                }
                if (!Files.exists(filePath)) {
                    Files.createFile(filePath);
                }
                try (Writer writer = new FileWriter(filePath.toString())) {
                    writer.write(compilationUnit.toString());
                } catch (Exception e) {
                    messager.printMessage(Diagnostic.Kind.WARNING, "Failed to generate source file for "
                            + fullQualifiedName + " caused by " + e.getMessage());
                }
            } else {
                messager.printMessage(Diagnostic.Kind.WARNING, "Failed to generate source file for "
                        + fullQualifiedName + " caused by invalid compilation unit");
            }
        } catch (Exception e) {
            messager.printMessage(Diagnostic.Kind.WARNING, "Failed to generate source file for "
                    + fullQualifiedName + " caused by " + e.getMessage());
        } finally {
            if (converterFile != null) {
                converterFile.delete();
            }
            if (filer instanceof JavacFiler) {
                ((JavacFiler) filer).close();
            }
        }
    }

    private void genReflect(Set reflects) {
        File file = null;
        try {
            file = new File("");
            String absolutePath = file.getAbsolutePath() +
                    File.separator +
                    Arrays.stream("src/main/resources/META-INF/native-image/reflect-config.json".split("/"))
                            .sequential()
                            .collect(Collectors.joining(File.separator));
            int idx = absolutePath.lastIndexOf(File.separator);
            Path dirPath = Paths.get(absolutePath.substring(0, idx));
            Path filePath = Paths.get(absolutePath);
            if (!Files.exists(dirPath)) {
                Files.createDirectories(dirPath);
            }
            if (!Files.exists(filePath)) {
                Files.createFile(filePath);
            }
            try (BufferedReader reader = Files.newBufferedReader(filePath)) {
                String content = reader.lines().collect(Collectors.joining());
                java.util.List reflectionMetadata = JSON.parseArray(content, ReflectionMetadata.class);
                if (reflectionMetadata != null) {
                    reflects.addAll(reflectionMetadata);
                }
                String jsonString = JSON.toJSONString(reflects, PrettyFormat);
                try (BufferedWriter writer = Files.newBufferedWriter(filePath)) {
                    writer.write(jsonString);
                }
            }
        } catch (Exception e) {
            messager.printMessage(Diagnostic.Kind.WARNING, "Failed to generate reflect-config.json caused by " + e.getMessage());
        } finally {
            if (file != null && file.exists()) {
                file.delete();
            }
        }
    }

    private boolean isDebug(JCTree.JCClassDecl beanClassDecl) {
        JCTree.JCExpression jsonCompiledIdent = qualIdent(JSONCompiled.class.getName());
        List annotations = beanClassDecl.mods.annotations;
        Optional jsonCompiledAnnoOpt = annotations.stream()
                .filter(a -> a.getAnnotationType().type.tsym.toString().equals(jsonCompiledIdent.type.tsym.toString()))
                .findAny();
        if (jsonCompiledAnnoOpt.isPresent()) {
            JCTree.JCAnnotation jsonCompiledAnno = jsonCompiledAnnoOpt.get();
            Optional jsonCompiledAsgOpt = jsonCompiledAnno.args.stream()
                    .map(a -> (JCTree.JCAssign) a)
                    .filter(a2 -> "debug".equals(a2.lhs.toString()))
                    .findAny();
            if (jsonCompiledAsgOpt.isPresent()) {
                JCTree.JCAssign jcAssign = jsonCompiledAsgOpt.get();
                return "true".equals(jcAssign.rhs.toString());
            }
        }
        return false;
    }

    private JCTree.JCExpression getFieldValueType(String type) {
        if (type.contains("[")) {
            return arrayIdentType(type);
        }
        if (type.contains("<")) {
            return collectionIdent(type);
        }
        return qualIdent(type);
    }

    private String findConverterName(StructInfo structInfo, String suffix) {
        int dotIndex;
        dotIndex = structInfo.binaryName.lastIndexOf('$');
        if (dotIndex != -1) {
            dotIndex = structInfo.binaryName.lastIndexOf('.');
        }
        String className = structInfo.binaryName.substring(dotIndex + 1);
        if (dotIndex == -1) {
            return className + suffix;
        }
        String packageName = structInfo.binaryName.substring(0, dotIndex);
        return packageName + '.' + className + suffix;
    }

    static final class MethodWriterContext {
        final JCTree.JCExpression beanType;
        final JCTree.JCIdent objectType;
        final JCTree.JCIdent jsonWriter = ident("jsonWriter");
        final JCTree.JCIdent object = ident("object");
        final JCTree.JCIdent fieldName = ident("fieldName");
        final JCTree.JCIdent fieldType = ident("fieldType");
        final JCTree.JCIdent features = ident("features");

        final JCTree.JCIdent contextFeatures = ident("contextFeatures");
        final JCTree.JCIdent notWriteDefaultValue = ident("notWriteDefaultValue");
        final JCTree.JCIdent nameDirect = ident("nameDirect");
        final JCTree.JCIdent writeNulls = ident("writeNulls");
        final boolean jsonb;

        public MethodWriterContext(JCTree.JCExpression beanType, JCTree.JCIdent objectType, boolean jsonb) {
            this.beanType = beanType;
            this.objectType = objectType;
            this.jsonb = jsonb;
        }

        void genVariantsMethodBefore(ListBuffer body) {
            JCTree.JCVariableDecl contextFeaturesVar
                    = defVar("contextFeatures", TypeTag.LONG, jsonWriterMethod("getFeatures"));
            body.append(contextFeaturesVar);

            JCTree.JCVariableDecl nameDirect;
            if (!jsonb) {
                nameDirect = defVar("nameDirect", TypeTag.BOOLEAN,
                        and(
                                not(jsonWriterField("useSingleQuote")),
                                isDisable(contextFeatures, UnquoteFieldName, UseSingleQuotes)
                        )
                );
            } else {
                nameDirect = defVar("nameDirect", TypeTag.BOOLEAN,
                        ternary(
                                notNull(jsonWriterField("symbolTable")),
                                literal(false),
                                isDisable(contextFeatures, WriteNameAsSymbol)
                        )
                );
            }
            body.append(nameDirect);

            body.append(
                    defVar("notWriteDefaultValue", TypeTag.BOOLEAN, isEnable(contextFeatures, NotWriteDefaultValue))
            );

            body.append(
                    defVar(
                            "writeNulls",
                            TypeTag.BOOLEAN,
                            ternary(this.notWriteDefaultValue, isEnable(contextFeatures, WriteNulls, NullAsDefaultValue), false)
                    )
            );
        }

        JCTree.JCExpression jsonWriterField(String fieldName) {
            return field(jsonWriter, fieldName);
        }

        JCTree.JCExpression jsonWriterMethod(String fieldName) {
            return method(jsonWriter, fieldName);
        }

        JCTree.JCExpression jsonWriterMethod(String fieldName, List args) {
            return method(jsonWriter, fieldName, args);
        }

        JCTree.JCExpression jsonWriterMethod(String fieldName, JCTree.JCExpression arg) {
            return method(jsonWriter, fieldName, List.of(arg));
        }

        JCTree.JCExpression jsonWriterMethod(String fieldName, JCTree.JCVariableDecl arg) {
            return method(jsonWriter, fieldName, List.of(ident(arg)));
        }

        JCTree.JCExpression jsonWriterMethod(String fieldName, String arg) {
            return method(jsonWriter, fieldName, List.of(literal(arg)));
        }

        JCTree.JCExpression jsonWriterMethod(
                String fieldName,
                JCTree.JCExpression arg0,
                JCTree.JCExpression arg1
        ) {
            return method(jsonWriter, fieldName, List.of(arg0, arg1));
        }

        JCTree.JCExpression jsonWriterMethod(
                String fieldName,
                JCTree.JCExpression arg0,
                JCTree.JCVariableDecl arg1
        ) {
            return method(jsonWriter, fieldName, List.of(arg0, ident(arg1)));
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy