/*
 * Copyright 2014 - 2020 Rafael Winterhalter
 *
 * 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 net.bytebuddy.implementation.bytecode.constant;

import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.auxiliary.PrivilegedMemberLookupAction;
import net.bytebuddy.implementation.bytecode.Duplication;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.TypeCreation;
import net.bytebuddy.implementation.bytecode.assign.TypeCasting;
import net.bytebuddy.implementation.bytecode.collection.ArrayFactory;
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import org.objectweb.asm.MethodVisitor;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.List;

import static net.bytebuddy.matcher.ElementMatchers.isConstructor;

Represents the creation of a Method value which can be created from a given set of constant pool values and can therefore be considered a constant in the broader meaning.
/** * Represents the creation of a {@link java.lang.reflect.Method} value which can be created from a given * set of constant pool values and can therefore be considered a constant in the broader meaning. */
public abstract class MethodConstant implements StackManipulation {
A description of the method to be loaded onto the stack.
/** * A description of the method to be loaded onto the stack. */
protected final MethodDescription.InDefinedShape methodDescription;
Creates a new method constant.
Params:
  • methodDescription – The method description for which the Method representation should be created.
/** * Creates a new method constant. * * @param methodDescription The method description for which the {@link java.lang.reflect.Method} representation * should be created. */
protected MethodConstant(MethodDescription.InDefinedShape methodDescription) { this.methodDescription = methodDescription; }
Creates a stack manipulation that loads a method constant onto the operand stack.
Params:
  • methodDescription – The method to be loaded onto the stack.
Returns:A stack manipulation that assigns a method constant for the given method description.
/** * Creates a stack manipulation that loads a method constant onto the operand stack. * * @param methodDescription The method to be loaded onto the stack. * @return A stack manipulation that assigns a method constant for the given method description. */
public static CanCache of(MethodDescription.InDefinedShape methodDescription) { if (methodDescription.isTypeInitializer()) { return CanCacheIllegal.INSTANCE; } else if (methodDescription.isConstructor()) { return new ForConstructor(methodDescription); } else { return new ForMethod(methodDescription); } }
Creates a stack manipulation that loads a method constant onto the operand stack using an AccessController.
Params:
  • methodDescription – The method to be loaded onto the stack.
Returns:A stack manipulation that assigns a method constant for the given method description.
/** * Creates a stack manipulation that loads a method constant onto the operand stack using an {@link AccessController}. * * @param methodDescription The method to be loaded onto the stack. * @return A stack manipulation that assigns a method constant for the given method description. */
public static CanCache ofPrivileged(MethodDescription.InDefinedShape methodDescription) { if (methodDescription.isTypeInitializer()) { return CanCacheIllegal.INSTANCE; } else if (methodDescription.isConstructor()) { return new ForConstructor(methodDescription).privileged(); } else { return new ForMethod(methodDescription).privileged(); } }
Returns a list of type constant load operations for the given list of parameters.
Params:
  • parameterTypes – A list of all type descriptions that should be represented as type constant load operations.
Returns:A corresponding list of type constant load operations.
/** * Returns a list of type constant load operations for the given list of parameters. * * @param parameterTypes A list of all type descriptions that should be represented as type constant * load operations. * @return A corresponding list of type constant load operations. */
protected static List<StackManipulation> typeConstantsFor(List<TypeDescription> parameterTypes) { List<StackManipulation> typeConstants = new ArrayList<StackManipulation>(parameterTypes.size()); for (TypeDescription parameterType : parameterTypes) { typeConstants.add(ClassConstant.of(parameterType)); } return typeConstants; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { return new Compound( ClassConstant.of(methodDescription.getDeclaringType()), methodName(), ArrayFactory.forType(TypeDescription.Generic.OfNonGenericType.CLASS) .withValues(typeConstantsFor(methodDescription.getParameters().asTypeList().asErasures())), MethodInvocation.invoke(accessorMethod()) ).apply(methodVisitor, implementationContext); }
Returns a method constant that uses an AccessController to look up this constant.
Returns:A method constant that uses an AccessController to look up this constant.
/** * Returns a method constant that uses an {@link AccessController} to look up this constant. * * @return A method constant that uses an {@link AccessController} to look up this constant. */
protected CanCache privileged() { return new PrivilegedLookup(methodDescription, methodName()); }
Returns a stack manipulation that loads the method name onto the operand stack if this is required.
Returns:A stack manipulation that loads the method name onto the operand stack if this is required.
/** * Returns a stack manipulation that loads the method name onto the operand stack if this is required. * * @return A stack manipulation that loads the method name onto the operand stack if this is required. */
protected abstract StackManipulation methodName();
Returns the method for loading a declared method or constructor onto the operand stack.
Returns:The method for loading a declared method or constructor onto the operand stack.
/** * Returns the method for loading a declared method or constructor onto the operand stack. * * @return The method for loading a declared method or constructor onto the operand stack. */
protected abstract MethodDescription.InDefinedShape accessorMethod(); @Override public int hashCode() { return methodDescription.hashCode(); } @Override public boolean equals(Object other) { if (this == other) { return true; } else if (other == null || getClass() != other.getClass()) { return false; } MethodConstant methodConstant = (MethodConstant) other; return methodDescription.equals(methodConstant.methodDescription); }
Represents a method constant that cannot be represented by Java's reflection API.
/** * Represents a method constant that cannot be represented by Java's reflection API. */
protected enum CanCacheIllegal implements CanCache {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation cached() { return Illegal.INSTANCE; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { return false; }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { return Illegal.INSTANCE.apply(methodVisitor, implementationContext); } }
Represents a MethodConstant that is directly loaded onto the operand stack without caching the value. Since the look-up of a Java method bares some costs that sometimes need to be avoided, such a stack manipulation offers a convenience method for defining this loading instruction as the retrieval of a field value that is initialized in the instrumented type's type initializer.
/** * Represents a {@link net.bytebuddy.implementation.bytecode.constant.MethodConstant} that is * directly loaded onto the operand stack without caching the value. Since the look-up of a Java method bares * some costs that sometimes need to be avoided, such a stack manipulation offers a convenience method for * defining this loading instruction as the retrieval of a field value that is initialized in the instrumented * type's type initializer. */
public interface CanCache extends StackManipulation {
Returns this method constant as a cached version.
Returns:A cached version of the method constant that is represented by this instance.
/** * Returns this method constant as a cached version. * * @return A cached version of the method constant that is represented by this instance. */
StackManipulation cached(); }
Creates a MethodConstant for loading a Method instance onto the operand stack.
/** * Creates a {@link net.bytebuddy.implementation.bytecode.constant.MethodConstant} for loading * a {@link java.lang.reflect.Method} instance onto the operand stack. */
protected static class ForMethod extends MethodConstant implements CanCache { /** * The {@link Class#getMethod(String, Class[])} method. */ private static final MethodDescription.InDefinedShape GET_METHOD; /** * The {@link Class#getDeclaredMethod(String, Class[])} method. */ private static final MethodDescription.InDefinedShape GET_DECLARED_METHOD; /* * Looks up methods used for creating the manipulation. */ static { try { GET_METHOD = new MethodDescription.ForLoadedMethod(Class.class.getMethod("getMethod", String.class, Class[].class)); GET_DECLARED_METHOD = new MethodDescription.ForLoadedMethod(Class.class.getMethod("getDeclaredMethod", String.class, Class[].class)); } catch (NoSuchMethodException exception) { throw new IllegalStateException("Could not locate method lookup", exception); } }
Creates a new MethodConstant for creating a Method instance.
Params:
  • methodDescription – The method to be loaded onto the stack.
/** * Creates a new {@link net.bytebuddy.implementation.bytecode.constant.MethodConstant} for * creating a {@link java.lang.reflect.Method} instance. * * @param methodDescription The method to be loaded onto the stack. */
protected ForMethod(MethodDescription.InDefinedShape methodDescription) { super(methodDescription); } @Override protected StackManipulation methodName() { return new TextConstant(methodDescription.getInternalName()); } @Override protected MethodDescription.InDefinedShape accessorMethod() { return methodDescription.isPublic() ? GET_METHOD : GET_DECLARED_METHOD; }
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation cached() { return new CachedMethod(this); } }
Creates a MethodConstant for loading a Constructor instance onto the operand stack.
/** * Creates a {@link net.bytebuddy.implementation.bytecode.constant.MethodConstant} for loading * a {@link java.lang.reflect.Constructor} instance onto the operand stack. */
protected static class ForConstructor extends MethodConstant implements CanCache { /** * The {@link Class#getConstructor(Class[])} method. */ private static final MethodDescription.InDefinedShape GET_CONSTRUCTOR; /** * The {@link Class#getDeclaredConstructor(Class[])} method. */ private static final MethodDescription.InDefinedShape GET_DECLARED_CONSTRUCTOR; /* * Looks up the method used for creating the manipulation. */ static { try { GET_CONSTRUCTOR = new MethodDescription.ForLoadedMethod(Class.class.getMethod("getConstructor", Class[].class)); GET_DECLARED_CONSTRUCTOR = new MethodDescription.ForLoadedMethod(Class.class.getMethod("getDeclaredConstructor", Class[].class)); } catch (NoSuchMethodException exception) { throw new IllegalStateException("Could not locate Class::getDeclaredConstructor", exception); } }
Creates a new MethodConstant for creating a Constructor instance.
Params:
  • methodDescription – The constructor to be loaded onto the stack.
/** * Creates a new {@link net.bytebuddy.implementation.bytecode.constant.MethodConstant} for * creating a {@link java.lang.reflect.Constructor} instance. * * @param methodDescription The constructor to be loaded onto the stack. */
protected ForConstructor(MethodDescription.InDefinedShape methodDescription) { super(methodDescription); } @Override protected StackManipulation methodName() { return Trivial.INSTANCE; } @Override protected MethodDescription.InDefinedShape accessorMethod() { return methodDescription.isPublic() ? GET_CONSTRUCTOR : GET_DECLARED_CONSTRUCTOR; }
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation cached() { return new CachedConstructor(this); } }
Performs a privileged lookup of a method constant by using an AccessController.
/** * Performs a privileged lookup of a method constant by using an {@link AccessController}. */
protected static class PrivilegedLookup implements StackManipulation, CanCache { /** * The {@link AccessController#doPrivileged(PrivilegedExceptionAction)} method. */ private static final MethodDescription.InDefinedShape DO_PRIVILEGED; /* * Locates the access controller's do privileged method. */ static { try { DO_PRIVILEGED = new MethodDescription.ForLoadedMethod(AccessController.class.getMethod("doPrivileged", PrivilegedExceptionAction.class)); } catch (NoSuchMethodException exception) { throw new IllegalStateException("Cannot locate AccessController::doPrivileged", exception); } }
The method constant to load.
/** * The method constant to load. */
private final MethodDescription.InDefinedShape methodDescription;
The stack manipulation for locating the method name.
/** * The stack manipulation for locating the method name. */
private final StackManipulation methodName;
Creates a new privileged lookup.
Params:
  • methodDescription – The method constant to load.
  • methodName – The stack manipulation for locating the method name.
/** * Creates a new privileged lookup. * * @param methodDescription The method constant to load. * @param methodName The stack manipulation for locating the method name. */
protected PrivilegedLookup(MethodDescription.InDefinedShape methodDescription, StackManipulation methodName) { this.methodDescription = methodDescription; this.methodName = methodName; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { return methodName.isValid(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { TypeDescription auxiliaryType = implementationContext.register(PrivilegedMemberLookupAction.of(methodDescription)); return new Compound( TypeCreation.of(auxiliaryType), Duplication.SINGLE, ClassConstant.of(methodDescription.getDeclaringType()), methodName, ArrayFactory.forType(TypeDescription.Generic.OfNonGenericType.CLASS) .withValues(typeConstantsFor(methodDescription.getParameters().asTypeList().asErasures())), MethodInvocation.invoke(auxiliaryType.getDeclaredMethods().filter(isConstructor()).getOnly()), MethodInvocation.invoke(DO_PRIVILEGED), TypeCasting.to(TypeDescription.ForLoadedType.of(methodDescription.isConstructor() ? Constructor.class : Method.class)) ).apply(methodVisitor, implementationContext); }
{@inheritDoc}
/** * {@inheritDoc} */
public StackManipulation cached() { return methodDescription.isConstructor() ? new CachedConstructor(this) : new CachedMethod(this); } @Override public int hashCode() { return methodDescription.hashCode(); } @Override public boolean equals(Object other) { if (this == other) { return true; } else if (other == null || getClass() != other.getClass()) { return false; } PrivilegedLookup privilegedLookup = (PrivilegedLookup) other; return methodDescription.equals(privilegedLookup.methodDescription); } }
Represents a cached method for a MethodConstant.
/** * Represents a cached method for a {@link net.bytebuddy.implementation.bytecode.constant.MethodConstant}. */
protected static class CachedMethod implements StackManipulation {
A description of the Method type.
/** * A description of the {@link java.lang.reflect.Method} type. */
private static final TypeDescription METHOD_TYPE = TypeDescription.ForLoadedType.of(Method.class);
The stack manipulation that is represented by this caching wrapper.
/** * The stack manipulation that is represented by this caching wrapper. */
private final StackManipulation methodConstant;
Creates a new cached MethodConstant.
Params:
  • methodConstant – The method constant to store in the field cache.
/** * Creates a new cached {@link net.bytebuddy.implementation.bytecode.constant.MethodConstant}. * * @param methodConstant The method constant to store in the field cache. */
protected CachedMethod(StackManipulation methodConstant) { this.methodConstant = methodConstant; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { return methodConstant.isValid(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { return FieldAccess.forField(implementationContext.cache(methodConstant, METHOD_TYPE)) .read() .apply(methodVisitor, implementationContext); } @Override public int hashCode() { return methodConstant.hashCode(); } @Override public boolean equals(Object other) { if (this == other) { return true; } else if (other == null || getClass() != other.getClass()) { return false; } CachedMethod cachedMethod = (CachedMethod) other; return methodConstant.equals(cachedMethod.methodConstant); } }
Represents a cached constructor for a MethodConstant.
/** * Represents a cached constructor for a {@link net.bytebuddy.implementation.bytecode.constant.MethodConstant}. */
protected static class CachedConstructor implements StackManipulation {
A description of the Constructor type.
/** * A description of the {@link java.lang.reflect.Constructor} type. */
private static final TypeDescription CONSTRUCTOR_TYPE = TypeDescription.ForLoadedType.of(Constructor.class);
The stack manipulation that is represented by this caching wrapper.
/** * The stack manipulation that is represented by this caching wrapper. */
private final StackManipulation constructorConstant;
Creates a new cached MethodConstant.
Params:
  • constructorConstant – The method constant to store in the field cache.
/** * Creates a new cached {@link net.bytebuddy.implementation.bytecode.constant.MethodConstant}. * * @param constructorConstant The method constant to store in the field cache. */
protected CachedConstructor(StackManipulation constructorConstant) { this.constructorConstant = constructorConstant; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isValid() { return constructorConstant.isValid(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { return FieldAccess.forField(implementationContext.cache(constructorConstant, CONSTRUCTOR_TYPE)) .read() .apply(methodVisitor, implementationContext); } @Override public int hashCode() { return constructorConstant.hashCode(); } @Override public boolean equals(Object other) { if (this == other) { return true; } else if (other == null || getClass() != other.getClass()) { return false; } CachedConstructor cachedConstructor = (CachedConstructor) other; return constructorConstant.equals(cachedConstructor.constructorConstant); } } }