EishayClassGen.java

package com.alibaba.fastjson2.benchmark.eishay.gen;

import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.TypeReference;
import com.alibaba.fastjson2.benchmark.eishay.EishayFuryWriteNoneCache;
import com.alibaba.fastjson2.reader.ObjectReaderProvider;
import com.alibaba.fastjson2.util.DynamicClassLoader;
import com.alibaba.fastjson2.util.TypeUtils;
import com.alibaba.fastjson2.writer.ObjectWriterProvider;
import org.apache.commons.io.IOUtils;
import org.objectweb.asm.*;

import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static org.objectweb.asm.Opcodes.*;

public class EishayClassGen {
    public Class genMedia(DynamicClassLoader classLoader, String packageName) throws Exception {
        classLoader.definePackage(packageName.replace('/', '.'));

        LinkedHashMap<String, byte[]> codes = new LinkedHashMap();
        LinkedHashMap<String, Class> classes = new LinkedHashMap();

        genCode(packageName, codes);
        codes.forEach((name, code) -> {
            Class<?> clazz = classLoader.loadClass(name, code, 0, code.length);
            classes.put(name, clazz);
        });

        String mediaContentClassName = packageName.replace('/', '.') + ".MediaContent";
        return classes.get(mediaContentClassName);
    }

    public void genCode(String packageName, Map<String, byte[]> classBytes) {
        String playerType = packageName + "/Media$Player";
        String playerClassName = playerType.replace('/', '.');
        byte[] playerCode = genEnum(playerType, "JAVA", "FLASH");
        classBytes.put(playerClassName, playerCode);

        String mediaType = packageName + "/Media";
        String mediaClassName = mediaType.replace('/', '.');
        byte[] mediaCode = genClass(mediaType,
                new FieldInfo[]{
                        new FieldInfo("bitrate", long.class),
                        new FieldInfo("duration", long.class),
                        new FieldInfo("format", String.class),
                        new FieldInfo("height", int.class),
                        new FieldInfo("persons", TypeReference.collectionType(List.class, String.class)),
                        new FieldInfo("player", "L" + playerType + ";", null),
                        new FieldInfo("size", long.class),
                        new FieldInfo("title", String.class),
                        new FieldInfo("uri", String.class),
                        new FieldInfo("width", int.class),
                        new FieldInfo("copyright", String.class)
                });
        classBytes.put(mediaClassName, mediaCode);

        String sizeType = packageName + "/Image$Size";
        String sizeClassName = sizeType.replace('/', '.');
        byte[] sizeCode = genEnum(sizeType, "SMALL", "LARGE");
        classBytes.put(sizeClassName, sizeCode);

        String imageType = packageName + "/Image";
        String imageClassName = imageType.replace('/', '.');
        byte[] imageCode = genClass(imageType,
                new FieldInfo[]{
                        new FieldInfo("height", int.class),
                        new FieldInfo("size", "L" + sizeType + ";", null),
                        new FieldInfo("title", String.class),
                        new FieldInfo("uri", String.class),
                        new FieldInfo("width", int.class)
                }
        );
        classBytes.put(imageClassName, imageCode);

        String mediaContentType = packageName + "/MediaContent";
        String mediaContentClassName = mediaContentType.replace('/', '.');
        byte[] mediaContentCode = genClass(mediaContentType,
                new FieldInfo("media", "L" + mediaType + ";", null),
                new FieldInfo("images", "Ljava/util/List;", "Ljava/util/List<L" + imageType + ";>;")
        );
        classBytes.put(mediaContentClassName, mediaContentCode);
    }

    static class FieldInfo {
        final String name;
        final String desc;
        final String signature;

        FieldInfo(String name, java.lang.reflect.Type type) {
            this.name = name;
            Class fieldClass = TypeUtils.getClass(type);
            this.desc = Type.getDescriptor(fieldClass);
            this.signature = getSignature(type);
        }

        FieldInfo(String name, String desc, String signature) {
            this.name = name;
            this.desc = desc;
            this.signature = signature;
        }
    }

    static String getSignature(java.lang.reflect.Type type) {
        if (type instanceof ParameterizedType) {
            ParameterizedType paramType = (ParameterizedType) type;
            java.lang.reflect.Type rawType = paramType.getRawType();
            java.lang.reflect.Type[] arguments = paramType.getActualTypeArguments();
            if (rawType == List.class) {
                // "Ljava/util/List<L" + type + ";>;",
                Class itemType = (Class) arguments[0];
                String signature = "Ljava/util/List<" + Type.getType(itemType) + ">;";
                return signature;
            }
        }
        return null;
    }

