org.smallmind.nutsnbolts.reflection.ProxyGenerator Maven / Gradle / Ivy
/*
* Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 David Berkman
*
* This file is part of the SmallMind Code Project.
*
* The SmallMind Code Project is free software, you can redistribute
* it and/or modify it under either, at your discretion...
*
* 1) The terms of GNU Affero General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* ...or...
*
* 2) The terms of the Apache License, Version 2.0.
*
* The SmallMind Code Project 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
* General Public License or Apache License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and the Apache License along with the SmallMind Code Project. If not, see
* or .
*
* Additional permission under the GNU Affero GPL version 3 section 7
* ------------------------------------------------------------------
* If you modify this Program, or any covered work, by linking or
* combining it with other code, such other code is not for that reason
* alone subject to any of the requirements of the GNU Affero GPL
* version 3.
*/
package org.smallmind.nutsnbolts.reflection;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.objectweb.asm.AnnotationVisitor;
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.Opcodes;
import org.objectweb.asm.util.CheckClassAdapter;
public class ProxyGenerator {
private static final HashMap OBJECT_METHOD_MAP = new HashMap<>();
private static final ConcurrentHashMap, Class>> INTERFACE_MAP = new ConcurrentHashMap<>();
private static final HashMap LOADER_MAP = new HashMap<>();
private static final String INVOCATION_HANDLER = "L" + InvocationHandler.class.getName().replace('.', '/') + ";";
static {
OBJECT_METHOD_MAP.put("hashCode", "()I");
OBJECT_METHOD_MAP.put("equals", "(Ljava/lang/Object;)Z");
OBJECT_METHOD_MAP.put("toString", "()Ljava/lang/String;");
}
public static T createProxy (Class toBeProxiedClass, InvocationHandler handler) {
return createProxy(toBeProxiedClass, handler, null);
}
public static T createProxy (Class toBeProxiedClass, InvocationHandler handler, AnnotationFilter annotationFilter) {
Class> extractedClass;
if ((extractedClass = INTERFACE_MAP.get(toBeProxiedClass)) == null) {
synchronized (INTERFACE_MAP) {
if ((extractedClass = INTERFACE_MAP.get(toBeProxiedClass)) == null) {
int toBeProxiedClassModifiers = toBeProxiedClass.getModifiers();
if (!Modifier.isPublic(toBeProxiedClassModifiers)) {
throw new ByteCodeManipulationException("The proxy class(%s) must be 'public'", toBeProxiedClass.getName());
}
if (Modifier.isStatic(toBeProxiedClassModifiers)) {
throw new ByteCodeManipulationException("The proxy class(%s) must not be 'static'", toBeProxiedClass.getName());
}
if ((!toBeProxiedClass.isInterface()) && Modifier.isAbstract(toBeProxiedClassModifiers)) {
throw new ByteCodeManipulationException("A concrete proxy class(%s) must not be 'abstract'", toBeProxiedClass.getName());
} else {
Class> currentClass;
ClassReader classReader;
ClassWriter classWriter;
CheckClassAdapter checkClassAdapter;
ProxyClassVisitor proxyClassVisitor;
ClassLoader toBeProxiedClassLoader;
ProxyClassLoader proxyClassLoader;
HashSet methodTrackerSet;
boolean initialized = false;
classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
checkClassAdapter = new CheckClassAdapter(classWriter, true);
currentClass = toBeProxiedClass;
methodTrackerSet = new HashSet<>();
do {
if (currentClass.equals(Object.class)) {
currentClass = ObjectImpersonator.class;
}
try {
classReader = new ClassReader(currentClass.getClassLoader().getResourceAsStream(currentClass.getCanonicalName().replace('.', '/') + ".class"));
} catch (IOException ioException) {
throw new ByteCodeManipulationException(ioException);
}
proxyClassVisitor = new ProxyClassVisitor(checkClassAdapter, toBeProxiedClass, currentClass, annotationFilter, methodTrackerSet, initialized);
classReader.accept(proxyClassVisitor, 0);
initialized = true;
} while ((currentClass = currentClass.equals(ObjectImpersonator.class) ? null : currentClass.getSuperclass()) != null);
checkClassAdapter.visitEnd();
synchronized (LOADER_MAP) {
if ((proxyClassLoader = LOADER_MAP.get(toBeProxiedClassLoader = toBeProxiedClass.getClassLoader())) == null) {
LOADER_MAP.put(toBeProxiedClassLoader, proxyClassLoader = new ProxyClassLoader(toBeProxiedClassLoader));
}
}
INTERFACE_MAP.put(toBeProxiedClass, extractedClass = proxyClassLoader.extractInterface(toBeProxiedClass.getName() + "$Proxy$_ExtractedSubclass", classWriter.toByteArray()));
}
}
}
}
try {
return toBeProxiedClass.cast(extractedClass.getConstructor(InvocationHandler.class).newInstance(handler));
} catch (Exception exception) {
throw new ByteCodeManipulationException(exception);
}
}
private static class MethodTracker {
private final String name;
private final String description;
private MethodTracker (String name, String description) {
this.name = name;
this.description = description;
}
public String getName () {
return name;
}
public String getDescription () {
return description;
}
@Override
public int hashCode () {
return name.hashCode() ^ ((description == null) ? 0 : description.hashCode());
}
@Override
public boolean equals (Object obj) {
return (obj instanceof MethodTracker) && ((MethodTracker)obj).getName().equals(name) && ((description == null) ? ((MethodTracker)obj).getDescription() == null : description.equals(((MethodTracker)obj).getDescription()));
}
}
private static class ProxyClassLoader extends ClassLoader {
public ProxyClassLoader (ClassLoader parent) {
super(parent);
}
public Class> extractInterface (String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
private static class ProxyClassVisitor extends ClassVisitor {
private final ClassVisitor nextClassVisitor;
private final Class> toBeProxiedClass;
private final Class> currentClass;
private final AnnotationFilter annotationFilter;
private final HashSet methodTrackerSet;
private final boolean initialized;
private boolean constructed = false;
public ProxyClassVisitor (ClassVisitor nextClassVisitor, Class> toBeProxiedClass, Class> currentClass, AnnotationFilter annotationFilter, HashSet methodTrackerSet, boolean initialized) {
super(Opcodes.ASM8);
this.nextClassVisitor = nextClassVisitor;
this.toBeProxiedClass = toBeProxiedClass;
this.currentClass = currentClass;
this.annotationFilter = annotationFilter;
this.methodTrackerSet = methodTrackerSet;
this.initialized = initialized;
}
@Override
public void visit (int version, int access, String name, String signature, String superName, String[] interfaces) {
if (!initialized) {
if (toBeProxiedClass.isInterface()) {
nextClassVisitor.visit(version, Opcodes.ACC_PUBLIC, name + "$Proxy$_ExtractedSubclass", null, "java/lang/Object", new String[] {toBeProxiedClass.getName().replace('.', '/')});
} else {
nextClassVisitor.visit(version, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, name + "$Proxy$_ExtractedSubclass", null, name, null);
}
nextClassVisitor.visitField(Opcodes.ACC_PRIVATE, "$proxy$_handler", INVOCATION_HANDLER, null, null).visitEnd();
}
}
private void createConstructor (String signature, String[] exceptions) {
if (!(initialized || constructed)) {
MethodVisitor initVisitor;
initVisitor = nextClassVisitor.visitMethod(Opcodes.ACC_PUBLIC, "", "(" + INVOCATION_HANDLER + ")V", signature, exceptions);
initVisitor.visitCode();
Label l0 = new Label();
initVisitor.visitLabel(l0);
initVisitor.visitVarInsn(Opcodes.ALOAD, 0);
initVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, (toBeProxiedClass.isInterface()) ? "java/lang/Object" : toBeProxiedClass.getName().replace('.', '/'), "", "()V", false);
Label l1 = new Label();
initVisitor.visitLabel(l1);
initVisitor.visitVarInsn(Opcodes.ALOAD, 0);
initVisitor.visitVarInsn(Opcodes.ALOAD, 1);
initVisitor.visitFieldInsn(Opcodes.PUTFIELD, toBeProxiedClass.getName().replace('.', '/') + "$Proxy$_ExtractedSubclass", "$proxy$_handler", INVOCATION_HANDLER);
Label l2 = new Label();
initVisitor.visitLabel(l2);
initVisitor.visitInsn(Opcodes.RETURN);
Label l3 = new Label();
initVisitor.visitLabel(l3);
initVisitor.visitLocalVariable("this", "L" + toBeProxiedClass.getName().replace('.', '/') + "$Proxy$_ExtractedSubclass;", null, l0, l3, 0);
initVisitor.visitLocalVariable("$proxy$_handler", INVOCATION_HANDLER, null, l0, l3, 1);
initVisitor.visitMaxs(2, 2);
initVisitor.visitEnd();
constructed = true;
}
}
@Override
public MethodVisitor visitMethod (int access, String name, String desc, String signature, String[] exceptions) {
MethodTracker methodTracker;
if (!methodTrackerSet.contains(methodTracker = new MethodTracker(name, desc))) {
if (toBeProxiedClass.isInterface() || ((access & Opcodes.ACC_ABSTRACT) == 0)) {
if ("".equals(name)) {
if ("()V".equals(desc)) {
methodTrackerSet.add(methodTracker);
createConstructor(signature, exceptions);
}
} else {
String objectMethodDesc;
if ((!currentClass.equals(ObjectImpersonator.class)) || (((objectMethodDesc = OBJECT_METHOD_MAP.get(name)) != null) && objectMethodDesc.equals(desc))) {
if (((access & Opcodes.ACC_PUBLIC) != 0) && ((access & Opcodes.ACC_STATIC) == 0) && ((access & Opcodes.ACC_FINAL) == 0) && ((access & Opcodes.ACC_SYNTHETIC) == 0)) {
MethodVisitor proxyVisitor;
methodTrackerSet.add(methodTracker);
proxyVisitor = nextClassVisitor.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, name, desc, null, exceptions);
proxyVisitor.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label[] exceptionLabels;
exceptionLabels = new Label[(exceptions == null) ? 1 : exceptions.length + 1];
for (int index = 0; index < ((exceptions == null) ? 0 : exceptions.length); index++) {
exceptionLabels[index] = new Label();
proxyVisitor.visitTryCatchBlock(l0, l1, exceptionLabels[index], exceptions[index]);
}
exceptionLabels[(exceptions == null) ? 0 : exceptions.length] = new Label();
proxyVisitor.visitTryCatchBlock(l0, l1, exceptionLabels[(exceptions == null) ? 0 : exceptions.length], "java/lang/Throwable");
proxyVisitor.visitLabel(l0);
proxyVisitor.visitVarInsn(Opcodes.ALOAD, 0);
proxyVisitor.visitVarInsn(Opcodes.ALOAD, 0);
proxyVisitor.visitFieldInsn(Opcodes.GETFIELD, toBeProxiedClass.getName().replace('.', '/') + "$Proxy$_ExtractedSubclass", "$proxy$_handler", INVOCATION_HANDLER);
proxyVisitor.visitInsn(toBeProxiedClass.isInterface() ? Opcodes.ICONST_0 : Opcodes.ICONST_1);
proxyVisitor.visitLdcInsn(UUID.randomUUID().toString());
proxyVisitor.visitLdcInsn(name);
proxyVisitor.visitLdcInsn(desc.substring(desc.indexOf(')') + 1));
String[] parameters;
LinkedList parameterList;
parameterList = new LinkedList<>();
for (String parameter : new ParameterIterable(desc.substring(1, desc.indexOf(')')))) {
parameterList.add(parameter);
}
parameters = new String[parameterList.size()];
parameterList.toArray(parameters);
insertNumber(proxyVisitor, parameterList.size());
proxyVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
for (int index = 0; index < parameters.length; index++) {
proxyVisitor.visitInsn(Opcodes.DUP);
insertNumber(proxyVisitor, index);
proxyVisitor.visitLdcInsn(parameters[index]);
proxyVisitor.visitInsn(Opcodes.AASTORE);
}
int[] parameterRegisters;
int variableIndex = 1;
insertNumber(proxyVisitor, parameterList.size());
proxyVisitor.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
parameterRegisters = new int[parameters.length];
for (int index = 0; index < parameters.length; index++) {
proxyVisitor.visitInsn(Opcodes.DUP);
insertNumber(proxyVisitor, index);
if (parameters[index].length() == 1) {
switch (parameters[index].charAt(0)) {
case 'Z':
proxyVisitor.visitVarInsn(Opcodes.ILOAD, parameterRegisters[index] = variableIndex++);
proxyVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
break;
case 'B':
proxyVisitor.visitVarInsn(Opcodes.ILOAD, parameterRegisters[index] = variableIndex++);
proxyVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
break;
case 'C':
proxyVisitor.visitVarInsn(Opcodes.ILOAD, parameterRegisters[index] = variableIndex++);
proxyVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
break;
case 'S':
proxyVisitor.visitVarInsn(Opcodes.ILOAD, parameterRegisters[index] = variableIndex++);
proxyVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
break;
case 'I':
proxyVisitor.visitVarInsn(Opcodes.ILOAD, parameterRegisters[index] = variableIndex++);
proxyVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
break;
case 'J':
proxyVisitor.visitVarInsn(Opcodes.LLOAD, parameterRegisters[index] = variableIndex);
variableIndex += 2;
proxyVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
break;
case 'F':
proxyVisitor.visitVarInsn(Opcodes.FLOAD, parameterRegisters[index] = variableIndex++);
proxyVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
break;
case 'D':
proxyVisitor.visitVarInsn(Opcodes.DLOAD, parameterRegisters[index] = variableIndex);
variableIndex += 2;
proxyVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
break;
default:
throw new ByteCodeManipulationException("Unknown primitive type(%s)", parameters[index]);
}
} else {
proxyVisitor.visitVarInsn(Opcodes.ALOAD, parameterRegisters[index] = variableIndex++);
}
proxyVisitor.visitInsn(Opcodes.AASTORE);
}
proxyVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, ProxyUtility.class.getName().replace('.', '/'), "invoke", "(Ljava/lang/Object;" + INVOCATION_HANDLER + "ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;", false);
String returnType;
if ((returnType = desc.substring(desc.indexOf(')') + 1)).length() == 1) {
switch (returnType.charAt(0)) {
case 'V':
proxyVisitor.visitInsn(Opcodes.POP);
break;
case 'Z':
proxyVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
proxyVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
break;
case 'B':
proxyVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Byte");
proxyVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false);
break;
case 'C':
proxyVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Character");
proxyVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
break;
case 'S':
proxyVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Short");
proxyVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
break;
case 'I':
proxyVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer");
proxyVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
break;
case 'J':
proxyVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Long");
proxyVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
break;
case 'F':
proxyVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Float");
proxyVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false);
break;
case 'D':
proxyVisitor.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Double");
proxyVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
break;
default:
throw new ByteCodeManipulationException("Unknown return type(%s)", returnType);
}
} else if (returnType.startsWith("L")) {
proxyVisitor.visitTypeInsn(Opcodes.CHECKCAST, returnType.substring(1, returnType.length() - 1));
} else {
proxyVisitor.visitTypeInsn(Opcodes.CHECKCAST, returnType);
}
proxyVisitor.visitLabel(l1);
Label lEnd = null;
if ((returnType = desc.substring(desc.indexOf(')') + 1)).length() == 1) {
switch (returnType.charAt(0)) {
case 'V':
lEnd = new Label();
proxyVisitor.visitJumpInsn(Opcodes.GOTO, lEnd);
break;
case 'Z':
proxyVisitor.visitInsn(Opcodes.IRETURN);
break;
case 'B':
proxyVisitor.visitInsn(Opcodes.IRETURN);
break;
case 'C':
proxyVisitor.visitInsn(Opcodes.IRETURN);
break;
case 'S':
proxyVisitor.visitInsn(Opcodes.IRETURN);
break;
case 'I':
proxyVisitor.visitInsn(Opcodes.IRETURN);
break;
case 'J':
proxyVisitor.visitInsn(Opcodes.LRETURN);
break;
case 'F':
proxyVisitor.visitInsn(Opcodes.FRETURN);
break;
case 'D':
proxyVisitor.visitInsn(Opcodes.DRETURN);
break;
default:
throw new ByteCodeManipulationException("Unknown return type(%s)", returnType);
}
} else {
proxyVisitor.visitInsn(Opcodes.ARETURN);
}
Label[] extraLabels;
extraLabels = new Label[(exceptions == null) ? 1 : exceptions.length + 1];
for (int index = 0; index <= ((exceptions == null) ? 0 : exceptions.length); index++) {
proxyVisitor.visitLabel(exceptionLabels[index]);
proxyVisitor.visitVarInsn(Opcodes.ASTORE, variableIndex);
proxyVisitor.visitLabel(extraLabels[index] = new Label());
if (index == ((exceptions == null) ? 0 : exceptions.length)) {
proxyVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/reflect/UndeclaredThrowableException");
proxyVisitor.visitInsn(Opcodes.DUP);
proxyVisitor.visitVarInsn(Opcodes.ALOAD, variableIndex);
proxyVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException", "", "(Ljava/lang/Throwable;)V", false);
proxyVisitor.visitInsn(Opcodes.ATHROW);
} else {
proxyVisitor.visitVarInsn(Opcodes.ALOAD, variableIndex);
proxyVisitor.visitInsn(Opcodes.ATHROW);
}
}
if (lEnd != null) {
proxyVisitor.visitLabel(lEnd);
proxyVisitor.visitInsn(Opcodes.RETURN);
}
Label lLocal;
proxyVisitor.visitLabel(lLocal = new Label());
proxyVisitor.visitLocalVariable("this", "L" + toBeProxiedClass.getName().replace('.', '/') + "$Proxy$_ExtractedSubclass;", null, l0, lLocal, 0);
for (int index = 0; index < parameters.length; index++) {
proxyVisitor.visitLocalVariable("$proxy$_var" + index, parameters[index], null, l0, lLocal, parameterRegisters[index]);
}
for (int index = 0; index < ((exceptions == null) ? 0 : exceptions.length); index++) {
proxyVisitor.visitLocalVariable("$proxy$_exc" + index, "L" + exceptions[index] + ";", null, extraLabels[index], exceptionLabels[index + 1], variableIndex);
}
proxyVisitor.visitLocalVariable("$proxy$_exc" + ((exceptions == null) ? 0 : exceptions.length), "Ljava/lang/Throwable;", null, extraLabels[(exceptions == null) ? 0 : exceptions.length], lLocal, variableIndex);
proxyVisitor.visitMaxs(12, variableIndex + 2);
return new ProxyMethodVisitor(proxyVisitor, annotationFilter);
}
}
}
}
}
return null;
}
@Override
public void visitEnd () {
createConstructor(null, null);
}
private void insertNumber (MethodVisitor methodVisitor, int number) {
switch (number) {
case 0:
methodVisitor.visitInsn(Opcodes.ICONST_0);
break;
case 1:
methodVisitor.visitInsn(Opcodes.ICONST_1);
break;
case 2:
methodVisitor.visitInsn(Opcodes.ICONST_2);
break;
case 3:
methodVisitor.visitInsn(Opcodes.ICONST_3);
break;
case 4:
methodVisitor.visitInsn(Opcodes.ICONST_4);
break;
case 5:
methodVisitor.visitInsn(Opcodes.ICONST_5);
break;
default:
if (number <= Byte.MAX_VALUE) {
methodVisitor.visitIntInsn(Opcodes.BIPUSH, number);
} else {
methodVisitor.visitIntInsn(Opcodes.SIPUSH, number);
}
break;
}
}
}
private static class ProxyMethodVisitor extends MethodVisitor {
private final MethodVisitor nextMethodVisitor;
private final AnnotationFilter annotationFilter;
public ProxyMethodVisitor (MethodVisitor nextMethodVisitor, AnnotationFilter annotationFilter) {
super(Opcodes.ASM8);
this.nextMethodVisitor = nextMethodVisitor;
this.annotationFilter = annotationFilter;
}
@Override
public AnnotationVisitor visitAnnotationDefault () {
return nextMethodVisitor.visitAnnotationDefault();
}
@Override
public AnnotationVisitor visitAnnotation (String desc, boolean visible) {
if ((annotationFilter == null) || annotationFilter.isAllowed(desc)) {
return nextMethodVisitor.visitAnnotation(desc, visible);
}
return null;
}
@Override
public AnnotationVisitor visitParameterAnnotation (int parameter, String desc, boolean visible) {
if ((annotationFilter == null) || annotationFilter.isAllowed(desc)) {
return nextMethodVisitor.visitParameterAnnotation(parameter, desc, visible);
}
return null;
}
@Override
public void visitEnd () {
nextMethodVisitor.visitEnd();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy