ObjectReaderCreator.java

package com.alibaba.fastjson2.reader;

import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.annotation.JSONField;
import com.alibaba.fastjson2.codec.BeanInfo;
import com.alibaba.fastjson2.codec.FieldInfo;
import com.alibaba.fastjson2.function.*;
import com.alibaba.fastjson2.internal.asm.ASMUtils;
import com.alibaba.fastjson2.modules.ObjectReaderAnnotationProcessor;
import com.alibaba.fastjson2.modules.ObjectReaderModule;
import com.alibaba.fastjson2.schema.JSONSchema;
import com.alibaba.fastjson2.support.LambdaMiscCodec;
import com.alibaba.fastjson2.util.BeanUtils;
import com.alibaba.fastjson2.util.Fnv;
import com.alibaba.fastjson2.util.JDKUtils;
import com.alibaba.fastjson2.util.TypeUtils;

import java.lang.invoke.*;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;

import static com.alibaba.fastjson2.util.BeanUtils.SUPER;
import static com.alibaba.fastjson2.util.TypeUtils.*;

public class ObjectReaderCreator {
    public static final boolean JIT = !JDKUtils.ANDROID && !JDKUtils.GRAAL;
    public static final ObjectReaderCreator INSTANCE = new ObjectReaderCreator();

    protected final AtomicInteger jitErrorCount = new AtomicInteger();
    protected volatile Throwable jitErrorLast;

    protected static final Map<Class, LambdaSetterInfo> methodTypeMapping = new HashMap<>();

    static class LambdaSetterInfo {
        final Class fieldClass;
        final MethodType sameMethodMethod;
        final MethodType methodType;
        final MethodType invokedType;

        LambdaSetterInfo(Class fieldClass, Class functionClass) {
            this.fieldClass = fieldClass;
            this.sameMethodMethod = MethodType.methodType(void.class, Object.class, fieldClass);
            this.methodType = MethodType.methodType(void.class, fieldClass);
            this.invokedType = MethodType.methodType(functionClass);
        }
    }

    static {
        methodTypeMapping.put(boolean.class, new LambdaSetterInfo(boolean.class, ObjBoolConsumer.class));
        methodTypeMapping.put(byte.class, new LambdaSetterInfo(byte.class, ObjByteConsumer.class));
        methodTypeMapping.put(short.class, new LambdaSetterInfo(short.class, ObjShortConsumer.class));
        methodTypeMapping.put(int.class, new LambdaSetterInfo(int.class, ObjIntConsumer.class));
        methodTypeMapping.put(long.class, new LambdaSetterInfo(long.class, ObjLongConsumer.class));
        methodTypeMapping.put(char.class, new LambdaSetterInfo(char.class, ObjCharConsumer.class));
        methodTypeMapping.put(float.class, new LambdaSetterInfo(float.class, ObjFloatConsumer.class));
        methodTypeMapping.put(double.class, new LambdaSetterInfo(double.class, ObjDoubleConsumer.class));
    }

    public <T> ObjectReader<T> createObjectReaderNoneDefaultConstructor(Constructor constructor, String... paramNames) {
        Function<Map<Long, Object>, T> function = createFunction(constructor, paramNames);
        Class declaringClass = constructor.getDeclaringClass();
        FieldReader[] fieldReaders = createFieldReaders(
                JSONFactory.getDefaultObjectReaderProvider(),
                declaringClass,
                declaringClass,
                constructor,
                constructor.getParameters(),
                paramNames
        );
        return createObjectReaderNoneDefaultConstructor(declaringClass, function, fieldReaders);
    }

    public <T> ObjectReader<T> createObjectReaderNoneDefaultConstructor(
            Class objectClass,
            Function<Map<Long, Object>, T> creator,
            FieldReader... fieldReaders
    ) {
        return new ObjectReaderNoneDefaultConstructor(
                objectClass,
                null,
                null,
                0,
                creator,
                null,
                null,
                fieldReaders,
                null,
                null,
                null
        );
    }

    public <T> ObjectReader<T> createObjectReaderFactoryMethod(Method factoryMethod, String... paramNames) {
        Function<Map<Long, Object>, Object> factoryFunction = createFactoryFunction(factoryMethod, paramNames);
        FieldReader[] fieldReaders = createFieldReaders(
                JSONFactory.getDefaultObjectReaderProvider(),
                null,
                null,
                factoryMethod,
                factoryMethod.getParameters(),
                paramNames
        );
        return new ObjectReaderNoneDefaultConstructor(
                null,
                null,
                null,
                0,
                factoryFunction,
                null,
                paramNames,
                fieldReaders,
                null,
                null,
                null
        );
    }

    public FieldReader[] createFieldReaders(
            ObjectReaderProvider provider,
            Class objectClass,
            Type objectType,
            Executable owner,
            Parameter[] parameters,
            String... paramNames
    ) {
        Class<?> declaringClass = null;
        if (owner != null) {
            declaringClass = owner.getDeclaringClass();
        }

        FieldReader[] fieldReaders = new FieldReader[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            FieldInfo fieldInfo = new FieldInfo();

            Parameter parameter = parameters[i];
            String paramName;
            if (i < paramNames.length) {
                paramName = paramNames[i];
            } else {
                paramName = parameter.getName();
            }

            if (owner instanceof Constructor) {
                provider.getFieldInfo(fieldInfo, declaringClass, (Constructor) owner, i, parameter);
            }

            if (owner instanceof Constructor) {
                Field field = BeanUtils.getDeclaredField(declaringClass, paramName);
                if (field != null) {
                    provider.getFieldInfo(fieldInfo, declaringClass, field);
                }
            }

            String fieldName;
            if (fieldInfo.fieldName == null || fieldInfo.fieldName.isEmpty()) {
                fieldName = paramName;
            } else {
                fieldName = fieldInfo.fieldName;
            }

            if (fieldName == null) {
                fieldName = "arg" + i;
            }

            if (paramName == null) {
                paramName = "arg" + i;
            }

            ObjectReader initReader = getInitReader(provider, parameter.getParameterizedType(), parameter.getType(), fieldInfo);
            Type paramType = parameter.getParameterizedType();
            fieldReaders[i] = createFieldReaderParam(
                    null,
                    null,
                    fieldName,
                    i,
                    fieldInfo.features,
                    fieldInfo.format,
                    paramType,
                    parameter.getType(),
                    paramName,
                    declaringClass,
                    parameter,
                    null,
                    initReader
            );
        }
        return fieldReaders;
    }

    public <T> Function<Map<Long, Object>, T> createFactoryFunction(Method factoryMethod, String... paramNames) {
        factoryMethod.setAccessible(true);
        return new FactoryFunction(factoryMethod, paramNames);
    }

    public <T> Function<Map<Long, Object>, T> createFunction(Constructor constructor, String... paramNames) {
        constructor.setAccessible(true);
        return new ConstructorFunction(
                null,
                constructor,
                null,
                null,
                null,
                paramNames
        );
    }

    public <T> Function<Map<Long, Object>, T> createFunction(
            Constructor constructor,
            Constructor markerConstructor,
            String... paramNames
    ) {
        if (markerConstructor == null) {
            constructor.setAccessible(true);
        } else {
            markerConstructor.setAccessible(true);
        }
        return new ConstructorFunction(
                null,
                constructor,
                null,
                null,
                markerConstructor,
                paramNames
        );
    }

    public <T> ObjectReader<T> createObjectReader(
            Class<T> objectClass,
            FieldReader... fieldReaders
    ) {
        return createObjectReader(
                objectClass,
                null,
                0,
                null,
                createSupplier(objectClass),
                null,
                fieldReaders
        );
    }

    public <T> ObjectReader<T> createObjectReader(
            Class<T> objectClass,
            Supplier<T> defaultCreator,
            FieldReader... fieldReaders
    ) {
        return createObjectReader(objectClass, null, 0, null, defaultCreator, null, fieldReaders);
    }

    public <T> ObjectReader<T> createObjectReaderSeeAlso(
            Class<T> objectType,
            Class[] seeAlso,
            FieldReader... fieldReaders
    ) {
        Supplier<T> instanceSupplier = createSupplier(objectType);
        return new ObjectReaderSeeAlso(
                objectType,
                instanceSupplier,
                "@type",
                seeAlso,
                null,
                null,
                fieldReaders
        );
    }

    public <T> ObjectReader<T> createObjectReaderSeeAlso(
            Class<T> objectClass,
            String typeKey,
            Class[] seeAlso,
            String[] seeAlsoNames,
            FieldReader... fieldReaders
    ) {
        Supplier<T> creator = createSupplier(objectClass);
        return new ObjectReaderSeeAlso(
                objectClass,
                creator,
                typeKey,
                seeAlso,
                seeAlsoNames,
                null,
                fieldReaders
        );
    }

    /**
     * @since 2.0.24
     */
    public <T> ObjectReader<T> createObjectReaderSeeAlso(
            Class<T> objectClass,
            String typeKey,
            Class[] seeAlso,
            String[] seeAlsoNames,
            Class seeAlsoDefault,
            FieldReader... fieldReaders
    ) {
        Supplier<T> creator = createSupplier(objectClass);
        return new ObjectReaderSeeAlso(
                objectClass,
                creator,
                typeKey,
                seeAlso,
                seeAlsoNames,
                seeAlsoDefault,
                fieldReaders
        );
    }

    public <T> ObjectReader<T> createObjectReaderSeeAlso(
            Class<T> objectType,
            Supplier<T> defaultCreator,
            String typeKey,
            Class[] seeAlso,
            String[] seeAlsoNames,
            FieldReader... fieldReaders
    ) {
        return new ObjectReaderSeeAlso(
                objectType,
                defaultCreator,
                typeKey,
                seeAlso,
                seeAlsoNames,
                null,
                fieldReaders
        );
    }

    protected <T> ObjectReader<T> createObjectReaderWithBuilder(
            Class<T> objectClass,
            Type objectType,
            ObjectReaderProvider provider,
            BeanInfo beanInfo
    ) {
        Function<Object, Object> builderFunction = null;
        if (beanInfo.buildMethod != null) {
            builderFunction = createBuildFunction(beanInfo.buildMethod);
        }
        Class builderClass = beanInfo.builder;

        String builderWithPrefix = beanInfo.builderWithPrefix;
        if (builderWithPrefix == null || builderWithPrefix.isEmpty()) {
            builderWithPrefix = "with";
        }
        int builderWithPrefixLenth = builderWithPrefix.length();

        Map<String, FieldReader> fieldReaders = new LinkedHashMap<>();

        final String prefix = builderWithPrefix;
        final FieldInfo fieldInfo = new FieldInfo();
        BeanUtils.setters(builderClass, false, method -> {
            fieldInfo.init();
            provider.getFieldInfo(fieldInfo, objectClass, method);

            if (fieldInfo.ignore) {
                return;
            }

            String methodName = method.getName();
            String fieldName;
            if (fieldInfo.fieldName == null || fieldInfo.fieldName.isEmpty()) {
                final int methodNameLength = methodName.length();
                boolean prefixNotMach = methodNameLength <= prefix.length() || !methodName.startsWith(prefix);
                if (prefixNotMach) {
                    if ((method.getDeclaringClass() != Object.class && method.getReturnType() == builderClass)
                            && (method.getAnnotation(JSONField.class) != null
                            || (beanInfo.readerFeatures & JSONReader.Feature.SupportSmartMatch.mask) != 0)
                    ) {
                        fieldName = methodName;
                    } else {
                        return;
                    }
                } else {
                    fieldName = BeanUtils.setterName(methodName, builderWithPrefixLenth);
                }
            } else {
                fieldName = fieldInfo.fieldName;
            }

            if (method.getParameterCount() == 0) {
                FieldReader fieldReader = createFieldReaderMethod(
                        builderClass,
                        builderClass,
                        fieldName,
                        fieldInfo.ordinal,
                        fieldInfo.features,
                        fieldInfo.format,
                        fieldInfo.locale,
                        fieldInfo.defaultValue,
                        fieldInfo.schema,
                        method.getGenericReturnType(),
                        method.getReturnType(),
                        method,
                        null
                );
                FieldReader origin = fieldReaders.putIfAbsent(fieldName,
                        fieldReader
                );
                if (origin != null && origin.compareTo(fieldReader) > 0) {
                    fieldReaders.put(fieldName, fieldReader);
                }
                return;
            }

            Type fieldType = method.getGenericParameterTypes()[0];
            Class fieldClass = method.getParameterTypes()[0];

            method.setAccessible(true);

            FieldReader fieldReader = createFieldReaderMethod(
                    builderClass,
                    objectType,
                    fieldName,
                    fieldInfo.ordinal,
                    fieldInfo.features,
                    fieldInfo.format,
                    fieldInfo.locale,
                    fieldInfo.defaultValue,
                    fieldInfo.schema,
                    fieldType,
                    fieldClass,
                    method,
                    null);

            FieldReader origin = fieldReaders.putIfAbsent(fieldName, fieldReader);
            if (origin != null && origin.compareTo(fieldReader) > 0) {
                fieldReaders.put(fieldName, fieldReader);
            }

            if (fieldInfo.alternateNames != null) {
                for (String alternateName : fieldInfo.alternateNames) {
                    if (fieldName.equals(alternateName)) {
                        continue;
                    }

                    fieldReaders
                            .putIfAbsent(alternateName,
                                    createFieldReaderMethod(
                                            builderClass,
                                            objectType,
                                            alternateName,
                                            fieldInfo.ordinal,
                                            fieldInfo.features,
                                            fieldInfo.format,
                                            fieldInfo.locale,
                                            fieldInfo.defaultValue,
                                            fieldInfo.schema,
                                            fieldType,
                                            fieldClass,
                                            method,
                                            null));
                }
            }
        });

        FieldReader[] fieldReaderArray = new FieldReader[fieldReaders.size()];
        fieldReaders.values().toArray(fieldReaderArray);
        Arrays.sort(fieldReaderArray);

        Supplier instanceSupplier = createSupplier(builderClass);
        return createObjectReader(builderClass, 0, instanceSupplier, builderFunction, fieldReaderArray);
    }

