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

shade.com.alibaba.fastjson2.JSONPathCompilerReflectASM Maven / Gradle / Ivy

There is a newer version: 1.3.7
Show newest version
package com.alibaba.fastjson2;

import com.alibaba.fastjson2.internal.asm.ASMUtils;
import com.alibaba.fastjson2.internal.asm.ClassWriter;
import com.alibaba.fastjson2.internal.asm.MethodWriter;
import com.alibaba.fastjson2.internal.asm.Opcodes;
import com.alibaba.fastjson2.reader.FieldReader;
import com.alibaba.fastjson2.reader.ObjectReader;
import com.alibaba.fastjson2.util.DynamicClassLoader;
import com.alibaba.fastjson2.writer.FieldWriter;
import com.alibaba.fastjson2.writer.ObjectWriter;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.atomic.AtomicLong;

class JSONPathCompilerReflectASM
        extends JSONPathCompilerReflect {
    // GraalVM not support
    // Android not support
    static final AtomicLong seed = new AtomicLong();
    static final JSONPathCompilerReflectASM INSTANCE = new JSONPathCompilerReflectASM(
            DynamicClassLoader.getInstance()
    );

    static final String DESC_OBJECT_READER = ASMUtils.desc(ObjectReader.class);
    static final String DESC_FIELD_READER = ASMUtils.desc(FieldReader.class);
    static final String DESC_OBJECT_WRITER = ASMUtils.desc(ObjectWriter.class);
    static final String DESC_FIELD_WRITER = ASMUtils.desc(FieldWriter.class);
    static final String TYPE_SINGLE_NAME_PATH_TYPED = ASMUtils.type(SingleNamePathTyped.class);
    static final String METHOD_SINGLE_NAME_PATH_TYPED_INIT = "(Ljava/lang/String;Ljava/lang/Class;" + DESC_OBJECT_READER + DESC_FIELD_READER + DESC_OBJECT_WRITER + DESC_FIELD_WRITER + ")V";

    static final int THIS = 0;

    protected final DynamicClassLoader classLoader;

    public JSONPathCompilerReflectASM(DynamicClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    private boolean support(Class objectClass) {
        boolean externalClass = classLoader.isExternalClass(objectClass);
        int objectClassModifiers = objectClass.getModifiers();
        return Modifier.isAbstract(objectClassModifiers)
                || Modifier.isInterface(objectClassModifiers)
                || !Modifier.isPublic(objectClassModifiers)
                || externalClass;
    }

    @Override
    protected JSONPath compileSingleNamePath(Class objectClass, JSONPathSingleName path) {
        if (support(objectClass)) {
            return super.compileSingleNamePath(objectClass, path);
        }

        String fieldName = path.name;

        String TYPE_OBJECT = ASMUtils.type(objectClass);

        ObjectReader objectReader = path.getReaderContext().getObjectReader(objectClass);
        FieldReader fieldReader = objectReader.getFieldReader(fieldName);

        ObjectWriter objectWriter = path.getWriterContext().getObjectWriter(objectClass);
        FieldWriter fieldWriter = objectWriter.getFieldWriter(fieldName);

        ClassWriter cw = new ClassWriter(null);

        String className = "JSONPath_" + seed.incrementAndGet();
        String classNameType;
        String classNameFull;

        Package pkg = JSONPathCompilerReflectASM.class.getPackage();
        if (pkg != null) {
            String packageName = pkg.getName();
            int packageNameLength = packageName.length();
            int charsLength = packageNameLength + 1 + className.length();
            char[] chars = new char[charsLength];
            packageName.getChars(0, packageName.length(), chars, 0);
            chars[packageNameLength] = '.';
            className.getChars(0, className.length(), chars, packageNameLength + 1);
            classNameFull = new String(chars);

            chars[packageNameLength] = '/';
            for (int i = 0; i < packageNameLength; ++i) {
                if (chars[i] == '.') {
                    chars[i] = '/';
                }
            }
            classNameType = new String(chars);
        } else {
            classNameType = className;
            classNameFull = className;
        }

        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, classNameType, TYPE_SINGLE_NAME_PATH_TYPED, new String[]{});

        {
            final int PATH = 1, CLASS = 2, OBJECT_READER = 3, FIELD_READER = 4, OBJECT_WRITER = 5, FIELD_WRITER = 6;

            MethodWriter mw = cw.visitMethod(
                    Opcodes.ACC_PUBLIC,
                    "",
                    METHOD_SINGLE_NAME_PATH_TYPED_INIT,
                    64
            );
            mw.visitVarInsn(Opcodes.ALOAD, THIS);
            mw.visitVarInsn(Opcodes.ALOAD, PATH);
            mw.visitVarInsn(Opcodes.ALOAD, CLASS);
            mw.visitVarInsn(Opcodes.ALOAD, OBJECT_READER);
            mw.visitVarInsn(Opcodes.ALOAD, FIELD_READER);
            mw.visitVarInsn(Opcodes.ALOAD, OBJECT_WRITER);
            mw.visitVarInsn(Opcodes.ALOAD, FIELD_WRITER);

            mw.visitMethodInsn(Opcodes.INVOKESPECIAL, TYPE_SINGLE_NAME_PATH_TYPED, "", METHOD_SINGLE_NAME_PATH_TYPED_INIT, false);

            mw.visitInsn(Opcodes.RETURN);
            mw.visitMaxs(3, 3);
        }

        if (fieldReader != null) {
            Class fieldClass = fieldReader.fieldClass;
            int OBJECT = 1, VALUE = 2;

            if (fieldClass == int.class) {
                MethodWriter mw = cw.visitMethod(
                        Opcodes.ACC_PUBLIC,
                        "setInt",
                        "(Ljava/lang/Object;I)V",
                        64
                );
                mw.visitVarInsn(Opcodes.ALOAD, OBJECT);
                mw.visitTypeInsn(Opcodes.CHECKCAST, TYPE_OBJECT);
                mw.visitVarInsn(Opcodes.ILOAD, VALUE);

                gwSetValue(mw, TYPE_OBJECT, fieldReader);

                mw.visitInsn(Opcodes.RETURN);
                mw.visitMaxs(2, 2);
            }
            if (fieldClass == long.class) {
                MethodWriter mw = cw.visitMethod(
                        Opcodes.ACC_PUBLIC,
                        "setLong",
                        "(Ljava/lang/Object;J)V",
                        64
                );
                mw.visitVarInsn(Opcodes.ALOAD, OBJECT);
                mw.visitTypeInsn(Opcodes.CHECKCAST, TYPE_OBJECT);
                mw.visitVarInsn(Opcodes.LLOAD, VALUE);

                gwSetValue(mw, TYPE_OBJECT, fieldReader);

                mw.visitInsn(Opcodes.RETURN);
                mw.visitMaxs(2, 2);
            }

            {
                MethodWriter mw = cw.visitMethod(
                        Opcodes.ACC_PUBLIC,
                        "set",
                        "(Ljava/lang/Object;Ljava/lang/Object;)V",
                        64
                );
                mw.visitVarInsn(Opcodes.ALOAD, OBJECT);
                mw.visitTypeInsn(Opcodes.CHECKCAST, TYPE_OBJECT);
                mw.visitVarInsn(Opcodes.ALOAD, VALUE);
                if (fieldClass == int.class) {
                    mw.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
                    mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "intValue", "()I", false);
                } else if (fieldClass == long.class) {
                    mw.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
                    mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "longValue", "()J", false);
                } else if (fieldClass == float.class) {
                    mw.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
                    mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "floatValue", "()F", false);
                } else if (fieldClass == double.class) {
                    mw.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
                    mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "doubleValue", "()D", false);
                } else if (fieldClass == short.class) {
                    mw.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
                    mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "shortValue", "()S", false);
                } else if (fieldClass == byte.class) {
                    mw.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
                    mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "byteValue", "()B", false);
                } else if (fieldClass == boolean.class) {
                    mw.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
                    mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
                } else if (fieldClass == char.class) {
                    mw.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Character");
                    mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
                }
                gwSetValue(mw, TYPE_OBJECT, fieldReader);

                mw.visitInsn(Opcodes.RETURN);
                mw.visitMaxs(2, 2);
            }
        }

        if (fieldWriter != null) {
            Class fieldClass = fieldReader.fieldClass;

            int OBJECT = 1;

            MethodWriter mw = cw.visitMethod(
                    Opcodes.ACC_PUBLIC,
                    "eval",
                    "(Ljava/lang/Object;)Ljava/lang/Object;",
                    64
            );
            mw.visitVarInsn(Opcodes.ALOAD, OBJECT);
            mw.visitTypeInsn(Opcodes.CHECKCAST, TYPE_OBJECT);
            gwGetValue(mw, TYPE_OBJECT, fieldWriter);
            if (fieldClass == int.class) {
                mw.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
            } else if (fieldClass == long.class) {
                mw.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
            } else if (fieldClass == float.class) {
                mw.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
            } else if (fieldClass == double.class) {
                mw.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
            } else if (fieldClass == short.class) {
                mw.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
            } else if (fieldClass == byte.class) {
                mw.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
            } else if (fieldClass == boolean.class) {
                mw.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
            } else if (fieldClass == char.class) {
                mw.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
            }

            mw.visitInsn(Opcodes.ARETURN);
            mw.visitMaxs(2, 2);
        }

        byte[] code = cw.toByteArray();

        Class readerClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length);

        try {
            Constructor constructor = readerClass.getConstructors()[0];
            return (JSONPath) constructor
                    .newInstance(path.path, objectClass, objectReader, fieldReader, objectWriter, fieldWriter);
        } catch (Throwable e) {
            throw new JSONException("compile jsonpath error, path " + path.path + ", objectType " + objectClass.getTypeName(), e);
        }

