/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.asm;
import com.liferay.portal.kernel.util.ReflectionUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* @author Matthew Tambara
*/
public class ASMWrapperUtil {
public static <T> T createASMWrapper(
ClassLoader classLoader, Class<T> interfaceClass, Object delegateObject,
T defaultObject) {
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass + " is not an interface");
}
Class<?> clazz = delegateObject.getClass();
Package pkg = clazz.getPackage();
StringBundler sb = new StringBundler(4);
sb.append(pkg.getName());
sb.append(StringPool.PERIOD);
sb.append(interfaceClass.getSimpleName());
sb.append("ASMWrapper");
String asmWrapperClassName = sb.toString();
Class<?> asmWrapperClass = null;
synchronized (classLoader) {
try {
try {
asmWrapperClass = classLoader.loadClass(
asmWrapperClassName);
}
catch (ClassNotFoundException cnfe) {
byte[] classData = _generateASMWrapperClassData(
asmWrapperClassName.replace('.', '/'), interfaceClass,
delegateObject, defaultObject);
asmWrapperClass = (Class<?>)_defineClassMethod.invoke(
classLoader, asmWrapperClassName, classData, 0,
classData.length);
}
Constructor<?> constructor =
asmWrapperClass.getDeclaredConstructor(
delegateObject.getClass(), defaultObject.getClass());
constructor.setAccessible(true);
return (T)constructor.newInstance(
delegateObject, defaultObject);
}
catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
private static <T> byte[] _generateASMWrapperClassData(
String asmWrapperClassBinaryName, Class<T> interfaceClass,
Object delegateObject, T defaultObject) {
String interfaceClassBinaryName = _getClassBinaryName(interfaceClass);
Class<?> delegateObjectClass = delegateObject.getClass();
String delegateObjectClassDescriptor = Type.getDescriptor(
delegateObjectClass);
String defaultObjectClassDescriptor = Type.getDescriptor(
defaultObject.getClass());
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classWriter.visit(
Opcodes.V1_7, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
asmWrapperClassBinaryName, null, _getClassBinaryName(Object.class),
new String[] {interfaceClassBinaryName});
// Fields
FieldVisitor fieldVisitor = classWriter.visitField(
Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, "_delegate",
delegateObjectClassDescriptor, null, null);
fieldVisitor.visitEnd();
fieldVisitor = classWriter.visitField(
Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, "_default",
defaultObjectClassDescriptor, null, null);
fieldVisitor.visitEnd();
// Constructor
MethodVisitor methodVisitor = classWriter.visitMethod(
Opcodes.ACC_PRIVATE, "<init>",
Type.getMethodDescriptor(
Type.VOID_TYPE, Type.getType(delegateObjectClass),
Type.getType(defaultObjectClassDescriptor)),
null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(
Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 1);
methodVisitor.visitFieldInsn(
Opcodes.PUTFIELD, asmWrapperClassBinaryName, "_delegate",
delegateObjectClassDescriptor);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitVarInsn(Opcodes.ALOAD, 2);
methodVisitor.visitFieldInsn(
Opcodes.PUTFIELD, asmWrapperClassBinaryName, "_default",
defaultObjectClassDescriptor);
methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
// Delegate and fallback methods
for (Method method : interfaceClass.getMethods()) {
try {
Method delegateMethod = delegateObjectClass.getMethod(
method.getName(), method.getParameterTypes());
_generateMethod(
classWriter, delegateMethod, asmWrapperClassBinaryName,
"_delegate", delegateObjectClassDescriptor,
_getClassBinaryName(delegateObjectClass));
}
catch (NoSuchMethodException nsme) {
_generateMethod(
classWriter, method, asmWrapperClassBinaryName, "_default",
defaultObjectClassDescriptor,
_getClassBinaryName(defaultObject.getClass()));
}
}
_generateMethod(
classWriter, _equalsMethod, asmWrapperClassBinaryName, "_delegate",
delegateObjectClassDescriptor,
_getClassBinaryName(delegateObjectClass));
_generateMethod(
classWriter, _hashCodeMethod, asmWrapperClassBinaryName,
"_delegate", delegateObjectClassDescriptor,
_getClassBinaryName(delegateObjectClass));
_generateMethod(
classWriter, _toStringMethod, asmWrapperClassBinaryName,
"_delegate", delegateObjectClassDescriptor,
_getClassBinaryName(delegateObjectClass));
classWriter.visitEnd();
return classWriter.toByteArray();
}
private static void _generateMethod(
ClassWriter classWriter, Method method,
String asmWrapperClassBinaryName, String fieldName,
String targetClassDescriptor, String targetClassBinaryName) {
Class<?>[] exceptions = method.getExceptionTypes();
String[] exceptionsBinaryClassNames = new String[exceptions.length];
for (int i = 0; i < exceptions.length; i++) {
exceptionsBinaryClassNames[i] = _getClassBinaryName(exceptions[i]);
}
MethodVisitor methodVisitor = classWriter.visitMethod(
Opcodes.ACC_PUBLIC, method.getName(),
Type.getMethodDescriptor(method), null, exceptionsBinaryClassNames);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(
Opcodes.GETFIELD, asmWrapperClassBinaryName, fieldName,
targetClassDescriptor);
int i = 1;
for (Class<?> parameterClass : method.getParameterTypes()) {
Type type = Type.getType(parameterClass);
methodVisitor.visitVarInsn(type.getOpcode(Opcodes.ILOAD), i);
i += type.getSize();
}
methodVisitor.visitMethodInsn(
Opcodes.INVOKEVIRTUAL, targetClassBinaryName, method.getName(),
Type.getMethodDescriptor(method), false);
Type type = Type.getType(method.getReturnType());
methodVisitor.visitInsn(type.getOpcode(Opcodes.IRETURN));
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
}
private static String _getClassBinaryName(Class<?> clazz) {
String className = clazz.getName();
return className.replace('.', '/');
}
private ASMWrapperUtil() {
}
private static final Method _defineClassMethod;
private static final Method _equalsMethod;
private static final Method _hashCodeMethod;
private static final Method _toStringMethod;
static {
try {
_defineClassMethod = ReflectionUtil.getDeclaredMethod(
ClassLoader.class, "defineClass", String.class, byte[].class,
int.class, int.class);
_equalsMethod = ReflectionUtil.getDeclaredMethod(
Object.class, "equals", Object.class);
_hashCodeMethod = ReflectionUtil.getDeclaredMethod(
Object.class, "hashCode");
_toStringMethod = ReflectionUtil.getDeclaredMethod(
Object.class, "toString");
}
catch (Throwable t) {
throw new ExceptionInInitializerError(t);
}
}
}