    protected <T> ObjectReader<T> createObjectReaderWithCreator(
            Class<T> objectClass,
            Type objectType,
            ObjectReaderProvider provider,
            BeanInfo beanInfo
    ) {
        FieldInfo fieldInfo = new FieldInfo();

        Map<String, FieldReader> fieldReaders = new LinkedHashMap<>();

        Class declaringClass;
        Parameter[] parameters;
        String[] paramNames;
        if (beanInfo.creatorConstructor != null) {
            parameters = beanInfo.creatorConstructor.getParameters();
            declaringClass = beanInfo.creatorConstructor.getDeclaringClass();
            paramNames = ASMUtils.lookupParameterNames(beanInfo.creatorConstructor);
        } else {
            parameters = beanInfo.createMethod.getParameters();
            declaringClass = beanInfo.createMethod.getDeclaringClass();
            paramNames = ASMUtils.lookupParameterNames(beanInfo.createMethod);
        }

        for (int i = 0; i < parameters.length; i++) {
            fieldInfo.init();

            Parameter parameter = parameters[i];

            if (beanInfo.creatorConstructor != null) {
                provider.getFieldInfo(fieldInfo, objectClass, beanInfo.creatorConstructor, i, parameter);
            } else {
                provider.getFieldInfo(fieldInfo, objectClass, beanInfo.createMethod, i, parameter);
            }

            if (parameters.length == 1 && (fieldInfo.features & FieldInfo.VALUE_MASK) != 0) {
                break;
            }

            String fieldName = fieldInfo.fieldName;
            if (fieldName == null || fieldName.isEmpty()) {
                if (beanInfo.createParameterNames != null && i < beanInfo.createParameterNames.length) {
                    fieldName = beanInfo.createParameterNames[i];
                }

                if (fieldName == null || fieldName.isEmpty()) {
                    fieldName = parameter.getName();
                }
            }
            if (fieldName == null || fieldName.isEmpty()) {
                fieldName = paramNames[i];
            } else if (fieldName.startsWith("arg")) {
                if (paramNames != null && paramNames.length > i) {
                    fieldName = paramNames[i];
                }
            } else {
                paramNames[i] = fieldName;
            }

            String finalFieldName = fieldName;
            Class<?> paramClass = parameter.getType();
            BeanUtils.getters(objectClass, method -> {
                if (method.getReturnType() != paramClass) {
                    return;
                }

                FieldInfo methodFieldInfo = new FieldInfo();
                provider.getFieldInfo(methodFieldInfo, objectClass, method);
                String methodFieldName = methodFieldInfo.fieldName;
                if (methodFieldName == null) {
                    methodFieldName = BeanUtils.getterName(method, PropertyNamingStrategy.CamelCase.name());
                }

                if (methodFieldInfo.readUsing != null && finalFieldName.equals(methodFieldName)
                ) {
                    fieldInfo.readUsing = methodFieldInfo.readUsing;
                }
            });

            if (fieldName == null || fieldName.isEmpty()) {
                fieldName = "arg" + i;
            }

            Type paramType = parameter.getParameterizedType();
            ObjectReader initReader = getInitReader(provider, paramType, paramClass, fieldInfo);
            FieldReader fieldReaderParam = createFieldReaderParam(
                    objectClass,
                    objectType,
                    fieldName,
                    i,
                    fieldInfo.features,
                    fieldInfo.format,
                    paramType,
                    paramClass,
                    fieldName,
                    declaringClass,
                    parameter,
                    null,
                    initReader
            );
            fieldReaders.put(fieldName, fieldReaderParam);

            if (fieldInfo.alternateNames != null) {
                for (String alternateName : fieldInfo.alternateNames) {
                    if (fieldName.equals(alternateName)) {
                        continue;
                    }

                    fieldReaders.putIfAbsent(alternateName,
                            createFieldReaderParam(
                                    objectClass,
                                    objectType,
                                    alternateName,
                                    i,
                                    fieldInfo.features,
                                    fieldInfo.format,
                                    paramType,
                                    paramClass,
                                    fieldName,
                                    declaringClass,
                                    parameter,
                                    null
                            ));
                }
            }
        }

        if (parameters.length == 1 && (fieldInfo.features & FieldInfo.VALUE_MASK) != 0) {
            Type valueType = beanInfo.creatorConstructor == null
                    ? beanInfo.createMethod.getGenericParameterTypes()[0]
                    : beanInfo.creatorConstructor.getGenericParameterTypes()[0];
            Class valueClass = beanInfo.creatorConstructor == null
                    ? beanInfo.createMethod.getParameterTypes()[0]
                    : beanInfo.creatorConstructor.getParameterTypes()[0];

            JSONSchema jsonSchema = null;
            if (fieldInfo.schema != null && !fieldInfo.schema.isEmpty()) {
                JSONObject object = JSON.parseObject(fieldInfo.schema);
                if (!object.isEmpty()) {
                    jsonSchema = JSONSchema.of(object, valueClass);
                }
            }

            Object defaultValue = fieldInfo.defaultValue;
            if (defaultValue != null && defaultValue.getClass() != valueClass) {
                Function typeConvert = JSONFactory
                        .getDefaultObjectReaderProvider()
                        .getTypeConvert(defaultValue.getClass(), valueType);
                if (typeConvert != null) {
                    defaultValue = typeConvert.apply(defaultValue);
                } else {
                    throw new JSONException("illegal defaultValue : " + defaultValue + ", class " + valueClass.getName());
                }
            }

            boolean jit = JIT || (fieldInfo.features & FieldInfo.JIT) != 0 || (beanInfo.readerFeatures & FieldInfo.JIT) != 0;
            Function function = null;
            if (defaultValue == null && jit) {
                if (valueClass == int.class) {
                    IntFunction intFunction = null;
                    if (beanInfo.creatorConstructor != null) {
                        intFunction = createIntFunction(beanInfo.creatorConstructor);
                    } else if (beanInfo.createMethod != null) {
                        intFunction = createIntFunction(beanInfo.createMethod);
                    }

                    if (intFunction != null) {
                        return ObjectReaderImplValueInt.of(objectClass, fieldInfo.features, jsonSchema, intFunction);
                    }
                } else if (valueClass == String.class) {
                    if (beanInfo.creatorConstructor != null) {
                        function = createStringFunction(beanInfo.creatorConstructor);
                    } else if (beanInfo.createMethod != null) {
                        function = createStringFunction(beanInfo.createMethod);
                    }

                    if (function != null) {
                        return ObjectReaderImplValueString.of(objectClass, fieldInfo.features, jsonSchema, function);
                    }
                }
            }

            if (jit && !valueClass.isPrimitive()) {
                if (beanInfo.creatorConstructor != null) {
                    function = createValueFunction(beanInfo.creatorConstructor, valueClass);
                } else if (beanInfo.createMethod != null) {
                    function = createValueFunction(beanInfo.createMethod, valueClass);
                }
            }

            return new ObjectReaderImplValue(
                    objectClass,
                    valueType,
                    valueClass,
                    fieldInfo.features,
                    fieldInfo.format,
                    defaultValue,
                    jsonSchema,
                    beanInfo.creatorConstructor,
                    beanInfo.createMethod,
                    function
            );
        }

        Function<Map<Long, Object>, Object> function;
        if (beanInfo.creatorConstructor != null) {
            function = createFunction(beanInfo.creatorConstructor, beanInfo.markerConstructor, paramNames);
        } else {
            function = createFactoryFunction(beanInfo.createMethod, paramNames);
        }

        FieldReader[] fieldReaderArray = new FieldReader[fieldReaders.size()];
        fieldReaders.values().toArray(fieldReaderArray);
        Arrays.sort(fieldReaderArray);

        FieldReader[] setterFieldReaders = createFieldReaders(objectClass, objectType);
        Arrays.sort(setterFieldReaders);

        boolean[] flags = null;
        int maskCount = 0;
        if (setterFieldReaders != null) {
            for (int i = 0; i < setterFieldReaders.length; i++) {
                FieldReader setterFieldReader = setterFieldReaders[i];
                if (fieldReaders.containsKey(setterFieldReader.fieldName)) {
                    if (flags == null) {
                        flags = new boolean[setterFieldReaders.length];
                    }
                    flags[i] = true;
                    maskCount++;
                }
            }
            if (maskCount > 0) {
                FieldReader[] array = new FieldReader[setterFieldReaders.length - maskCount];
                int index = 0;
                for (int i = 0; i < setterFieldReaders.length; i++) {
                    if (!flags[i]) {
                        array[index++] = setterFieldReaders[i];
                    }
                }
                setterFieldReaders = array;
            }
        }

        return (ObjectReader<T>) new ObjectReaderNoneDefaultConstructor(
                objectClass,
                beanInfo.typeKey,
                beanInfo.typeName,
                beanInfo.readerFeatures,
                function,
                null,
                paramNames,
                fieldReaderArray,
                setterFieldReaders,
                beanInfo.seeAlso,
                beanInfo.seeAlsoNames
        );
    }

    public <T> ObjectReader<T> createObjectReader(
            Class<T> objectClass,
            long features,
            Supplier<T> defaultCreator,
            Function buildFunction,
            FieldReader... fieldReaders
    ) {
        return createObjectReader(objectClass, null, features, null, defaultCreator, buildFunction, fieldReaders);
    }