//        return new SingleNamePathTyped(path.path, objectClass, objectReader, fieldReader, objectWriter, fieldWriter);
    }

    private void gwSetValue(MethodWriter mw, String TYPE_OBJECT, FieldReader fieldReader) {
        Method method = fieldReader.method;
        Field field = fieldReader.field;
        Class fieldClass = fieldReader.fieldClass;
        String fieldClassDesc = ASMUtils.desc(fieldClass);

        if (method != null) {
            Class returnType = method.getReturnType();
            String methodDesc = '(' + fieldClassDesc + ')' + ASMUtils.desc(returnType);
            mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_OBJECT, method.getName(), methodDesc, false);
            if (returnType != Void.TYPE) { // builder
                mw.visitInsn(Opcodes.POP);
            }
        } else {
            mw.visitFieldInsn(Opcodes.PUTFIELD, TYPE_OBJECT, field.getName(), fieldClassDesc);
        }
    }

    private void gwGetValue(MethodWriter mw, String TYPE_OBJECT, FieldWriter fieldWriter) {
        Method method = fieldWriter.method;
        Field field = fieldWriter.field;
        Class fieldClass = fieldWriter.fieldClass;
        String fieldClassDesc = ASMUtils.desc(fieldClass);

        if (method != null) {
            String methodDesc = "()" + fieldClassDesc;
            mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_OBJECT, method.getName(), methodDesc, false);
        } else {
            mw.visitFieldInsn(Opcodes.GETFIELD, TYPE_OBJECT, field.getName(), fieldClassDesc);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy