/*
* 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.File;
import java.io.FileOutputStream;
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.util.concurrent.atomic.AtomicInteger;
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> T instantiate(byte[] classData, Class<T> 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 {
File f = new File(dumpPath, className + ".class");
FileOutputStream fos = new FileOutputStream(f);
fos.write(classData);
fos.close();
} 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, "<init>", "(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();
}
}