    public <T> ObjectReader<T> createObjectReader(
            Class<T> objectClass,
            String typeKey,
            long features,
            JSONSchema schema,
            Supplier<T> defaultCreator,
            Function buildFunction,
            FieldReader... fieldReaders
    ) {
        if (objectClass != null) {
            int modifiers = objectClass.getModifiers();
            if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers)) {
                return new ObjectReaderAdapter(objectClass, typeKey, null, features, schema, defaultCreator, buildFunction, fieldReaders);
            }
        }

        switch (fieldReaders.length) {
            case 1:
                return new ObjectReader1(
                        objectClass,
                        features,
                        schema,
                        defaultCreator,
                        buildFunction,
                        fieldReaders[0]
                );
            case 2:
                return new ObjectReader2(
                        objectClass,
                        features,
                        schema,
                        defaultCreator,
                        buildFunction,
                        fieldReaders[0],
                        fieldReaders[1]);
            case 3:
                return new ObjectReader3(
                        objectClass,
                        defaultCreator,
                        features,
                        schema,
                        buildFunction,
                        fieldReaders[0],
                        fieldReaders[1],
                        fieldReaders[2]
                );
            case 4:
                return new ObjectReader4(
                        objectClass,
                        features,
                        schema,
                        defaultCreator,
                        buildFunction,
                        fieldReaders[0],
                        fieldReaders[1],
                        fieldReaders[2],
                        fieldReaders[3]
                );
            case 5:
                return new ObjectReader5(
                        objectClass,
                        defaultCreator,
                        features,
                        schema,
                        buildFunction,
                        fieldReaders[0],
                        fieldReaders[1],
                        fieldReaders[2],
                        fieldReaders[3],
                        fieldReaders[4]
                );
            case 6:
                return new ObjectReader6(
                        objectClass,
                        defaultCreator,
                        features,
                        schema,
                        buildFunction,
                        fieldReaders[0],
                        fieldReaders[1],
                        fieldReaders[2],
                        fieldReaders[3],
                        fieldReaders[4],
                        fieldReaders[5]
                );
            default:
                return new ObjectReaderAdapter(objectClass, typeKey, null, features, schema, defaultCreator, buildFunction, fieldReaders);
        }
    }

    public <T> ObjectReader<T> createObjectReader(Type objectType) {
        if (objectType instanceof Class) {
            return createObjectReader((Class<T>) objectType);
        }

        Class<T> objectClass = (Class<T>) TypeUtils.getMapping(objectType);
        FieldReader[] fieldReaderArray = createFieldReaders(objectClass, objectType);
        return createObjectReader(
                objectClass,
                createSupplier(objectClass),
                fieldReaderArray);
    }

    public <T> ObjectReader<T> createObjectReader(Class<T> objectType) {
        return createObjectReader(
                objectType,
                objectType,
                false,
                JSONFactory.getDefaultObjectReaderProvider()
        );
    }

    public <T> ObjectReader<T> createObjectReader(Class<T> objectType, boolean fieldBased) {
        return createObjectReader(
                objectType,
                objectType,
                fieldBased,
                JSONFactory.getDefaultObjectReaderProvider()
        );
    }

    public <T> ObjectReader<T> createObjectReader(
            Class<T> objectClass,
            Type objectType,
            boolean fieldBased,
            ObjectReaderProvider provider
    ) {
        BeanInfo beanInfo = new BeanInfo();
        if (fieldBased) {
            beanInfo.readerFeatures |= JSONReader.Feature.FieldBased.mask;
        }

        for (ObjectReaderModule module : provider.modules) {
            ObjectReaderAnnotationProcessor annotationProcessor = module.getAnnotationProcessor();
            if (annotationProcessor != null) {
                annotationProcessor.getBeanInfo(beanInfo, objectClass);
            }
        }

        if (beanInfo.deserializer != null && ObjectReader.class.isAssignableFrom(beanInfo.deserializer)) {
            try {
                Constructor constructor = beanInfo.deserializer.getDeclaredConstructor();
                constructor.setAccessible(true);
                return (ObjectReader<T>) constructor.newInstance();
            } catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
                     InvocationTargetException e) {
                throw new JSONException("create deserializer error", e);
            }
        }

        if (fieldBased) {
            beanInfo.readerFeatures |= JSONReader.Feature.FieldBased.mask;
        }

        if (Enum.class.isAssignableFrom(objectClass) && (beanInfo.createMethod == null || beanInfo.createMethod.getParameterCount() == 1)) {
            return createEnumReader(objectClass, beanInfo.createMethod, provider);
        }

        if (Throwable.class.isAssignableFrom(objectClass)) {
            fieldBased = false;
            beanInfo.readerFeatures |= JSONReader.Feature.IgnoreSetNullValue.mask;
        }

        if (fieldBased && objectClass.isInterface()) {
            fieldBased = false;
        }

        FieldReader[] fieldReaderArray = createFieldReaders(objectClass, objectType, beanInfo, fieldBased, provider);
        boolean allReadOnlyOrZero = true;
        for (int i = 0; i < fieldReaderArray.length; i++) {
            FieldReader fieldReader = fieldReaderArray[i];
            if (!fieldReader.isReadOnly()) {
                allReadOnlyOrZero = false;
                break;
            }
        }

        if (beanInfo.creatorConstructor != null || beanInfo.createMethod != null) {
            return createObjectReaderWithCreator(objectClass, objectType, provider, beanInfo);
        }

        if (beanInfo.builder != null) {
            return createObjectReaderWithBuilder(objectClass, objectType, provider, beanInfo);
        }

        Constructor creatorConstructor = null;

        final List<Constructor> alternateConstructors = new ArrayList<>();
        BeanUtils.constructor(objectClass, alternateConstructors::add);

        if (Throwable.class.isAssignableFrom(objectClass)) {
            return new ObjectReaderException<>(objectClass, alternateConstructors, fieldReaderArray);
        }

        Constructor defaultConstructor = null;

        Class<?> declaringClass = objectClass.getDeclaringClass();

        int index = -1;
        for (int i = 0; i < alternateConstructors.size(); i++) {
            Constructor constructor = alternateConstructors.get(i);

            if (constructor.getParameterCount() == 0) {
                defaultConstructor = constructor;
            }

            if (declaringClass != null
                    && constructor.getParameterCount() == 1
                    && declaringClass.equals(constructor.getParameterTypes()[0])) {
                creatorConstructor = constructor;
                index = i;
                break;
            } else if (creatorConstructor == null) {
                creatorConstructor = constructor;
                index = i;
            } else if (constructor.getParameterCount() == 0) {
                creatorConstructor = constructor;
                index = i;
            } else if (creatorConstructor.getParameterCount() < constructor.getParameterCount()) {
                creatorConstructor = constructor;
                index = i;
            }
        }

        if (index != -1) {
            alternateConstructors.remove(index);
        }

        if (creatorConstructor != null && creatorConstructor.getParameterCount() != 0 && beanInfo.seeAlso == null) {
            boolean record = BeanUtils.isRecord(objectClass);

            creatorConstructor.setAccessible(true);
            String[] parameterNames = beanInfo.createParameterNames;
            if (record && parameterNames == null) {
                parameterNames = BeanUtils.getRecordFieldNames(objectClass);
            }

            if (parameterNames == null || parameterNames.length == 0) {
                parameterNames = ASMUtils.lookupParameterNames(creatorConstructor);

                Parameter[] parameters = creatorConstructor.getParameters();
                FieldInfo fieldInfo = new FieldInfo();
                for (int i = 0; i < parameters.length && i < parameterNames.length; i++) {
                    fieldInfo.init();

                    Parameter parameter = parameters[i];

                    provider.getFieldInfo(fieldInfo, objectClass, creatorConstructor, i, parameter);
                    if (fieldInfo.fieldName != null) {
                        parameterNames[i] = fieldInfo.fieldName;
                    }
                }
            }

            int matchCount = 0;
            if (defaultConstructor != null) {
                for (int i = 0; i < parameterNames.length; i++) {
                    String parameterName = parameterNames[i];
                    if (parameterName == null) {
                        continue;
                    }

                    for (int j = 0; j < fieldReaderArray.length; j++) {
                        FieldReader fieldReader = fieldReaderArray[j];
                        if (fieldReader != null) {
                            if (parameterName.equals(fieldReader.fieldName)) {
                                matchCount++;
                                break;
                            }
                        }
                    }
                }
            }

            if (!(fieldBased)
                    && !(Throwable.class.isAssignableFrom(objectClass))
                    && defaultConstructor == null
                    && matchCount != parameterNames.length) {
                if (creatorConstructor.getParameterCount() == 1) {
                    FieldInfo fieldInfo = new FieldInfo();
                    provider.getFieldInfo(fieldInfo, objectClass, creatorConstructor, 0, creatorConstructor.getParameters()[0]);
                    if ((fieldInfo.features & FieldInfo.VALUE_MASK) != 0) {
                        Type valueType = creatorConstructor.getGenericParameterTypes()[0];
                        Class valueClass = creatorConstructor.getParameterTypes()[0];

                        JSONSchema jsonSchema = null;
                        if (fieldInfo.schema != null && !fieldInfo.schema.isEmpty()) {
                            JSONObject object = JSON.parseObject(fieldInfo.schema);
                            if (!object.isEmpty()) {
                                jsonSchema = JSONSchema.of(object, valueClass);
                            }
                        }

                        Object defaultValue = fieldInfo.defaultValue;
                        if (defaultValue != null && defaultValue.getClass() != valueClass) {
                            Function typeConvert = JSONFactory
                                    .getDefaultObjectReaderProvider()
                                    .getTypeConvert(defaultValue.getClass(), valueType);
                            if (typeConvert != null) {
                                defaultValue = typeConvert.apply(defaultValue);
                            } else {
                                throw new JSONException("illegal defaultValue : " + defaultValue + ", class " + valueClass.getName());
                            }
                        }

                        return new ObjectReaderImplValue(
                                objectClass,
                                valueType,
                                valueClass,
                                fieldInfo.features,
                                fieldInfo.format,
                                defaultValue,
                                jsonSchema,
                                creatorConstructor,
                                null,
                                null
                        );
                    }
                }

                if (allReadOnlyOrZero && fieldReaderArray.length != 0 && alternateConstructors.isEmpty()) {
                    for (int i = 0; i < parameterNames.length; i++) {
                        String paramName = parameterNames[i];
                        for (FieldReader fieldReader : fieldReaderArray) {
                            if (fieldReader.field != null
                                    && fieldReader.field.getName().equals(paramName)
                                    && !fieldReader.fieldName.equals(paramName)
                            ) {
                                parameterNames[i] = fieldReader.fieldName;
                                break;
                            }
                        }
                    }
                }

                Function function = null;
                BiFunction biFunction = null;
                if (JIT) {
                    if (creatorConstructor.getParameterCount() == 1) {
                        function = LambdaMiscCodec.createFunction(creatorConstructor);
                    } else if (creatorConstructor.getParameterCount() == 2) {
                        biFunction = LambdaMiscCodec.createBiFunction(creatorConstructor);
                    }
                }

                Function<Map<Long, Object>, T> constructorFunction = new ConstructorFunction(
                        alternateConstructors,
                        creatorConstructor,
                        function,
                        biFunction,
                        null,
                        parameterNames
                );

                FieldReader[] paramFieldReaders = createFieldReaders(
                        provider,
                        objectClass,
                        objectType,
                        creatorConstructor,
                        creatorConstructor.getParameters(),
                        parameterNames
                );
                return new ObjectReaderNoneDefaultConstructor(
                        objectClass,
                        beanInfo.typeKey,
                        beanInfo.typeName,
                        beanInfo.readerFeatures,
                        constructorFunction,
                        alternateConstructors,
                        parameterNames,
                        paramFieldReaders,
                        fieldReaderArray,
                        null,
                        null
                );
            }
        }

        if (beanInfo.seeAlso != null && beanInfo.seeAlso.length != 0) {
            return createObjectReaderSeeAlso(
                    objectClass,
                    beanInfo.typeKey,
                    beanInfo.seeAlso,
                    beanInfo.seeAlsoNames,
                    beanInfo.seeAlsoDefault,
                    fieldReaderArray
            );
        }

        if (objectClass.isInterface()) {
            return new ObjectReaderInterface(
                    objectClass,
                    null,
                    null,
                    0L,
                    null,
                    null,
                    fieldReaderArray
            );
        }

        Supplier<T> creator = createSupplier(objectClass);
        JSONSchema jsonSchema = JSONSchema.of(JSON.parseObject(beanInfo.schema), objectClass);
        ObjectReader<T> objectReader = createObjectReader(
                objectClass,
                beanInfo.typeKey,
                beanInfo.readerFeatures,
                jsonSchema,
                creator,
                null,
                fieldReaderArray);

        if (objectReader instanceof ObjectReaderBean) {
            JSONReader.AutoTypeBeforeHandler beforeHandler = null;
            if (beanInfo.autoTypeBeforeHandler != null) {
                try {
                    Constructor constructor = beanInfo.autoTypeBeforeHandler.getDeclaredConstructor();
                    constructor.setAccessible(true);
                    beforeHandler = (JSONReader.AutoTypeBeforeHandler) constructor.newInstance();
                } catch (Exception ignored) {
                    // ignored
                }
            }

            if (beforeHandler != null) {
                ((ObjectReaderBean<T>) objectReader).setAutoTypeBeforeHandler(beforeHandler);
            }
        }

        return objectReader;
    }

    public <T> FieldReader[] createFieldReaders(Class<T> objectClass) {
        return createFieldReaders(
                objectClass,
                objectClass,
                null,
                false,
                JSONFactory.getDefaultObjectReaderProvider()
        );
    }

    public <T> FieldReader[] createFieldReaders(Class<T> objectClass, Type objectType) {
        return createFieldReaders(
                objectClass,
                objectType,
                null,
                false,
                JSONFactory.getDefaultObjectReaderProvider()
        );
    }

    protected void createFieldReader(
            Class objectClass,
            Type objectType,
            String namingStrategy,
            FieldInfo fieldInfo,
            Field field,
            Map<String, FieldReader> fieldReaders,
            ObjectReaderProvider provider
    ) {
        provider.getFieldInfo(fieldInfo, objectClass, field);

        if (fieldInfo.ignore) {
            boolean unwrap = (fieldInfo.features & FieldInfo.UNWRAPPED_MASK) != 0
                    && Map.class.isAssignableFrom(field.getType());
            if (!unwrap) {
                return;
            }
        }

        String fieldName;
        if (fieldInfo.fieldName == null || fieldInfo.fieldName.isEmpty()) {
            fieldName = field.getName();
            if (namingStrategy != null) {
                fieldName = BeanUtils.fieldName(fieldName, namingStrategy);
            }
        } else {
            fieldName = fieldInfo.fieldName;
        }

        Type fieldType = field.getGenericType();
        Class<?> fieldClass = field.getType();

        ObjectReader initReader = getInitReader(provider, fieldType, fieldClass, fieldInfo);
        String schema = fieldInfo.schema;
        if (fieldInfo.required) {
            if (schema == null) {
                schema = "{\"required\":true}";
            }
        }

        FieldReader<Object> fieldReader = createFieldReader(
                objectClass,
                objectType,
                fieldName,
                fieldInfo.ordinal,
                fieldInfo.features,
                fieldInfo.format,
                fieldInfo.locale,
                fieldInfo.defaultValue,
                schema,
                fieldType,
                fieldClass,
                field,
                initReader
        );

        FieldReader previous = fieldReaders.putIfAbsent(fieldName, fieldReader);
        if (previous != null) {
            int cmp = fieldReader.compareTo(previous);
            if (cmp > 0) {
                fieldReaders.put(fieldName, fieldReader);
            }
        }

        if (fieldInfo.alternateNames != null) {
            for (String alternateName : fieldInfo.alternateNames) {
                if (fieldName.equals(alternateName)) {
                    continue;
                }

                FieldReader<Object> fieldReader1 = createFieldReader(
                        objectClass,
                        objectType,
                        alternateName,
                        fieldInfo.ordinal,
                        fieldInfo.features,
                        null,
                        fieldInfo.locale,
                        fieldInfo.defaultValue,
                        schema,
                        fieldType,
                        fieldClass,
                        field,
                        null
                );
                fieldReaders.putIfAbsent(alternateName, fieldReader1);
            }
        }
    }

    protected void createFieldReader(
            Class objectClass,
            Type objectType,
            String namingStrategy,
            String[] orders,
            BeanInfo beanInfo,
            FieldInfo fieldInfo,
            Method method,
            Map<String, FieldReader> fieldReaders,
            ObjectReaderProvider provider
    ) {
        provider.getFieldInfo(fieldInfo, objectClass, method);

        if (fieldInfo.ignore) {
            return;
        }

        String fieldName;
        if (fieldInfo.fieldName == null || fieldInfo.fieldName.isEmpty()) {
            String methodName = method.getName();
            if (methodName.startsWith("set")) {
                fieldName = BeanUtils.setterName(methodName, namingStrategy);
            } else {
                fieldName = BeanUtils.getterName(method, namingStrategy);
            }

            char c0 = '\0', c1;
            int len = fieldName.length();
            if (len > 0) {
                c0 = fieldName.charAt(0);
            }

            if ((len == 1 && c0 >= 'a' && c0 <= 'z')
                    || (len > 2 && c0 >= 'A' && c0 <= 'Z' && (c1 = fieldName.charAt(1)) >= 'A' && c1 <= 'Z')
            ) {
                char[] chars = fieldName.toCharArray();
                if (len == 1) {
                    chars[0] = (char) (chars[0] - 32);
                } else {
                    chars[0] = (char) (chars[0] + 32);
                }
                String fieldName1 = new String(chars);
                Field field = BeanUtils.getDeclaredField(objectClass, fieldName1);
                if (field != null) {
                    if (Modifier.isPublic(field.getModifiers())) {
                        fieldName = field.getName();
                    } else if (len == 1) {
                        fieldInfo.alternateNames = new String[]{fieldName};
                        fieldName = field.getName();
                    }
                }
            }
        } else {
            fieldName = fieldInfo.fieldName;
        }

        if (orders != null && orders.length > 0) {
            boolean match = false;
            for (int i = 0; i < orders.length; i++) {
                if (fieldName.equals(orders[i])) {
                    fieldInfo.ordinal = i;
                    match = true;
                    break;
                }
            }
            if (!match) {
                if (fieldInfo.ordinal == 0) {
                    fieldInfo.ordinal = orders.length;
                }
            }
        }

        int parameterCount = method.getParameterCount();
        if (parameterCount == 0) {
            Type fieldType = method.getGenericReturnType();
            Class<?> fieldClass = method.getReturnType();
            FieldReader fieldReader = createFieldReaderMethod(
                    objectClass,
                    objectType,
                    fieldName,
                    fieldInfo.ordinal,
                    fieldInfo.features,
                    fieldInfo.format,
                    fieldInfo.locale,
                    fieldInfo.defaultValue,
                    fieldInfo.schema,
                    fieldType,
                    fieldClass,
                    method,
                    fieldInfo.getInitReader()
            );
            FieldReader origin = fieldReaders.putIfAbsent(fieldName,
                    fieldReader
            );
            if (origin != null && origin.compareTo(fieldReader) > 0) {
                fieldReaders.put(fieldName, fieldReader);
            }
            return;
        }

        if (parameterCount == 2) {
            Class<?> fieldClass = method.getParameterTypes()[1];
            Type fieldType = method.getGenericParameterTypes()[1];
            method.setAccessible(true);
            FieldReaderAnySetter anySetter = new FieldReaderAnySetter(fieldType, fieldClass, fieldInfo.ordinal, fieldInfo.features, fieldInfo.format, null, method);
            fieldReaders.put(anySetter.fieldName, anySetter);
            return;
        }

        Type fieldType = method.getGenericParameterTypes()[0];
        Class fieldClass = method.getParameterTypes()[0];
        if (fieldType instanceof Class && Collection.class.isAssignableFrom((Class<?>) fieldType)) {
            Class[] interfaces = objectClass.getInterfaces();
            for (int i = 0; i < interfaces.length; i++) {
                Method interfaceMethod = BeanUtils.getMethod(interfaces[i], method);
                if (interfaceMethod != null) {
                    Type[] genericParameterTypes = interfaceMethod.getGenericParameterTypes();
                    if (genericParameterTypes.length == 1
                            && genericParameterTypes[0] instanceof ParameterizedType
                    ) {
                        fieldType = genericParameterTypes[0];
                    }
                }
            }
        }

        // skip function
        if (isFunction(fieldClass)) {
            return;
        }

        ObjectReader initReader = getInitReader(provider, fieldType, fieldClass, fieldInfo);
        FieldReader fieldReader = null;
        boolean jit = (fieldInfo.features & FieldInfo.JIT) != 0;
        if (jit) {
            try {
                fieldReader = createFieldReaderLambda(
                        objectClass,
                        objectType,
                        fieldName,
                        fieldInfo.ordinal,
                        fieldInfo.features,
                        fieldInfo.format,
                        fieldInfo.locale,
                        fieldInfo.defaultValue,
                        fieldInfo.schema,
                        fieldType,
                        fieldClass,
                        method,
                        initReader
                );
            } catch (Throwable ignored) {
                jitErrorCount.incrementAndGet();
                jitErrorLast = ignored;
            }
        }

        if (fieldReader == null) {
            fieldReader = createFieldReaderMethod(
                    objectClass,
                    objectType,
                    fieldName,
                    fieldInfo.ordinal,
                    fieldInfo.features,
                    fieldInfo.format,
                    fieldInfo.locale,
                    fieldInfo.defaultValue,
                    fieldInfo.schema,
                    fieldType,
                    fieldClass,
                    method,
                    initReader
            );
        }

        FieldReader origin = fieldReaders.putIfAbsent(fieldName, fieldReader);
        if (origin != null && origin.compareTo(fieldReader) > 0) {
            fieldReaders.put(fieldName, fieldReader);
        }

        if (fieldInfo.alternateNames != null) {
            for (String alternateName : fieldInfo.alternateNames) {
                if (fieldName.equals(alternateName)) {
                    continue;
                }

                fieldReaders
                        .putIfAbsent(alternateName,
                                createFieldReaderMethod(
                                        objectClass,
                                        objectType,
                                        alternateName,
                                        fieldInfo.ordinal,
                                        fieldInfo.features,
                                        fieldInfo.format,
                                        fieldInfo.locale,
                                        fieldInfo.defaultValue,
                                        fieldInfo.schema,
                                        fieldType,
                                        fieldClass,
                                        method,
                                        initReader
                                ));
            }
        }
    }

    protected <T> FieldReader[] createFieldReaders(
            Class<T> objectClass,
            Type objectType,
            BeanInfo beanInfo,
            boolean fieldBased,
            ObjectReaderProvider provider
    ) {
        if (beanInfo == null) {
            beanInfo = new BeanInfo();
            for (ObjectReaderModule module : provider.modules) {
                ObjectReaderAnnotationProcessor annotationProcessor = module.getAnnotationProcessor();
                if (annotationProcessor != null) {
                    annotationProcessor.getBeanInfo(beanInfo, objectClass);
                }
            }
        }

        boolean recoard = BeanUtils.isRecord(objectClass);
        final String namingStrategy = beanInfo.namingStrategy;

        Map<String, FieldReader> fieldReaders = new LinkedHashMap<>();

        BeanInfo finalBeanInfo = beanInfo;
        final long beanFeatures = beanInfo.readerFeatures;
        final String beanFormat = beanInfo.format;
        final FieldInfo fieldInfo = new FieldInfo();
        final String[] orders = beanInfo.orders;
        if (fieldBased) {
            BeanUtils.declaredFields(objectClass, field -> {
                fieldInfo.init();
                fieldInfo.features |= JSONReader.Feature.FieldBased.mask;
                fieldInfo.features |= beanFeatures;
                fieldInfo.format = beanFormat;

                createFieldReader(objectClass, objectType, namingStrategy, fieldInfo, field, fieldReaders, provider);
            });
        } else {
            if (!recoard) {
                BeanUtils.declaredFields(objectClass, field -> {
                    fieldInfo.init();
                    fieldInfo.ignore = (field.getModifiers() & Modifier.PUBLIC) == 0;
                    fieldInfo.features |= beanFeatures;
                    fieldInfo.format = beanFormat;

                    createFieldReader(objectClass, objectType, namingStrategy, fieldInfo, field, fieldReaders, provider);
                    if (fieldInfo.required) {
                        String fieldName = fieldInfo.fieldName;
                        if (fieldName == null || fieldName.isEmpty()) {
                            fieldName = field.getName();
                        }
                        finalBeanInfo.required(fieldName);
                    }
                });
            }

            Class mixIn = provider.getMixIn(objectClass);
            BeanUtils.setters(objectClass, mixIn, method -> {
                fieldInfo.init();
                fieldInfo.features |= beanFeatures;
                fieldInfo.format = beanFormat;
                createFieldReader(objectClass, objectType, namingStrategy, orders, finalBeanInfo, fieldInfo, method, fieldReaders, provider);
            });

            if (objectClass.isInterface()) {
                BeanUtils.getters(objectClass, method -> {
                    fieldInfo.init();
                    fieldInfo.features |= beanFeatures;
                    createFieldReader(objectClass, objectType, namingStrategy, orders, finalBeanInfo, fieldInfo, method, fieldReaders, provider);
                });
            }
        }

        Class<? super T> superclass = objectClass.getSuperclass();
        if (BeanUtils.isExtendedMap(objectClass)) {
            Type superType = objectClass.getGenericSuperclass();
            FieldReader fieldReader = ObjectReaders.fieldReader(
                    SUPER,
                    superType,
                    superclass,
                    (o, f) -> {
                        Map thisMap = (Map) o;
                        Map superMap = (Map) f;
                        // avoid putAll oom
                        for (Object value : superMap.entrySet()) {
                            Map.Entry entry = (Map.Entry) value;
                            thisMap.put(entry.getKey(), entry.getValue());
                        }
                    }
            );
            fieldReaders.put(SUPER, fieldReader);
        }

        FieldReader[] fieldReaderArray = new FieldReader[fieldReaders.size()];
        fieldReaders.values().toArray(fieldReaderArray);
        Arrays.sort(fieldReaderArray);
        return fieldReaderArray;
    }

    public <T> Supplier<T> createSupplier(Class<T> objectClass) {
        if (objectClass.isInterface()) {
            return null;
        }

        int modifiers = objectClass.getModifiers();
        if (Modifier.isAbstract(modifiers)) {
            return null;
        }

        final Constructor<T> constructor;
        try {
            constructor = objectClass.getDeclaredConstructor();
            constructor.setAccessible(true);
        } catch (NoSuchMethodException ignored) {
            return null;
        } catch (Throwable e) {
            throw new JSONException("get constructor error, class " + objectClass.getName(), e);
        }

        return createSupplier(constructor, true);
    }

    public <T> Supplier<T> createSupplier(Constructor constructor, boolean jit) {
        jit &= JIT;

        if (jit) {
            Class declaringClass = constructor.getDeclaringClass();
            MethodHandles.Lookup lookup = JDKUtils.trustedLookup(declaringClass);
            try {
                if (constructor.getParameterCount() == 0) {
                    MethodHandle handle = lookup.findConstructor(declaringClass, METHOD_TYPE_VOID);
                    CallSite callSite = LambdaMetafactory.metafactory(
                            lookup,
                            "get",
                            METHOD_TYPE_SUPPLIER,
                            METHOD_TYPE_OBJECT,
                            handle,
                            METHOD_TYPE_OBJECT
                    );
                    return (Supplier) callSite.getTarget().invokeExact();
                }
            } catch (Throwable e) {
                jitErrorCount.incrementAndGet();
                jitErrorLast = e;
            }
        }

        return new ConstructorSupplier(constructor);
    }

    protected <T> IntFunction<T> createIntFunction(Constructor constructor) {
        Class declaringClass = constructor.getDeclaringClass();
        MethodHandles.Lookup lookup = JDKUtils.trustedLookup(declaringClass);
        try {
            MethodHandle handle = lookup.findConstructor(declaringClass, METHOD_TYPE_VOID_INT);
            MethodType instantiatedMethodType = MethodType.methodType(declaringClass, int.class);
            CallSite callSite = LambdaMetafactory.metafactory(
                    lookup,
                    "apply",
                    METHOD_TYPE_INT_FUNCTION,
                    METHOD_TYPE_OBJECT_INT,
                    handle,
                    instantiatedMethodType
            );
            return (IntFunction) callSite.getTarget().invokeExact();
        } catch (Throwable e) {
            jitErrorCount.incrementAndGet();
            jitErrorLast = e;
        }

        return null;
    }

    protected <T> IntFunction<T> createIntFunction(Method factoryMethod) {
        Class declaringClass = factoryMethod.getDeclaringClass();
        MethodHandles.Lookup lookup = JDKUtils.trustedLookup(declaringClass);
        try {
            MethodType methodType = MethodType.methodType(factoryMethod.getReturnType(), int.class);
            MethodHandle handle = lookup.findStatic(declaringClass, factoryMethod.getName(), methodType);
            CallSite callSite = LambdaMetafactory.metafactory(
                    lookup,
                    "apply",
                    METHOD_TYPE_INT_FUNCTION,
                    METHOD_TYPE_OBJECT_INT,
                    handle,
                    methodType
            );
            return (IntFunction) callSite.getTarget().invokeExact();
        } catch (Throwable e) {
            jitErrorCount.incrementAndGet();
            jitErrorLast = e;
        }

        return null;
    }

    protected <T> Function<String, T> createStringFunction(Constructor constructor) {
        Class declaringClass = constructor.getDeclaringClass();
        MethodHandles.Lookup lookup = JDKUtils.trustedLookup(declaringClass);
        try {
            MethodHandle handle = lookup.findConstructor(declaringClass, METHOD_TYPE_VOID_STRING);
            MethodType instantiatedMethodType = MethodType.methodType(declaringClass, String.class);
            CallSite callSite = LambdaMetafactory.metafactory(
                    lookup,
                    "apply",
                    METHOD_TYPE_FUNCTION,
                    METHOD_TYPE_OBJECT_OBJECT,
                    handle,
                    instantiatedMethodType
            );
            return (Function<String, T>) callSite.getTarget().invokeExact();
        } catch (Throwable e) {
            jitErrorCount.incrementAndGet();
            jitErrorLast = e;
        }

        return null;
    }

    protected <T> Function<String, T> createStringFunction(Method factoryMethod) {
        Class declaringClass = factoryMethod.getDeclaringClass();
        MethodHandles.Lookup lookup = JDKUtils.trustedLookup(declaringClass);
        try {
            MethodType methodType = MethodType.methodType(factoryMethod.getReturnType(), String.class);
            MethodHandle handle = lookup.findStatic(declaringClass, factoryMethod.getName(), methodType);
            CallSite callSite = LambdaMetafactory.metafactory(
                    lookup,
                    "apply",
                    METHOD_TYPE_FUNCTION,
                    METHOD_TYPE_OBJECT_OBJECT,
                    handle,
                    methodType
            );
            return (Function<String, T>) callSite.getTarget().invokeExact();
        } catch (Throwable e) {
            jitErrorCount.incrementAndGet();
            jitErrorLast = e;
        }

        return null;
    }

    protected <I, T> Function<I, T> createValueFunction(Constructor<T> constructor, Class<I> valueClass) {
        Class declaringClass = constructor.getDeclaringClass();
        MethodHandles.Lookup lookup = JDKUtils.trustedLookup(declaringClass);
        try {
            MethodType methodType = MethodType.methodType(void.class, valueClass);
            MethodHandle handle = lookup.findConstructor(declaringClass, methodType);
            MethodType instantiatedMethodType = MethodType.methodType(declaringClass, valueClass);
            CallSite callSite = LambdaMetafactory.metafactory(
                    lookup,
                    "apply",
                    METHOD_TYPE_FUNCTION,
                    METHOD_TYPE_OBJECT_OBJECT,
                    handle,
                    instantiatedMethodType
            );
            return (Function<I, T>) callSite.getTarget().invokeExact();
        } catch (Throwable e) {
            jitErrorCount.incrementAndGet();
            jitErrorLast = e;
        }

        return null;
    }

    protected <I, T> Function<I, T> createValueFunction(Method factoryMethod, Class valueClass) {
        Class declaringClass = factoryMethod.getDeclaringClass();
        MethodHandles.Lookup lookup = JDKUtils.trustedLookup(declaringClass);
        try {
            MethodType methodType = MethodType.methodType(factoryMethod.getReturnType(), valueClass);
            MethodHandle handle = lookup.findStatic(declaringClass, factoryMethod.getName(), methodType);
            CallSite callSite = LambdaMetafactory.metafactory(
                    lookup,
                    "apply",
                    METHOD_TYPE_FUNCTION,
                    METHOD_TYPE_OBJECT_OBJECT,
                    handle,
                    methodType
            );
            return (Function<I, T>) callSite.getTarget().invokeExact();
        } catch (Throwable e) {
            jitErrorCount.incrementAndGet();
            jitErrorLast = e;
        }

        return null;
    }

    public <T, R> Function<T, R> createBuildFunction(Method builderMethod) {
        try {
            return createBuildFunctionLambda(builderMethod);
        } catch (Throwable e) {
            jitErrorCount.incrementAndGet();
            jitErrorLast = e;
        }

        builderMethod.setAccessible(true);

        return (T o) -> {
            try {
                return (R) builderMethod.invoke(o);
            } catch (Throwable e) {
                throw new JSONException("create instance error", e);
            }
        };
    }

    <T, R> Function<T, R> createBuildFunctionLambda(Method builderMethod) {
        MethodHandles.Lookup lookup = JDKUtils.trustedLookup(builderMethod.getDeclaringClass());
        try {
            MethodHandle target = lookup.findVirtual(builderMethod.getDeclaringClass(),
                    builderMethod.getName(),
                    MethodType.methodType(builderMethod.getReturnType())
            );
            MethodType func = target.type();

            CallSite callSite = LambdaMetafactory.metafactory(
                    lookup,
                    "apply",
                    METHOD_TYPE_FUNCTION,
                    func.erase(),
                    target,
                    func
            );

            Object object = callSite
                    .getTarget()
                    .invoke();
            return (Function<T, R>) object;
        } catch (Throwable e) {
            throw new JSONException("create fieldReader error", e);
        }
    }

    public <T> FieldReader createFieldReader(
            Class<T> objectType,
            String fieldName,
            Type fieldType,
            Class fieldClass,
            Method method
    ) {
        return createFieldReaderMethod(
                objectType,
                objectType,
                fieldName,
                0,
                0L,
                null,
                null,
                null,
                null,
                fieldType,
                fieldClass,
                method,
                null
        );
    }

    public <T> FieldReader createFieldReader(
            Class<T> objectType,
            String fieldName,
            String format,
            Type fieldType,
            Class fieldClass,
            Method method
    ) {
        return createFieldReaderMethod(objectType, fieldName, format, fieldType, fieldClass, method);
    }

    public <T> FieldReader createFieldReaderMethod(
            Class<T> objectClass,
            String fieldName,
            String format,
            Type fieldType,
            Class fieldClass,
            Method method
    ) {
        return createFieldReaderMethod(
                objectClass,
                objectClass,
                fieldName,
                0,
                0L,
                format,
                null,
                null,
                null,
                fieldType,
                fieldClass,
                method,
                null
        );
    }

    public <T> FieldReader createFieldReaderParam(
            Class<T> objectClass,
            Type objectType,
            String fieldName,
            int ordinal,
            long features,
            String format,
            Type fieldType,
            Class fieldClass,
            String paramName,
            Class declaringClass,
            Parameter parameter,
            JSONSchema schema
    ) {
        return createFieldReaderParam(
                objectClass,
                objectType,
                fieldName,
                ordinal,
                features,
                format,
                fieldType,
                fieldClass,
                paramName,
                declaringClass,
                parameter,
                schema,
                null
        );
    }

    public <T> FieldReader createFieldReaderParam(
            Class<T> objectClass,
            Type objectType,
            String fieldName,
            int ordinal,
            long features,
            String format,
            Type fieldType,
            Class fieldClass,
            String paramName,
            Class declaringClass,
            Parameter parameter,
            JSONSchema schema,
            ObjectReader initReader
    ) {
        if (initReader != null) {
            FieldReaderObjectParam paramReader = new FieldReaderObjectParam(
                    fieldName,
                    fieldType,
                    fieldClass,
                    paramName,
                    parameter,
                    ordinal,
                    features,
                    format,
                    schema
            );
            paramReader.initReader = initReader;
            return paramReader;
        }

        if (fieldType == byte.class || fieldType == Byte.class) {
            return new FieldReaderInt8Param(fieldName, fieldClass, paramName, parameter, ordinal, features, format, schema);
        }

        if (fieldType == short.class || fieldType == Short.class) {
            return new FieldReaderInt16Param(fieldName, fieldClass, paramName, parameter, ordinal, features, format, schema);
        }

        if (fieldType == int.class || fieldType == Integer.class) {
            return new FieldReaderInt32Param(fieldName, fieldClass, paramName, parameter, ordinal, features, format, schema);
        }

        if (fieldType == long.class || fieldType == Long.class) {
            return new FieldReaderInt64Param(fieldName, fieldClass, paramName, parameter, ordinal, features, format, schema);
        }

        Type fieldTypeResolved = null;
        Class fieldClassResolved = null;
        if (!(fieldType instanceof Class) && objectType != null) {
            fieldTypeResolved = BeanUtils.getParamType(TypeReference.get(objectType), objectClass, declaringClass, parameter, fieldType);
            if (fieldTypeResolved != null) {
                fieldClassResolved = TypeUtils.getMapping(fieldTypeResolved);
            }
        }
        if (fieldTypeResolved == null) {
            fieldTypeResolved = fieldType;
        }
        if (fieldClassResolved == null) {
            fieldClassResolved = fieldClass;
        }

        return new FieldReaderObjectParam(fieldName, fieldTypeResolved, fieldClassResolved, paramName, parameter, ordinal, features, format, schema);
    }

    public <T> FieldReader createFieldReaderMethod(
            Class<T> objectClass,
            Type objectType,
            String fieldName,
            int ordinal,
            long features,
            String format,
            Locale locale,
            Object defaultValue,
            String schema,
            Type fieldType,
            Class fieldClass,
            Method method,
            ObjectReader initReader
    ) {
        if (method != null) {
            method.setAccessible(true);
        }

        if (defaultValue != null && defaultValue.getClass() != fieldClass) {
            Function typeConvert = JSONFactory
                    .getDefaultObjectReaderProvider()
                    .getTypeConvert(defaultValue.getClass(), fieldType);
            if (typeConvert != null) {
                defaultValue = typeConvert.apply(defaultValue);
            } else {
                throw new JSONException("illegal defaultValue : " + defaultValue + ", class " + fieldClass.getName());
            }
        }

        JSONSchema jsonSchema = null;
        if (schema != null && !schema.isEmpty()) {
            JSONObject object = JSON.parseObject(schema);
            if (!object.isEmpty()) {
                jsonSchema = JSONSchema.of(object, fieldClass);
            }
        }

        if (initReader != null) {
            FieldReaderObject fieldReaderObjectMethod = new FieldReaderObject(
                    fieldName,
                    fieldType,
                    fieldClass,
                    ordinal,
                    features | FieldInfo.READ_USING_MASK,
                    format,
                    locale,
                    defaultValue,
                    jsonSchema,
                    method,
                    null,
                    null
            );
            fieldReaderObjectMethod.initReader = initReader;
            return fieldReaderObjectMethod;
        }

        if (fieldType == boolean.class) {
            return new FieldReaderBoolValueMethod(fieldName, ordinal, features, format, (Boolean) defaultValue, jsonSchema, method);
        }

        if (fieldType == Boolean.class) {
            return new FieldReaderBoolMethod(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (Boolean) defaultValue, jsonSchema, method);
        }

        if (fieldType == byte.class) {
            return new FieldReaderInt8ValueMethod(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (Byte) defaultValue, jsonSchema, method);
        }

        if (fieldType == short.class) {
            return new FieldReaderInt16ValueMethod(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (Short) defaultValue, jsonSchema, method);
        }

        if (fieldType == int.class) {
            return new FieldReaderInt32ValueMethod(fieldName, fieldType, fieldClass, ordinal, features, format, (Integer) defaultValue, jsonSchema, method);
        }

        if (fieldType == long.class) {
            return new FieldReaderInt64ValueMethod(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (Long) defaultValue, jsonSchema, method);
        }

        if (fieldType == float.class) {
            return new FieldReaderFloatValueMethod(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (Float) defaultValue, jsonSchema, method);
        }

        if (fieldType == double.class) {
            return new FieldReaderDoubleValueMethod(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (Double) defaultValue, jsonSchema, method);
        }

        if (fieldType == Byte.class) {
            return new FieldReaderInt8Method(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (Byte) defaultValue, jsonSchema, method);
        }

        if (fieldType == Short.class) {
            return new FieldReaderInt16Method(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (Short) defaultValue, jsonSchema, method);
        }

        if (fieldType == Integer.class) {
            return new FieldReaderInt32Method(fieldName, ordinal, features, format, locale, (Integer) defaultValue, jsonSchema, method);
        }

        if (fieldType == Long.class) {
            return new FieldReaderInt64Method(fieldName, ordinal, features, format, locale, (Long) defaultValue, jsonSchema, method);
        }

        if (fieldType == Float.class) {
            return new FieldReaderFloatMethod(fieldName, ordinal, features, format, locale, (Float) defaultValue, jsonSchema, method);
        }

        if (fieldType == Double.class) {
            return new FieldReaderDoubleMethod(fieldName, ordinal, features, format, (Double) defaultValue, jsonSchema, method);
        }

        if (fieldClass == BigDecimal.class) {
            return new FieldReaderBigDecimalMethod(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (BigDecimal) defaultValue, jsonSchema, method);
        }

        if (fieldClass == BigInteger.class) {
            return new FieldReaderBigIntegerMethod(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (BigInteger) defaultValue, jsonSchema, method);
        }

        if (fieldType == String.class) {
            return new FieldReaderStringMethod(fieldName, fieldType, fieldClass, ordinal, features, format, locale, (String) defaultValue, jsonSchema, method);
        }

        if (fieldType == LocalDate.class) {
            return new FieldReaderLocalDate(
                    fieldName,
                    fieldType,
                    fieldClass,
                    ordinal,
                    features,
                    format,
                    locale,
                    defaultValue,
                    jsonSchema,
                    method,
                    null,
                    null
            );
        }

        if (fieldType == OffsetDateTime.class) {
            return new FieldReaderOffsetDateTime(
                    fieldName,
                    fieldType,
                    fieldClass,
                    ordinal,
                    features,
                    format,
                    locale,
                    defaultValue,
                    jsonSchema,
                    method,
                    null,
                    null
            );
        }

        if (fieldType == UUID.class) {
            return new FieldReaderUUID(
                    fieldName,
                    fieldType,
                    fieldClass,
                    ordinal,
                    features,
                    format,
                    locale,
                    defaultValue,
                    jsonSchema,
                    method,
                    null,
                    null
            );
        }

        if (fieldType == String[].class) {
            return new FieldReaderStringArray(
                    fieldName,
                    fieldType,
                    fieldClass,
                    ordinal,
                    features,
                    format,
                    locale,
                    defaultValue,
                    jsonSchema,
                    method,
                    null,
                    null
            );
        }

        Type fieldTypeResolved = null;
        Class fieldClassResolved = null;
        if (!(fieldType instanceof Class) || !(objectType instanceof Class)) {
            fieldTypeResolved = BeanUtils.getFieldType(TypeReference.get(objectType), objectClass, method, fieldType);
            fieldClassResolved = TypeUtils.getMapping(fieldTypeResolved);
        }

        if (method.getParameterCount() == 0) {
            if (fieldClass == AtomicInteger.class) {
                return new FieldReaderAtomicIntegerMethodReadOnly(fieldName, fieldClass, ordinal, jsonSchema, method);
            }

            if (fieldClass == AtomicLong.class) {
                return new FieldReaderAtomicLongReadOnly(fieldName, fieldClass, ordinal, jsonSchema, method);
            }

            if (fieldClass == AtomicIntegerArray.class) {
                return new FieldReaderAtomicIntegerArrayReadOnly(fieldName, fieldClass, ordinal, jsonSchema, method);
            }

            if (fieldClass == AtomicLongArray.class) {
                return new FieldReaderAtomicLongArrayReadOnly(fieldName, fieldClass, ordinal, jsonSchema, method);
            }

            if (fieldClass == AtomicBoolean.class) {
                return new FieldReaderAtomicBooleanMethodReadOnly(fieldName, fieldClass, ordinal, jsonSchema, method);
            }

            if (fieldClass == AtomicReference.class) {
                return new FieldReaderAtomicReferenceMethodReadOnly(fieldName, fieldType, fieldClass, ordinal, jsonSchema, method);
            }

            if (Collection.class.isAssignableFrom(fieldClass)) {
                Field field = null;
                String methodName = method.getName();
                if (methodName.startsWith("get")) {
                    String getterName = BeanUtils.getterName(methodName, PropertyNamingStrategy.CamelCase.name());
                    field = BeanUtils.getDeclaredField(method.getDeclaringClass(), getterName);
                }
                return new FieldReaderCollectionMethodReadOnly(
                        fieldName,
                        fieldTypeResolved != null ? fieldTypeResolved : fieldType,
                        fieldClass,
                        ordinal,
                        features,
                        format,
                        jsonSchema,
                        method,
                        field
                );
            }

            if (Map.class.isAssignableFrom(fieldClass)) {
                Field field = null;
                String methodName = method.getName();
                if (methodName.startsWith("get")) {
                    String getterName = BeanUtils.getterName(methodName, PropertyNamingStrategy.CamelCase.name());
                    field = BeanUtils.getDeclaredField(method.getDeclaringClass(), getterName);
                }
                return new FieldReaderMapMethodReadOnly(
                        fieldName,
                        fieldType,
                        fieldClass,
                        ordinal,
                        features,
                        format,
                        jsonSchema,
                        method,
                        field
                );
            }

            if (!objectClass.isInterface()) {
                return null;
            }
        }

        if (fieldClass == List.class
                || fieldClass == ArrayList.class
                || fieldClass == LinkedList.class
        ) {
            if (fieldTypeResolved instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) fieldTypeResolved;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                if (actualTypeArguments.length == 1) {
                    Type itemType = actualTypeArguments[0];
                    Class itemClass = TypeUtils.getMapping(itemType);

                    if (itemClass == String.class) {
                        return new FieldReaderList(fieldName, fieldTypeResolved, fieldClass, String.class, String.class, ordinal, features, format, locale, null, jsonSchema, method, null, null);
                    }

                    return new FieldReaderList(fieldName, fieldTypeResolved, fieldClassResolved, itemType, itemClass, ordinal, features, format, locale, null, jsonSchema, method, null, null);
                }
            }
            return new FieldReaderList(fieldName, fieldType, fieldClass, Object.class, Object.class, ordinal, features, format, locale, null, jsonSchema, method, null, null);
        }

        if (fieldClass == Date.class) {
            return new FieldReaderDate(fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, jsonSchema, null, method, null);
        }

        if (fieldClass == StackTraceElement[].class && method.getDeclaringClass() == Throwable.class) {
            return new FieldReaderStackTrace(
                    fieldName,
                    fieldTypeResolved != null ? fieldTypeResolved : fieldType,
                    fieldClass,
                    ordinal,
                    features,
                    format,
                    locale,
                    defaultValue,
                    jsonSchema,
                    method,
                    null,
                    (BiConsumer<Throwable, StackTraceElement[]>) Throwable::setStackTrace
            );
        }

        Field field = null;
        if ((features & FieldInfo.UNWRAPPED_MASK) != 0) {
            String methodName = method.getName();
            if (methodName.startsWith("set")) {
                String setterName = BeanUtils.setterName(methodName, PropertyNamingStrategy.CamelCase.name());
                field = BeanUtils.getDeclaredField(method.getDeclaringClass(), setterName);
                try {
                    field.setAccessible(true);
                } catch (Throwable ignored) {
                    // ignored
                }
            }
        }

        return new FieldReaderObject(
                fieldName,
                fieldTypeResolved != null ? fieldTypeResolved : fieldType,
                fieldClass,
                ordinal,
                features,
                format,
                locale,
                defaultValue,
                jsonSchema,
                method,
                field,
                null
        );
    }

    public <T> FieldReader<T> createFieldReader(
            String fieldName,
            Type fieldType,
            Field field
    ) {
        return createFieldReader(fieldName, null, fieldType, field);
    }

    public <T> FieldReader<T> createFieldReader(
            String fieldName,
            Field field
    ) {
        return createFieldReader(fieldName, null, field.getGenericType(), field);
    }

    public <T> FieldReader createFieldReader(
            String fieldName,
            Method method
    ) {
        Class<?> declaringClass = method.getDeclaringClass();
        int parameterCount = method.getParameterCount();

        Class fieldClass;
        Type fieldType;
        if (parameterCount == 0) {
            fieldClass = method.getReturnType();
            fieldType = method.getGenericReturnType();
        } else if (parameterCount == 1) {
            fieldClass = method.getParameterTypes()[0];
            fieldType = method.getGenericParameterTypes()[0];
        } else {
            throw new JSONException("illegal setter method " + method);
        }

        return createFieldReaderMethod(
                declaringClass,
                declaringClass,
                fieldName,
                0,
                0L,
                null,
                null,
                null,
                null,
                fieldType,
                fieldClass,
                method,
                null
        );
    }

    public <T> FieldReader<T> createFieldReader(
            String fieldName,
            String format,
            Type fieldType,
            Field field
    ) {
        Class objectClass = field.getDeclaringClass();
        return createFieldReader(objectClass, objectClass, fieldName, 0, format, fieldType, field.getType(), field);
    }

    public <T> FieldReader<T> createFieldReader(
            Class objectClass,
            Type objectType,
            String fieldName,
            long features,
            String format,
            Type fieldType,
            Class fieldClass,
            Field field
    ) {
        return createFieldReader(
                objectClass,
                objectType,
                fieldName,
                0,
                features,
                format,
                null,
                null,
                null,
                fieldType,
                field.getType(),
                field,
                null
        );
    }

    public <T> FieldReader<T> createFieldReader(
            Class objectClass,
            Type objectType,
            String fieldName,
            int ordinal,
            long features,
            String format,
            Locale locale,
            Object defaultValue,
            String schema,
            Type fieldType,
            Class fieldClass,
            Field field,
            ObjectReader initReader
    ) {
        if (defaultValue != null && defaultValue.getClass() != fieldClass) {
            ObjectReaderProvider provider = JSONFactory
                    .getDefaultObjectReaderProvider();
            Function typeConvert = provider.getTypeConvert(defaultValue.getClass(), fieldType);
            if (typeConvert != null) {
                defaultValue = typeConvert.apply(defaultValue);
            } else {
                throw new JSONException("illegal defaultValue : " + defaultValue + ", class " + fieldClass.getName());
            }
        }

        JSONSchema jsonSchema = null;
        if (schema != null && !schema.isEmpty()) {
            JSONObject object = JSON.parseObject(schema);
            if (!object.isEmpty()) {
                jsonSchema = JSONSchema.of(object, fieldClass);
            }
        }

        if (field != null) {
            String objectClassName = objectClass.getName();
            if (!objectClassName.startsWith("java.lang") && !objectClassName.startsWith("java.time")) {
                field.setAccessible(true);
            }
        }

        if (initReader != null) {
            FieldReaderObjectField fieldReader = new FieldReaderObjectField(fieldName, fieldType, fieldClass, ordinal, features | FieldInfo.READ_USING_MASK, format, defaultValue, jsonSchema, field);
            fieldReader.initReader = initReader;
            return fieldReader;
        }

        if (fieldClass == int.class) {
            return new FieldReaderInt32ValueField(fieldName, fieldClass, ordinal, format, (Integer) defaultValue, jsonSchema, field);
        }
        if (fieldClass == Integer.class) {
            return new FieldReaderInt32Field(fieldName, fieldClass, ordinal, features, format, (Integer) defaultValue, jsonSchema, field);
        }

        if (fieldClass == long.class) {
            return new FieldReaderInt64ValueField(fieldName, fieldClass, ordinal, features, format, (Long) defaultValue, jsonSchema, field);
        }
        if (fieldClass == Long.class) {
            return new FieldReaderInt64Field(fieldName, fieldClass, ordinal, features, format, (Long) defaultValue, jsonSchema, field);
        }

        if (fieldClass == short.class) {
            return new FieldReaderInt16ValueField(fieldName, fieldClass, ordinal, features, format, (Short) defaultValue, jsonSchema, field);
        }

        if (fieldClass == Short.class) {
            return new FieldReaderInt16Field(fieldName, fieldClass, ordinal, features, format, (Short) defaultValue, jsonSchema, field);
        }

        if (fieldClass == boolean.class) {
            return new FieldReaderBoolValueField(fieldName, ordinal, features, format, (Boolean) defaultValue, jsonSchema, field);
        }

        if (fieldClass == Boolean.class) {
            return new FieldReaderBoolField(fieldName, fieldClass, ordinal, features, format, (Boolean) defaultValue, jsonSchema, field);
        }

        if (fieldClass == byte.class) {
            return new FieldReaderInt8ValueField(fieldName, fieldClass, ordinal, features, format, (Byte) defaultValue, jsonSchema, field);
        }

        if (fieldClass == Byte.class) {
            return new FieldReaderInt8Field(fieldName, fieldClass, ordinal, features, format, (Byte) defaultValue, jsonSchema, field);
        }

        if (fieldClass == float.class) {
            return new FieldReaderFloatValueField(fieldName, fieldClass, ordinal, features, format, (Float) defaultValue, jsonSchema, field);
        }
        if (fieldClass == Float.class) {
            return new FieldReaderFloatField(fieldName, fieldClass, ordinal, features, format, (Float) defaultValue, jsonSchema, field);
        }

        if (fieldClass == double.class) {
            return new FieldReaderDoubleValueField(fieldName, fieldClass, ordinal, features, format, (Double) defaultValue, jsonSchema, field);
        }
        if (fieldClass == Double.class) {
            return new FieldReaderDoubleField(fieldName, fieldClass, ordinal, features, format, (Double) defaultValue, jsonSchema, field);
        }

        if (fieldClass == char.class) {
            return new FieldReaderCharValueField(fieldName, ordinal, features, format, (Character) defaultValue, jsonSchema, field);
        }

        if (fieldClass == BigDecimal.class) {
            return new FieldReaderBigDecimalField(fieldName, fieldClass, ordinal, features, format, (BigDecimal) defaultValue, jsonSchema, field);
        }

        if (fieldClass == BigInteger.class) {
            return new FieldReaderBigIntegerField(fieldName, fieldClass, ordinal, features, format, (BigInteger) defaultValue, jsonSchema, field);
        }

        if (fieldClass == String.class) {
            return new FieldReaderStringField(fieldName, fieldClass, ordinal, features, format, (String) defaultValue, jsonSchema, field);
        }

        if (fieldClass == Date.class) {
            return new FieldReaderDate(fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, jsonSchema, field, null, null);
        }

        if (fieldClass == AtomicBoolean.class) {
            return new FieldReaderAtomicBooleanFieldReadOnly(fieldName, fieldClass, ordinal, format, (AtomicBoolean) defaultValue, jsonSchema, field);
        }

        if (fieldClass == AtomicReference.class) {
            return new FieldReaderAtomicReferenceField(fieldName, fieldType, fieldClass, ordinal, format, jsonSchema, field);
        }

        Type fieldTypeResolved = null;
        Class fieldClassResolved = null;
        if (!(fieldType instanceof Class)) {
            fieldTypeResolved = BeanUtils.getFieldType(TypeReference.get(objectType), objectClass, field, fieldType);
            fieldClassResolved = TypeUtils.getMapping(fieldTypeResolved);
        }

        boolean finalField = Modifier.isFinal(field.getModifiers());
        if (Collection.class.isAssignableFrom(fieldClass)) {
            if (fieldTypeResolved instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) fieldTypeResolved;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

                if (actualTypeArguments.length == 1) {
                    Type itemType = actualTypeArguments[0];
                    Class itemClass = TypeUtils.getMapping(itemType);
                    if (itemClass == String.class) {
                        if (finalField) {
                            if ((features & JSONReader.Feature.FieldBased.mask) != 0) {
                                return new FieldReaderListField(fieldName, fieldTypeResolved, fieldClassResolved, String.class, String.class, ordinal, features, format, locale, null, jsonSchema, field);
                            }

                            return new FieldReaderCollectionFieldReadOnly(fieldName, fieldTypeResolved, fieldClassResolved, ordinal, features, format, jsonSchema, field);
                        }

                        return new FieldReaderListField(fieldName, fieldTypeResolved, fieldClassResolved, String.class, String.class, ordinal, features, format, locale, null, jsonSchema, field);
                    }

                    return new FieldReaderListField(
                            fieldName,
                            fieldTypeResolved,
                            fieldClassResolved,
                            itemType,
                            itemClass,
                            ordinal,
                            features,
                            format,
                            locale,
                            (Collection) defaultValue,
                            jsonSchema,
                            field
                    );
                }
            }

            Type itemType = null;
            if (fieldType instanceof ParameterizedType) {
                Type[] actualTypeArguments = ((ParameterizedType) fieldType).getActualTypeArguments();
                if (actualTypeArguments.length > 0) {
                    itemType = actualTypeArguments[0];
                }
            }
            if (itemType == null) {
                itemType = Object.class;
            }
            Class itemClass = TypeUtils.getClass(itemType);

            return new FieldReaderListField(
                    fieldName,
                    fieldType,
                    fieldClass,
                    itemType,
                    itemClass,
                    ordinal,
                    features,
                    format,
                    locale,
                    (Collection) defaultValue,
                    jsonSchema,
                    field
            );
        }

        if (Map.class.isAssignableFrom(fieldClass)) {
            if (fieldTypeResolved instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) fieldTypeResolved;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

                if (actualTypeArguments.length == 2) {
                    if (finalField && ((features & JSONReader.Feature.FieldBased.mask) == 0)) {
                        return new FieldReaderMapFieldReadOnly(fieldName, fieldTypeResolved, fieldClassResolved, ordinal, features, format, jsonSchema, field);
                    }
                }
            }
        }

        if (finalField) {
            if (fieldClass == int[].class) {
                return new FieldReaderInt32ValueArrayFinalField(fieldName, fieldClass, ordinal, features, format, (int[]) defaultValue, jsonSchema, field);
            }

            if (fieldClass == long[].class) {
                return new FieldReaderInt64ValueArrayFinalField(fieldName, fieldClass, ordinal, features, format, (long[]) defaultValue, jsonSchema, field);
            }
        }

        if (fieldClassResolved != null) {
            if ((features & FieldInfo.UNWRAPPED_MASK) != 0
                    && Map.class.isAssignableFrom(fieldClassResolved)
            ) {
                return new FieldReaderMapFieldReadOnly(fieldName,
                        fieldTypeResolved,
                        fieldClass,
                        ordinal,
                        features,
                        format,
                        jsonSchema,
                        field);
            }

            return new FieldReaderObjectField(
                    fieldName,
                    fieldTypeResolved,
                    fieldClass,
                    ordinal,
                    features,
                    format,
                    defaultValue,
                    jsonSchema,
                    field);
        }

        if (fieldClass == LocalDateTime.class) {
            return new FieldReaderLocalDateTime(fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, jsonSchema, field, null, null);
        }

        if (fieldClass == ZonedDateTime.class) {
            return new FieldReaderZonedDateTime(fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, jsonSchema, field, null, null);
        }

        if (fieldClass == Instant.class) {
            return new FieldReaderInstant(fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, jsonSchema, field, null, null);
        }

        return new FieldReaderObjectField(fieldName, fieldType, fieldClass, ordinal, features, format, defaultValue, jsonSchema, field);
    }

    public <T, V> FieldReader createFieldReader(
            String fieldName,
            Type fieldType,
            Class<V> fieldClass,
            long features,
            BiConsumer<T, V> function
    ) {
        return createFieldReader(null, null, fieldName, fieldType, fieldClass, 0, features, null, null, null, null, null, function, null);
    }

    public <T, V> FieldReader createFieldReader(
            String fieldName,
            Type fieldType,
            Class<V> fieldClass,
            Method method,
            BiConsumer<T, V> function
    ) {
        return createFieldReader(null, null, fieldName, fieldType, fieldClass, 0, 0, null, null, null, null, method, function, null);
    }

    public <T, V> FieldReader createFieldReader(
            Class objectClass,
            Type objectType,
            String fieldName,
            Type fieldType,
            Class<V> fieldClass,
            int ordinal,
            long features,
            String format,
            Locale locale,
            Object defaultValue,
            JSONSchema schema,
            Method method,
            BiConsumer<T, V> function,
            ObjectReader initReader
    ) {
        if (initReader != null) {
            FieldReaderObject fieldReaderObjectMethod = new FieldReaderObject(
                    fieldName,
                    fieldType,
                    fieldClass,
                    ordinal,
                    features | FieldInfo.READ_USING_MASK,
                    format,
                    locale,
                    defaultValue,
                    schema,
                    method,
                    null,
                    function
            );
            fieldReaderObjectMethod.initReader = initReader;
            return fieldReaderObjectMethod;
        }

        if (fieldClass == Integer.class) {
            return new FieldReaderInt32Func<>(fieldName, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, function);
        }

        if (fieldClass == Long.class) {
            return new FieldReaderInt64Func<>(fieldName, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, function);
        }

        if (fieldClass == String.class) {
            return new FieldReaderStringFunc<>(fieldName, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, function);
        }

        if (fieldClass == Boolean.class) {
            return new FieldReaderBoolFunc<>(fieldName, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, function);
        }

        if (fieldClass == Short.class) {
            return new FieldReaderInt16Func(fieldName, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, function);
        }

        if (fieldClass == Byte.class) {
            return new FieldReaderInt8Func(fieldName, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, function);
        }

        if (fieldClass == BigDecimal.class) {
            return new FieldReaderBigDecimalFunc(fieldName, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, function);
        }

        if (fieldClass == BigInteger.class) {
            return new FieldReaderBigIntegerFunc(fieldName, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, function);
        }

        if (fieldClass == Float.class) {
            return new FieldReaderFloatFunc(fieldName, fieldClass, ordinal, features, format, locale, (Float) defaultValue, schema, method, function);
        }

        if (fieldClass == Double.class) {
            return new FieldReaderDoubleFunc(fieldName, fieldClass, ordinal, features, format, locale, (Double) defaultValue, schema, method, function);
        }

        if (fieldClass == Number.class) {
            return new FieldReaderNumberFunc(fieldName, fieldClass, ordinal, features, format, locale, (Number) defaultValue, schema, method, function);
        }

        if (fieldClass == Date.class) {
            return new FieldReaderDate(fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, schema, null, method, function);
        }

        Type fieldTypeResolved = null;
        Class fieldClassResolved = null;

        if (!(fieldType instanceof Class)) {
            TypeReference<?> objectTypeReference;
            if (objectType == null) {
                objectTypeReference = null;
            } else {
                objectTypeReference = TypeReference.get(objectType);
            }
            fieldTypeResolved = BeanUtils.getFieldType(objectTypeReference, objectClass, method, fieldType);
            fieldClassResolved = TypeUtils.getMapping(fieldTypeResolved);
        }

        if (fieldClass == List.class || fieldClass == ArrayList.class) {
            Type itemType = Object.class;
            Class itemClass = Object.class;
            if (fieldTypeResolved instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) fieldTypeResolved;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

                if (actualTypeArguments.length == 1) {
                    itemType = actualTypeArguments[0];
                    itemClass = TypeUtils.getMapping(itemType);
                    if (itemClass == String.class) {
                        return new FieldReaderList(fieldName, fieldTypeResolved, fieldClassResolved, String.class, String.class, ordinal, features, format, locale, defaultValue, schema, method, null, function);
                    }
                }
            }

            return new FieldReaderList(fieldName, fieldTypeResolved, fieldClassResolved, itemType, itemClass, ordinal, features, format, locale, defaultValue, schema, method, null, function);
        }

        if (fieldTypeResolved != null) {
            return new FieldReaderObjectFunc<>(fieldName, fieldTypeResolved, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, function, null);
        }

        return new FieldReaderObjectFunc<>(fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, schema, method, function, null);
    }

    protected ObjectReader createEnumReader(
            Class objectClass,
            Method createMethod,
            ObjectReaderProvider provider
    ) {
        FieldInfo fieldInfo = new FieldInfo();

        Enum[] ordinalEnums = (Enum[]) objectClass.getEnumConstants();

        Map<Long, Enum> enumMap = new LinkedHashMap<>();
        for (int i = 0; ordinalEnums != null && i < ordinalEnums.length; ++i) {
            Enum e = ordinalEnums[i];
            String name = e.name();
            long hash = Fnv.hashCode64(name);
            enumMap.put(hash, e);

            try {
                fieldInfo.init();
                Field field = objectClass.getField(name);
                provider.getFieldInfo(fieldInfo, objectClass, field);
                String jsonFieldName = fieldInfo.fieldName;
                if (jsonFieldName != null && !jsonFieldName.isEmpty() && !jsonFieldName.equals(name)) {
                    long jsonFieldNameHash = Fnv.hashCode64(jsonFieldName);
                    enumMap.putIfAbsent(jsonFieldNameHash, e);
                }
                if (fieldInfo.alternateNames != null) {
                    for (String alternateName : fieldInfo.alternateNames) {
                        if (alternateName == null || alternateName.isEmpty()) {
                            continue;
                        }
                        long alternateNameHash = Fnv.hashCode64(alternateName);
                        enumMap.putIfAbsent(alternateNameHash, e);
                    }
                }
            } catch (Exception ignored) {
            }
        }

        for (int i = 0; ordinalEnums != null && i < ordinalEnums.length; ++i) {
            Enum e = ordinalEnums[i];
            String name = e.name();
            long hashLCase = Fnv.hashCode64LCase(name);
            enumMap.putIfAbsent(hashLCase, e);
        }

        long[] enumNameHashCodes = new long[enumMap.size()];
        {
            int i = 0;
            for (Long h : enumMap.keySet()) {
                enumNameHashCodes[i++] = h;
            }
            Arrays.sort(enumNameHashCodes);
        }

        Member enumValueField = BeanUtils.getEnumValueField(objectClass, provider);
        if (enumValueField == null && provider.modules.size() > 0) {
            Class fieldClassMixInSource = provider.getMixIn(objectClass);
            if (fieldClassMixInSource != null) {
                Member mixedValueField = BeanUtils.getEnumValueField(fieldClassMixInSource, provider);
                if (mixedValueField instanceof Field) {
                    try {
                        enumValueField = objectClass.getField(mixedValueField.getName());
                    } catch (NoSuchFieldException ignored) {
                    }
                } else if (mixedValueField instanceof Method) {
                    try {
                        enumValueField = objectClass.getMethod(mixedValueField.getName());
                    } catch (NoSuchMethodException ignored) {
                    }
                }
            }
        }

        Enum[] enums = new Enum[enumNameHashCodes.length];
        for (int i = 0; i < enumNameHashCodes.length; ++i) {
            long hash = enumNameHashCodes[i];
            Enum e = enumMap.get(hash);
            enums[i] = e;
        }

        return new ObjectReaderImplEnum(objectClass, createMethod, enumValueField, enums, ordinalEnums, enumNameHashCodes);
    }

    static ObjectReader getInitReader(
            ObjectReaderProvider provider,
            Type fieldType,
            Class fieldClass,
            FieldInfo fieldInfo
    ) {
        ObjectReader initReader = fieldInfo.getInitReader();
        if (initReader == null && Map.class.isAssignableFrom(fieldClass) && (fieldInfo.keyUsing != null || fieldInfo.valueUsing != null)) {
            ObjectReader keyReader = null;
            if (fieldInfo.keyUsing != null) {
                try {
                    Constructor<?> constructor = fieldInfo.keyUsing.getDeclaredConstructor();
                    constructor.setAccessible(true);
                    keyReader = (ObjectReader) constructor.newInstance();
                } catch (Exception ignored) {
                    // ignored
                }
            }

            ObjectReader valueReader = null;
            if (fieldInfo.valueUsing != null) {
                try {
                    Constructor<?> constructor = fieldInfo.valueUsing.getDeclaredConstructor();
                    constructor.setAccessible(true);
                    valueReader = (ObjectReader) constructor.newInstance();
                } catch (Exception ignored) {
                    // ignored
                }
            }

            if (keyReader != null || valueReader != null) {
                ObjectReader reader = ObjectReaderImplMap.of(fieldType, fieldClass, fieldInfo.features);
                if (reader instanceof ObjectReaderImplMapTyped) {
                    ObjectReaderImplMapTyped mapReader = (ObjectReaderImplMapTyped) reader;
                    if (keyReader != null) {
                        mapReader.keyObjectReader = keyReader;
                    }
                    if (valueReader != null) {
                        mapReader.valueObjectReader = valueReader;
                    }

                    return mapReader;
                }
            }
        }
        if (initReader == null) {
            if (fieldClass == long.class || fieldClass == Long.class) {
                ObjectReader objectReader = provider.getObjectReader(Long.class);
                if (objectReader != ObjectReaderImplInt64.INSTANCE) {
                    initReader = objectReader;
                }
            } else if (fieldClass == BigDecimal.class) {
                ObjectReader objectReader = provider.getObjectReader(BigDecimal.class);
                if (objectReader != ObjectReaderImplBigDecimal.INSTANCE) {
                    initReader = objectReader;
                }
            } else if (fieldClass == BigInteger.class) {
                ObjectReader objectReader = provider.getObjectReader(BigInteger.class);
                if (objectReader != ObjectReaderImplBigInteger.INSTANCE) {
                    initReader = objectReader;
                }
            } else if (fieldClass == Date.class) {
                ObjectReader objectReader = provider.getObjectReader(Date.class);
                if (objectReader != ObjectReaderImplDate.INSTANCE) {
                    initReader = objectReader;
                }
            }
        }
        return initReader;
    }

    protected <T> FieldReader createFieldReaderLambda(
            Class<T> objectClass,
            Type objectType,
            String fieldName,
            int ordinal,
            long features,
            String format,
            Locale locale,
            Object defaultValue,
            String schema,
            Type fieldType,
            Class fieldClass,
            Method method,
            ObjectReader initReader
    ) {
        if (defaultValue != null && defaultValue.getClass() != fieldClass) {
            Function typeConvert = JSONFactory
                    .getDefaultObjectReaderProvider()
                    .getTypeConvert(defaultValue.getClass(), fieldType);
            if (typeConvert != null) {
                defaultValue = typeConvert.apply(defaultValue);
            } else {
                throw new JSONException("illegal defaultValue : " + defaultValue + ", class " + fieldClass.getName());
            }
        }

        JSONSchema jsonSchema = null;
        if (schema != null && !schema.isEmpty()) {
            JSONObject object = JSON.parseObject(schema);
            if (!object.isEmpty()) {
                jsonSchema = JSONSchema.of(object, fieldClass);
            }
        }

        if (initReader != null) {
            BiConsumer function = (BiConsumer) lambdaSetter(objectClass, fieldClass, method);
            return createFieldReader(objectClass, objectType, fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, jsonSchema, method, function, initReader);
        }

        if (fieldType == boolean.class) {
            ObjBoolConsumer function = (ObjBoolConsumer) lambdaSetter(objectClass, fieldClass, method);
            return new FieldReaderBoolValFunc<>(fieldName, ordinal, jsonSchema, method, function);
        }

        if (fieldType == byte.class) {
            ObjByteConsumer function = (ObjByteConsumer) lambdaSetter(objectClass, fieldClass, method);
            return new FieldReaderInt8ValueFunc<>(fieldName, ordinal, jsonSchema, method, function);
        }

        if (fieldType == short.class) {
            ObjShortConsumer function = (ObjShortConsumer) lambdaSetter(objectClass, fieldClass, method);
            return new FieldReaderInt16ValueFunc<>(fieldName, ordinal, features, format, locale, (Short) defaultValue, jsonSchema, method, function);
        }

        if (fieldType == int.class) {
            ObjIntConsumer function = (ObjIntConsumer) lambdaSetter(objectClass, fieldClass, method);
            return new FieldReaderInt32ValueFunc<>(fieldName, ordinal, (Integer) defaultValue, jsonSchema, method, function);
        }

        if (fieldType == long.class) {
            ObjLongConsumer function = (ObjLongConsumer) lambdaSetter(objectClass, fieldClass, method);
            return new FieldReaderInt64ValueFunc<>(fieldName, ordinal, (Long) defaultValue, jsonSchema, method, function);
        }

        if (fieldType == char.class) {
            ObjCharConsumer function = (ObjCharConsumer) lambdaSetter(objectClass, fieldClass, method);
            return new FieldReaderCharValueFunc<>(fieldName, ordinal, format, (Character) defaultValue, jsonSchema, method, function);
        }

        if (fieldType == float.class) {
            ObjFloatConsumer function = (ObjFloatConsumer) lambdaSetter(objectClass, fieldClass, method);
            return new FieldReaderFloatValueFunc<>(fieldName, ordinal, (Float) defaultValue, jsonSchema, method, function);
        }

        if (fieldType == double.class) {
            ObjDoubleConsumer function = (ObjDoubleConsumer) lambdaSetter(objectClass, fieldClass, method);
            return new FieldReaderDoubleValueFunc<>(fieldName, ordinal, (Double) defaultValue, jsonSchema, method, function);
        }

        BiConsumer consumer = (BiConsumer) lambdaSetter(objectClass, fieldClass, method);
        return createFieldReader(objectClass, objectType, fieldName, fieldType, fieldClass, ordinal, features, format, locale, defaultValue, jsonSchema, method, consumer, null);
    }

    protected Object lambdaSetter(Class objectClass, Class fieldClass, Method method) {
        MethodHandles.Lookup lookup = JDKUtils.trustedLookup(objectClass);

        Class<?> returnType = method.getReturnType();
        LambdaSetterInfo lambdaInfo = methodTypeMapping.get(fieldClass);

        MethodType samMethodType;
        MethodType invokedType;
        MethodType methodType = null;
        if (lambdaInfo != null) {
            samMethodType = lambdaInfo.sameMethodMethod;
            invokedType = lambdaInfo.invokedType;
            if (returnType == void.class) {
                methodType = lambdaInfo.methodType;
            }
        } else {
            samMethodType = METHOD_TYPE_VOO;
            invokedType = METHOD_TYPE_BI_CONSUMER;
        }

        if (methodType == null) {
            methodType = MethodType.methodType(returnType, fieldClass);
        }

        try {
            MethodHandle target = lookup.findVirtual(objectClass, method.getName(), methodType);
            MethodType instantiatedMethodType = MethodType.methodType(void.class, objectClass, fieldClass);
            CallSite callSite = LambdaMetafactory.metafactory(
                    lookup,
                    "accept",
                    invokedType,
                    samMethodType,
                    target,
                    instantiatedMethodType
            );

            return callSite
                    .getTarget()
                    .invoke();
        } catch (Throwable e) {
            throw new JSONException("create fieldReader error", e);
        }
    }

    public Function<Consumer, ByteArrayValueConsumer> createByteArrayValueConsumerCreator(
            Class objectClass,
            FieldReader[] fieldReaderArray
    ) {
        return null;
    }

    public Function<Consumer, CharArrayValueConsumer> createCharArrayValueConsumerCreator(
            Class objectClass,
            FieldReader[] fieldReaderArray
    ) {
        return null;
    }
}