    public byte[] genClass(String type, FieldInfo... fields) {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cw.visit(
                Opcodes.V1_8,
                ACC_PUBLIC + ACC_SUPER,
                type,
                null,
                "java/lang/Object",
                new String[] {
                        "java/io/Serializable"
                }
        );

        for (FieldInfo field : fields) {
            FieldVisitor fv = cw.visitField(ACC_PUBLIC, field.name, field.desc, field.signature, null);
            fv.visitEnd();
        }

        {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V",
                    "()V", null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            mv.visitInsn(RETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        cw.visitEnd();

        return cw.toByteArray();
    }

    public byte[] genEnum(String type, String... values) {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        String desc = "L" + type + ";";
        cw.visit(
                Opcodes.V1_8,
                ACC_PUBLIC + ACC_FINAL + ACC_SUPER + ACC_ENUM,
                type,
                "Ljava/lang/Enum<L" + type + ";>;",
                "java/lang/Enum",
                null
        );

        for (String value : values) {
            FieldVisitor fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC + ACC_ENUM, value, desc, null, null);
            fv.visitEnd();
        }

        {
            FieldVisitor fv = cw.visitField(
                    ACC_PRIVATE + ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC,
                    "$VALUES", "[" + desc, null, null);
            fv.visitEnd();
        }

        {
            MethodVisitor mv = cw.visitMethod(ACC_PRIVATE, "<init>", "(Ljava/lang/String;I)V",
                    "()V", null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitVarInsn(ILOAD, 2);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Enum", "<init>",
                    "(Ljava/lang/String;I)V");
            mv.visitInsn(RETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "values",
                    "()[" + desc, null, null);
            mv.visitCode();
            mv.visitFieldInsn(GETSTATIC, type, "$VALUES", "[" + desc);
            mv.visitMethodInsn(INVOKEVIRTUAL, "[" + desc, "clone",
                    "()Ljava/lang/Object;");
            mv.visitTypeInsn(CHECKCAST, "[" + desc);
            mv.visitInsn(ARETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "valueOf",
                    "(Ljava/lang/String;)" + desc, null, null);
            mv.visitCode();
            mv.visitLdcInsn(Type.getType(desc));
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Enum", "valueOf",
                    "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;");
            mv.visitTypeInsn(CHECKCAST, type);
            mv.visitInsn(ARETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        {
            MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
            mv.visitCode();

            for (int i = 0; i < values.length; i++) {
                String value = values[i];
                mv.visitTypeInsn(NEW, type);
                mv.visitInsn(DUP);
                mv.visitLdcInsn(value);
                mv.visitLdcInsn(i);
                mv.visitMethodInsn(INVOKESPECIAL, type, "<init>", "(Ljava/lang/String;I)V");
                mv.visitFieldInsn(PUTSTATIC, type, value, desc);
            }

            mv.visitLdcInsn(values.length);
            mv.visitTypeInsn(ANEWARRAY, type);

            for (int i = 0; i < values.length; i++) {
                String value = values[i];

                mv.visitInsn(DUP);
                mv.visitLdcInsn(i);
                mv.visitFieldInsn(GETSTATIC, type, value, desc);
                mv.visitInsn(AASTORE);
            }

            mv.visitFieldInsn(PUTSTATIC, type, "$VALUES", "[" + desc);
            mv.visitInsn(RETURN);
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }

        cw.visitEnd();

        return cw.toByteArray();
    }

    public byte[][] genFastjsonJSONBBytes(int count, JSONWriter.Feature[] writerFeatures) throws Exception {
        try (InputStream is = EishayFuryWriteNoneCache.class.getClassLoader()
                .getResourceAsStream("data/eishay.json")
        ) {
            String str = IOUtils.toString(is, "UTF-8");

            DynamicClassLoader classLoader = new DynamicClassLoader();
            byte[][] bytes = new byte[count][];
            EishayClassGen gen = new EishayClassGen();
            for (int i = 0; i < count; i++) {
                String packageName = "com/alibaba/fastjson2/benchmark/eishay" + i;
                Class objectClass = gen.genMedia(classLoader, packageName);
                ObjectReaderProvider provider = new ObjectReaderProvider();
                Object object = JSONReader.of(str, JSONFactory.createReadContext(provider)).read(objectClass);

                ObjectWriterProvider writerProvider = new ObjectWriterProvider();
                bytes[i] = JSONB.toBytes(object, JSONFactory.createWriteContext(writerProvider, writerFeatures));
            }
            return bytes;
        }
    }

    public LinkedHashMap<String, byte[]> genCodes(int count) throws Exception {
        LinkedHashMap<String, byte[]> codeMap = new LinkedHashMap();
        EishayClassGen gen = new EishayClassGen();
        for (int i = 0; i < count; i++) {
            String packageName = "com/alibaba/fastjson2/benchmark/eishay" + i;
            gen.genCode(packageName, codeMap);
        }
        return codeMap;
    }

    public byte[][] genFuryBytes(int count) throws Exception {
//        org.apache.fury.ThreadSafeFury fury = org.apache.fury.Fury.builder()
//                .withLanguage(org.apache.fury.Language.JAVA)
//                .withReferenceTracking(true)
//                .disableSecureMode()
//                .buildThreadSafeFury();
//
//        try (InputStream is = EishayFuryWriteNoneCache.class.getClassLoader()
//                .getResourceAsStream("data/eishay.json")
//        ) {
//            String str = IOUtils.toString(is, "UTF-8");
//
//            DynamicClassLoader classLoader = new DynamicClassLoader();
//            byte[][] bytes = new byte[count][];
//            EishayClassGen gen = new EishayClassGen();
//            for (int i = 0; i < count; i++) {
//                String packageName = "com/alibaba/fastjson2/benchmark/eishay" + i;
//                Class objectClass = gen.genMedia(classLoader, packageName);
//                ObjectReaderProvider provider = new ObjectReaderProvider();
//                Object object = JSONReader.of(str, JSONFactory.createReadContext(provider)).read(objectClass);
//                bytes[i] = fury.serialize(object);
//                System.out.println(java.time.LocalDateTime.now() + " write " + i + " done");
//            }
//            return bytes;
//        }
        throw new JSONException("TODO");
    }
}