blob: ff07fd636d214bbbbe66f20f9c92c00e04ed92ef [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.karaf.service.interceptor.impl.runtime.proxy;
import static org.objectweb.asm.ClassReader.SKIP_CODE;
import static org.objectweb.asm.ClassReader.SKIP_DEBUG;
import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
import static org.objectweb.asm.Opcodes.AALOAD;
import static org.objectweb.asm.Opcodes.AASTORE;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.ACC_VARARGS;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ASM7;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.BIPUSH;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Opcodes.ICONST_2;
import static org.objectweb.asm.Opcodes.ICONST_3;
import static org.objectweb.asm.Opcodes.ICONST_4;
import static org.objectweb.asm.Opcodes.ICONST_5;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.SIPUSH;
import static org.objectweb.asm.Opcodes.V10;
import static org.objectweb.asm.Opcodes.V11;
import static org.objectweb.asm.Opcodes.V12;
import static org.objectweb.asm.Opcodes.V13;
import static org.objectweb.asm.Opcodes.V1_8;
import static org.objectweb.asm.Opcodes.V9;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
// forked from OWB
public class AsmProxyFactory {
private static final Method[] EMPTY_METHODS = new Method[0];
private static final String FIELD_INTERCEPTOR_HANDLER = "karafInterceptorProxyHandler";
private static final String FIELD_INTERCEPTED_METHODS = "karafInterceptorProxyMethods";
public <T> T create(final Class<?> clazz, final InterceptorHandler handler) {
try {
final T proxy = (T) clazz.getConstructor().newInstance();
final Field invocationHandlerField = clazz.getDeclaredField(FIELD_INTERCEPTOR_HANDLER);
invocationHandlerField.setAccessible(true);
invocationHandlerField.set(proxy, handler);
return proxy;
} catch (final IllegalAccessException | NoSuchFieldException | NoSuchMethodException | InstantiationException e) {
throw new IllegalStateException(e);
} catch (final InvocationTargetException ite) {
throw new IllegalStateException(ite.getTargetException());
}
}
public <T> Class<T> createProxyClass(final ProxyFactory.ProxyClassLoader classLoader,
final String proxyClassName, final Class<?>[] classesToProxy,
final Method[] interceptedMethods) {
try {
return (Class<T>) Class.forName(proxyClassName, true, classLoader);
} catch (final ClassNotFoundException cnfe) {
return doCreateProxyClass(classLoader, proxyClassName, classesToProxy, interceptedMethods);
}
}
private <T> Class<T> doCreateProxyClass(final ProxyFactory.ProxyClassLoader classLoader, final String proxyClassName,
final Class<?>[] classesToProxy, final Method[] interceptedMethods) {
final String proxyClassFileName = proxyClassName.replace('.', '/');
final byte[] proxyBytes = generateProxy(classesToProxy, proxyClassFileName, sortOutDuplicateMethods(interceptedMethods));
final Class<T> proxyCLass = classLoader.getOrRegister(proxyClassName, proxyBytes, classesToProxy[0].getPackage(), classesToProxy[0].getProtectionDomain());
try {
final Field interceptedMethodsField = proxyCLass.getDeclaredField(FIELD_INTERCEPTED_METHODS);
interceptedMethodsField.setAccessible(true);
interceptedMethodsField.set(null, interceptedMethods);
} catch (final Exception e) {
throw new IllegalStateException(e);
}
return proxyCLass;
}
private Method[] sortOutDuplicateMethods(final Method[] methods) {
if (methods == null || methods.length == 0) {
return null;
}
final List<Method> duplicates = new ArrayList<>();
for (final Method outer : methods) {
for (final Method inner : methods) {
if (inner != outer
&& hasSameSignature(outer, inner)
&& !(duplicates.contains(outer) || duplicates.contains(inner))) {
duplicates.add(inner);
}
}
}
final List<Method> outsorted = new ArrayList<>(Arrays.asList(methods));
outsorted.removeAll(duplicates);
return outsorted.toArray(EMPTY_METHODS);
}
private boolean hasSameSignature(Method a, Method b) {
return a.getName().equals(b.getName())
&& a.getReturnType().equals(b.getReturnType())
&& Arrays.equals(a.getParameterTypes(), b.getParameterTypes());
}
private void createConstructor(final ClassWriter cw, final String proxyClassFileName, final Class<?> classToProxy,
final String classFileName) {
Constructor superDefaultCt;
String parentClassFileName = classFileName;
String descriptor = "()V";
try {
if (classToProxy.isInterface()) {
parentClassFileName = Type.getInternalName(Object.class);
superDefaultCt = Object.class.getConstructor(null);
descriptor = Type.getConstructorDescriptor(superDefaultCt);
}
} catch (final NoSuchMethodException nsme) {
// no worries
}
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", descriptor, null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, parentClassFileName, "<init>", descriptor, false);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ACONST_NULL);
mv.visitFieldInsn(PUTFIELD, proxyClassFileName, FIELD_INTERCEPTOR_HANDLER, Type.getDescriptor(InterceptorHandler.class));
mv.visitInsn(RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
private byte[] generateProxy(final Class<?>[] classesToProxy, final String proxyClassFileName, final Method[] interceptedMethods) {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
final String classFileName = classesToProxy[0].getName().replace('.', '/');
final String[] interfaces = Stream.of(classesToProxy)
.filter(Class::isInterface)
.map(Type::getInternalName)
.toArray(String[]::new);
final String superClassName;
if (interfaces.length == classesToProxy.length) {
superClassName = Type.getInternalName(Object.class);
} else {
superClassName = Type.getInternalName(classesToProxy[0]);
}
cw.visit(findJavaVersion(classesToProxy[0]), ACC_PUBLIC + ACC_SUPER + ACC_SYNTHETIC, proxyClassFileName, null, superClassName, interfaces);
cw.visitSource(classFileName + ".java", null);
createInstanceVariables(cw);
createConstructor(cw, proxyClassFileName, classesToProxy[0], classFileName);
if (interceptedMethods != null) {
delegateInterceptedMethods(cw, proxyClassFileName, classesToProxy[0], interceptedMethods);
}
return cw.toByteArray();
}
private void createInstanceVariables(final ClassWriter cw) {
cw.visitField(ACC_PRIVATE,
FIELD_INTERCEPTOR_HANDLER, Type.getDescriptor(InterceptorHandler.class), null, null).visitEnd();
cw.visitField(ACC_PRIVATE | ACC_STATIC,
FIELD_INTERCEPTED_METHODS, Type.getDescriptor(Method[].class), null, null).visitEnd();
}
private void delegateInterceptedMethods(final ClassWriter cw,
final String proxyClassFileName, final Class<?> classToProxy,
final Method[] interceptedMethods) {
for (int i = 0; i < interceptedMethods.length; i++) {
if (!unproxyableMethod(interceptedMethods[i])) {
generateInterceptorHandledMethod(cw, interceptedMethods[i], i, classToProxy, proxyClassFileName);
}
}
}
private void generateInterceptorHandledMethod(final ClassWriter cw, final Method method, final int methodIndex,
final Class<?> classToProxy, final String proxyClassFileName) {
if ("<init>".equals(method.getName())) {
return;
}
final Class<?> returnType = method.getReturnType();
final Class<?>[] parameterTypes = method.getParameterTypes();
final Class<?>[] exceptionTypes = method.getExceptionTypes();
final int modifiers = method.getModifiers();
if (Modifier.isFinal(modifiers) || Modifier.isStatic(modifiers)) {
throw new IllegalStateException("It's not possible to proxy a final or static method: " + classToProxy.getName() + " " + method.getName());
}
// push the method definition
final int modifier = modifiers & (ACC_PUBLIC | ACC_PROTECTED | ACC_VARARGS);
final MethodVisitor mv = cw.visitMethod(modifier, method.getName(), Type.getMethodDescriptor(method), null, null);
mv.visitCode();
// push try/catch block, to catch declared exceptions, and to catch java.lang.Throwable
final Label l0 = new Label();
final Label l1 = new Label();
final Label l2 = new Label();
if (exceptionTypes.length > 0) {
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/reflect/InvocationTargetException");
}
// push try code
mv.visitLabel(l0);
final String classNameToOverride = method.getDeclaringClass().getName().replace('.', '/');
mv.visitLdcInsn(Type.getType("L" + classNameToOverride + ";"));
// the following code generates the bytecode for this line of Java:
// Method method = <proxy>.class.getMethod("add", new Class[] { <array of function argument classes> });
// get the method name to invoke, and push to stack
mv.visitLdcInsn(method.getName());
// create the Class[]
createArrayDefinition(mv, parameterTypes.length, Class.class);
int length = 1;
// push parameters into array
for (int i = 0; i < parameterTypes.length; i++) {
// keep copy of array on stack
mv.visitInsn(DUP);
final Class<?> parameterType = parameterTypes[i];
// push number onto stack
pushIntOntoStack(mv, i);
if (parameterType.isPrimitive()) {
String wrapperType = getWrapperType(parameterType);
mv.visitFieldInsn(GETSTATIC, wrapperType, "TYPE", "Ljava/lang/Class;");
} else {
mv.visitLdcInsn(Type.getType(parameterType));
}
mv.visitInsn(AASTORE);
if (Long.TYPE.equals(parameterType) || Double.TYPE.equals(parameterType)) {
length += 2;
} else {
length++;
}
}
// the following code generates bytecode equivalent to:
// return ((<returntype>) invocationHandler.invoke(this, {methodIndex}, new Object[] { <function arguments }))[.<primitive>Value()];
final Label l4 = new Label();
mv.visitLabel(l4);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, proxyClassFileName, FIELD_INTERCEPTOR_HANDLER, Type.getDescriptor(InterceptorHandler.class));
mv.visitFieldInsn(GETSTATIC, proxyClassFileName, FIELD_INTERCEPTED_METHODS, Type.getDescriptor(Method[].class));
if (methodIndex < 128) {
mv.visitIntInsn(BIPUSH, methodIndex);
} else if (methodIndex < 32267) {
mv.visitIntInsn(SIPUSH, methodIndex);
} else {
throw new IllegalStateException("Sorry, we only support Classes with 2^15 methods...");
}
mv.visitInsn(AALOAD);
pushMethodParameterArray(mv, parameterTypes);
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(InterceptorHandler.class), "invoke",
"(Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true);
mv.visitTypeInsn(CHECKCAST, getCastType(returnType));
if (returnType.isPrimitive() && (!Void.TYPE.equals(returnType))) {
// get the primitive value
mv.visitMethodInsn(INVOKEVIRTUAL, getWrapperType(returnType), getPrimitiveMethod(returnType),
"()" + Type.getDescriptor(returnType), false);
}
mv.visitLabel(l1);
if (!Void.TYPE.equals(returnType)) {
mv.visitInsn(getReturnInsn(returnType));
} else {
mv.visitInsn(POP);
mv.visitInsn(RETURN);
}
// catch InvocationTargetException
if (exceptionTypes.length > 0) {
mv.visitLabel(l2);
mv.visitVarInsn(ASTORE, length);
Label l5 = new Label();
mv.visitLabel(l5);
for (int i = 0; i < exceptionTypes.length; i++) {
Class<?> exceptionType = exceptionTypes[i];
mv.visitLdcInsn(Type.getType("L" + exceptionType.getCanonicalName().replace('.', '/') + ";"));
mv.visitVarInsn(ALOAD, length);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/InvocationTargetException", "getCause",
"()Ljava/lang/Throwable;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false);
Label l6 = new Label();
mv.visitJumpInsn(IFEQ, l6);
Label l7 = new Label();
mv.visitLabel(l7);
mv.visitVarInsn(ALOAD, length);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/InvocationTargetException", "getCause",
"()Ljava/lang/Throwable;", false);
mv.visitTypeInsn(CHECKCAST, getCastType(exceptionType));
mv.visitInsn(ATHROW);
mv.visitLabel(l6);
if (i == (exceptionTypes.length - 1)) {
mv.visitTypeInsn(NEW, "java/lang/reflect/UndeclaredThrowableException");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, length);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException", "<init>",
"(Ljava/lang/Throwable;)V", false);
mv.visitInsn(ATHROW);
}
}
}
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private int findJavaVersion(final Class<?> from) {
final String resource = from.getName().replace('.', '/') + ".class";
try (final InputStream stream = from.getClassLoader().getResourceAsStream(resource)) {
if (stream == null) {
return V1_8;
}
final ClassReader reader = new ClassReader(stream);
final VersionVisitor visitor = new VersionVisitor();
reader.accept(visitor, SKIP_DEBUG + SKIP_CODE + SKIP_FRAMES);
if (visitor.version != 0) {
return visitor.version;
}
} catch (final Exception e) {
// no-op
}
// mainly for JVM classes - outside the classloader, find to fallback on the JVM version
final String javaVersionProp = System.getProperty("java.version", "1.8");
if (javaVersionProp.startsWith("1.8")) {
return V1_8;
} else if (javaVersionProp.startsWith("9") || javaVersionProp.startsWith("1.9")) {
return V9;
} else if (javaVersionProp.startsWith("10")) {
return V10;
} else if (javaVersionProp.startsWith("11")) {
return V11;
} else if (javaVersionProp.startsWith("12")) {
return V12;
} else if (javaVersionProp.startsWith("13")) {
return V13;
}
try {
final int i = Integer.parseInt(javaVersionProp);
if (i > 13) {
return V13 + (i - 13);
}
return V1_8;
} catch (final NumberFormatException nfe) {
return V1_8;
}
}
private boolean unproxyableMethod(final Method delegatedMethod) {
final int modifiers = delegatedMethod.getModifiers();
return (modifiers & (Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL | Modifier.NATIVE)) > 0 ||
"finalize".equals(delegatedMethod.getName()) || delegatedMethod.isBridge();
}
/**
* @return the wrapper type for a primitive, e.g. java.lang.Integer for int
*/
private String getWrapperType(Class<?> type) {
if (Integer.TYPE.equals(type)) {
return Integer.class.getCanonicalName().replace('.', '/');
} else if (Boolean.TYPE.equals(type)) {
return Boolean.class.getCanonicalName().replace('.', '/');
} else if (Character.TYPE.equals(type)) {
return Character.class.getCanonicalName().replace('.', '/');
} else if (Byte.TYPE.equals(type)) {
return Byte.class.getCanonicalName().replace('.', '/');
} else if (Short.TYPE.equals(type)) {
return Short.class.getCanonicalName().replace('.', '/');
} else if (Float.TYPE.equals(type)) {
return Float.class.getCanonicalName().replace('.', '/');
} else if (Long.TYPE.equals(type)) {
return Long.class.getCanonicalName().replace('.', '/');
} else if (Double.TYPE.equals(type)) {
return Double.class.getCanonicalName().replace('.', '/');
} else if (Void.TYPE.equals(type)) {
return Void.class.getCanonicalName().replace('.', '/');
}
throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
}
/**
* Returns the appropriate bytecode instruction to load a value from a variable to the stack
*
* @param type Type to load
* @return Bytecode instruction to use
*/
private int getVarInsn(Class<?> type) {
if (type.isPrimitive()) {
if (Integer.TYPE.equals(type)) {
return ILOAD;
} else if (Boolean.TYPE.equals(type)) {
return ILOAD;
} else if (Character.TYPE.equals(type)) {
return ILOAD;
} else if (Byte.TYPE.equals(type)) {
return ILOAD;
} else if (Short.TYPE.equals(type)) {
return ILOAD;
} else if (Float.TYPE.equals(type)) {
return FLOAD;
} else if (Long.TYPE.equals(type)) {
return LLOAD;
} else if (Double.TYPE.equals(type)) {
return DLOAD;
}
}
throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
}
/**
* Invokes the most appropriate bytecode instruction to put a number on the stack
*
* @param mv
* @param i
*/
private void pushIntOntoStack(final MethodVisitor mv, final int i) {
if (i == 0) {
mv.visitInsn(ICONST_0);
} else if (i == 1) {
mv.visitInsn(ICONST_1);
} else if (i == 2) {
mv.visitInsn(ICONST_2);
} else if (i == 3) {
mv.visitInsn(ICONST_3);
} else if (i == 4) {
mv.visitInsn(ICONST_4);
} else if (i == 5) {
mv.visitInsn(ICONST_5);
} else if (i > 5 && i <= 255) {
mv.visitIntInsn(BIPUSH, i);
} else {
mv.visitIntInsn(SIPUSH, i);
}
}
/**
* Gets the appropriate bytecode instruction for RETURN, according to what type we need to return
*
* @param type Type the needs to be returned
* @return The matching bytecode instruction
*/
private int getReturnInsn(final Class<?> type) {
if (type.isPrimitive()) {
if (Void.TYPE.equals(type)) {
return RETURN;
}
if (Integer.TYPE.equals(type)) {
return IRETURN;
} else if (Boolean.TYPE.equals(type)) {
return IRETURN;
} else if (Character.TYPE.equals(type)) {
return IRETURN;
} else if (Byte.TYPE.equals(type)) {
return IRETURN;
} else if (Short.TYPE.equals(type)) {
return IRETURN;
} else if (Float.TYPE.equals(type)) {
return FRETURN;
} else if (Long.TYPE.equals(type)) {
return LRETURN;
} else if (Double.TYPE.equals(type)) {
return DRETURN;
}
}
return ARETURN;
}
/**
* Gets the string to use for CHECKCAST instruction, returning the correct value for any type, including primitives and arrays
*
* @param returnType The type to cast to with CHECKCAST
* @return CHECKCAST parameter
*/
private String getCastType(Class<?> returnType) {
if (returnType.isPrimitive()) {
return getWrapperType(returnType);
} else {
return Type.getInternalName(returnType);
}
}
/**
* Returns the name of the Java method to call to get the primitive value from an Object - e.g. intValue for java.lang.Integer
*
* @param type Type whose primitive method we want to lookup
* @return The name of the method to use
*/
private String getPrimitiveMethod(final Class<?> type) {
if (Integer.TYPE.equals(type)) {
return "intValue";
} else if (Boolean.TYPE.equals(type)) {
return "booleanValue";
} else if (Character.TYPE.equals(type)) {
return "charValue";
} else if (Byte.TYPE.equals(type)) {
return "byteValue";
} else if (Short.TYPE.equals(type)) {
return "shortValue";
} else if (Float.TYPE.equals(type)) {
return "floatValue";
} else if (Long.TYPE.equals(type)) {
return "longValue";
} else if (Double.TYPE.equals(type)) {
return "doubleValue";
}
throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
}
private void generateReturn(final MethodVisitor mv, final Method delegatedMethod) {
final Class<?> returnType = delegatedMethod.getReturnType();
mv.visitInsn(getReturnInsn(returnType));
}
/**
* Create an Object[] parameter which contains all the parameters of the currently invoked method
* and store this array for use in the call stack.
*
* @param mv
* @param parameterTypes
*/
private void pushMethodParameterArray(MethodVisitor mv, Class<?>[] parameterTypes) {
// need to construct the array of objects passed in
// create the Object[]
createArrayDefinition(mv, parameterTypes.length, Object.class);
int index = 1;
for (int i = 0; i < parameterTypes.length; i++) {
// keep copy of array on stack
mv.visitInsn(DUP);
final Class<?> parameterType = parameterTypes[i];
pushIntOntoStack(mv, i);
if (parameterType.isPrimitive()) {
final String wrapperType = getWrapperType(parameterType);
mv.visitVarInsn(getVarInsn(parameterType), index);
mv.visitMethodInsn(INVOKESTATIC, wrapperType, "valueOf",
"(" + Type.getDescriptor(parameterType) + ")L" + wrapperType + ";", false);
mv.visitInsn(AASTORE);
if (Long.TYPE.equals(parameterType) || Double.TYPE.equals(parameterType)) {
index += 2;
} else {
index++;
}
} else {
mv.visitVarInsn(ALOAD, index);
mv.visitInsn(AASTORE);
index++;
}
}
}
private void createArrayDefinition(final MethodVisitor mv, final int size, final Class<?> type) {
if (size < 0) {
throw new IllegalStateException("Array size cannot be less than zero");
}
pushIntOntoStack(mv, size);
mv.visitTypeInsn(ANEWARRAY, type.getCanonicalName().replace('.', '/'));
}
private static class VersionVisitor extends ClassVisitor {
private int version;
private VersionVisitor() {
super(ASM7);
}
@Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName, final String[] interfaces) {
this.version = version;
}
}
public interface InterceptorHandler {
Object invoke(Method method, Object[] args) throws Exception;
}
}