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

one.nio.gen.BytecodeGenerator Maven / Gradle / Ivy

/*
 * Copyright 2015 Odnoklassniki Ltd, Mail.Ru Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package one.nio.gen;

import one.nio.mgt.Management;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicInteger;

import static java.nio.file.StandardOpenOption.*;

public class BytecodeGenerator extends ClassLoader implements BytecodeGeneratorMXBean, Opcodes {
    private static final Log log = LogFactory.getLog(BytecodeGenerator.class);

    public static final BytecodeGenerator INSTANCE = new BytecodeGenerator();
    public static final String MAGIC_CLASS = "sun/reflect/MagicAccessorImpl";

    static {
        Management.registerMXBean(INSTANCE, "one.nio.gen:type=BytecodeGenerator");
    }

    protected final AtomicInteger totalClasses;
    protected final AtomicInteger totalBytes;
    protected String dumpPath;

    public BytecodeGenerator() {
        super(BytecodeGenerator.class.getClassLoader());
        this.totalClasses = new AtomicInteger();
        this.totalBytes = new AtomicInteger();
        this.dumpPath = System.getProperty("one.nio.gen.dump");
    }

    public Class defineClass(byte[] classData) {
        Class result = super.defineClass(null, classData, 0, classData.length, null);
        totalClasses.incrementAndGet();
        totalBytes.addAndGet(classData.length);
        if (dumpPath != null && !"".equals(dumpPath)) {
            dumpClass(classData, result.getSimpleName());
        }
        return result;
    }

    public synchronized Class defineClassIfNotExists(String className, byte[] classData) {
        Class result = findLoadedClass(className);
        if (result == null) {
            result = defineClass(classData);
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    public  T instantiate(byte[] classData, Class iface) {
        try {
            return (T) defineClass(classData).newInstance();
        } catch (Exception e) {
            throw new IllegalArgumentException("Cannot instantiate class", e);
        }
    }

    public void dumpClass(byte[] classData, String className) {
        try {
            Files.write(Paths.get(dumpPath, className + ".class"), classData, WRITE, CREATE, TRUNCATE_EXISTING);
        } catch (IOException e) {
            log.error("Could not dump " + className, e);
        }
    }

    public static void emitGetField(MethodVisitor mv, Field f) {
        int opcode = (f.getModifiers() & Modifier.STATIC) != 0 ? GETSTATIC : GETFIELD;
        String holder = Type.getInternalName(f.getDeclaringClass());
        String name = f.getName();
        String sig = Type.getDescriptor(f.getType());
        mv.visitFieldInsn(opcode, holder, name, sig);
    }

    public static void emitPutField(MethodVisitor mv, Field f) {
        int opcode = (f.getModifiers() & Modifier.STATIC) != 0 ? PUTSTATIC : PUTFIELD;
        String holder = Type.getInternalName(f.getDeclaringClass());
        String name = f.getName();
        String sig = Type.getDescriptor(f.getType());
        mv.visitFieldInsn(opcode, holder, name, sig);
    }

    public static void emitInvoke(MethodVisitor mv, Method m) {
        int opcode;
        if ((m.getModifiers() & Modifier.STATIC) != 0) {
            opcode = INVOKESTATIC;
        } else if ((m.getModifiers() & Modifier.PRIVATE) != 0) {
            opcode = INVOKESPECIAL;
        } else if (m.getDeclaringClass().isInterface()) {
            opcode = INVOKEINTERFACE;
        } else {
            opcode = INVOKEVIRTUAL;
        }

        String holder = Type.getInternalName(m.getDeclaringClass());
        String name = m.getName();
        String sig = Type.getMethodDescriptor(m);
        mv.visitMethodInsn(opcode, holder, name, sig);
    }

    public static void emitInvoke(MethodVisitor mv, Constructor c) {
        String holder = Type.getInternalName(c.getDeclaringClass());
        String name = c.getName();
        String sig = Type.getConstructorDescriptor(c);
        mv.visitMethodInsn(INVOKESPECIAL, holder, name, sig);
    }

    public static void emitThrow(MethodVisitor mv, String exceptionClass, String message) {
        mv.visitTypeInsn(NEW, exceptionClass);
        mv.visitInsn(DUP);
        mv.visitLdcInsn(message);
        mv.visitMethodInsn(INVOKESPECIAL, exceptionClass, "", "(Ljava/lang/String;)V");
        mv.visitInsn(ATHROW);
    }

    public static void emitInt(MethodVisitor mv, int c) {
        if (c >= -1 && c <= 5) {
            mv.visitInsn(ICONST_0 + c);
        } else if (c >= Byte.MIN_VALUE && c <= Byte.MAX_VALUE) {
            mv.visitIntInsn(BIPUSH, c);
        } else if (c >= Short.MIN_VALUE && c <= Short.MAX_VALUE) {
            mv.visitIntInsn(SIPUSH, c);
        } else {
            mv.visitLdcInsn(c);
        }
    }

    public static void emitLong(MethodVisitor mv, long c) {
        if (c >= 0 && c <= 1) {
            mv.visitInsn(LCONST_0 + (int) c);
        } else {
            mv.visitLdcInsn(c);
        }
    }

    public static void emitFloat(MethodVisitor mv, float c) {
        if (c == 0.0f) {
            mv.visitInsn(FCONST_0);
        } else if (c == 1.0f) {
            mv.visitInsn(FCONST_1);
        } else if (c == 2.0f) {
            mv.visitInsn(FCONST_2);
        } else {
            mv.visitLdcInsn(c);
        }
    }

    public static void emitDouble(MethodVisitor mv, double c) {
        if (c == 0.0) {
            mv.visitInsn(DCONST_0);
        } else if (c == 1.0) {
            mv.visitInsn(DCONST_1);
        } else {
            mv.visitLdcInsn(c);
        }
    }

    public static void emitBoxing(MethodVisitor mv, Class type) {
        if (type == boolean.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
        } else if (type == byte.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
        } else if (type == char.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
        } else if (type == short.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
        } else if (type == int.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
        } else if (type == float.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
        } else if (type == long.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
        } else if (type == double.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
        } else if (type == void.class) {
            mv.visitInsn(ACONST_NULL);
        } else {
            throw new IllegalArgumentException("Not a primitive type: " + type);
        }
    }

    public static void emitUnboxing(MethodVisitor mv, Class type) {
        if (type == Boolean.class) {
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
        } else if (type == Byte.class) {
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
        } else if (type == Character.class) {
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
        } else if (type == Short.class) {
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
        } else if (type == Integer.class) {
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
        } else if (type == Float.class) {
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
        } else if (type == Long.class) {
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
        } else if (type == Double.class) {
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
        } else {
            throw new IllegalArgumentException("Not a wrapper type: " + type);
        }
    }

    @Override
    public String getDumpPath() {
        return dumpPath;
    }

    @Override
    public void setDumpPath(String dumpPath) {
        this.dumpPath = dumpPath;
    }

    @Override
    public int getTotalClasses() {
        return totalClasses.get();
    }

    @Override
    public int getTotalBytes() {
        return totalBytes.get();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy