1 /*
2  * Copyright 2014 - 2020 Rafael Winterhalter
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package net.bytebuddy.implementation.bind.annotation;
17
18 import net.bytebuddy.description.annotation.AnnotationDescription;
19 import net.bytebuddy.description.method.MethodDescription;
20 import net.bytebuddy.description.method.ParameterDescription;
21 import net.bytebuddy.description.type.TypeDescription;
22 import net.bytebuddy.implementation.Implementation;
23 import net.bytebuddy.implementation.bind.MethodDelegationBinder;
24 import net.bytebuddy.implementation.bytecode.StackManipulation;
25 import net.bytebuddy.implementation.bytecode.assign.Assigner;
26 import net.bytebuddy.implementation.bytecode.constant.*;
27 import net.bytebuddy.utility.JavaConstant;
28 import net.bytebuddy.utility.JavaType;
29
30 import java.lang.annotation.*;
31 import java.lang.reflect.Constructor;
32 import java.lang.reflect.Method;
33
34 /**
35  * <p>
36  * The origin annotation provides some meta information about the source method that is bound to this method where
37  * the binding is dependant of the parameter's type:
38  * </p>
39  * <ol>
40  * <li>If the annotated parameter is of type {@link java.lang.reflect.Method}, {@link java.lang.reflect.Constructor} or
41  * {@code java.lang.reflect.Executable}, the parameter is assigned a reference to the method or constructor it
42  * instruments. If the reference is not assignable to the sort of the intercepted source, the target is not considered
43  * for binding.</li>
44  * <li>If the annotated parameter is of type {@link java.lang.Class}, the parameter is assigned a reference of the
45  * type of the instrumented type.</li>
46  * <li>If the annotated parameter is of type {@link java.lang.String}, the parameter is assigned a string with
47  * the value that would be returned by the {@link Method#toString()} method.
48  * </li>
49  * <li>If the annotated parameter is a {@code int} type, it is assigned the intercepted method's modifiers.</li>
50  * <li>If the annotated type is {@code java.lang.invoke.MethodHandle}, a handle of the intercepted method is injected.
51  * A {@code java.lang.invoke.MethodHandle} is stored in a class's constant pool and does therefore not face the same
52  * runtime performance limitations as a (non-cached) {@link java.lang.reflect.Method} reference. Method handles are
53  * only supported for byte code versions starting from Java 7.</li>
54  * <li>If the annotated type is {@code java.lang.invoke.MethodType}, a description of the intercepted method's type
55  * is injected. Method type descriptions are only supported for byte code versions starting from Java 7.</li>
56  * </ol>
57  * <p>
58  * Any other parameter type will cause an {@link java.lang.IllegalStateException}.
59  * </p>
60  * <p>
61  * <b>Important:</b> A method handle or method type reference can only be used if the referenced method's types are all visible
62  * to the instrumented type or an {@link IllegalAccessError} will be thrown at runtime.
63  * </p>
64  *
65  * @see net.bytebuddy.implementation.MethodDelegation
66  * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder
67  */

68 @Documented
69 @Retention(RetentionPolicy.RUNTIME)
70 @Target(ElementType.PARAMETER)
71 public @interface Origin {
72
73     /**
74      * Determines if the value that is assigned by this annotation is cached. For values that can be stored in the constant pool,
75      * this value is ignored as such values are cached implicitly. As a result, this value currently only affects caching of
76      * {@link Method} instances.
77      *
78      * @return {@code trueif the value for this parameter should be cached in a {@code static} field inside the instrumented class.
79      */

80     boolean cache() default true;
81
82     /**
83      * Determines if the method should be resolved by using an {@link java.security.AccessController} using the privileges of the generated class.
84      * Doing so requires the generation of an auxiliary class that implements {@link java.security.PrivilegedExceptionAction}.
85      *
86      * @return {@code trueif the class should be looked up using an {@link java.security.AccessController}.
87      */

88     boolean privileged() default false;
89
90     /**
91      * A binder for binding parameters that are annotated with {@link net.bytebuddy.implementation.bind.annotation.Origin}.
92      *
93      * @see TargetMethodAnnotationDrivenBinder
94      */

95     enum Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder<Origin> {
96
97         /**
98          * The singleton instance.
99          */

100         INSTANCE;
101
102         /**
103          * Loads a method constant onto the operand stack.
104          *
105          * @param origin            The origin annotation.
106          * @param methodDescription The method description to load.
107          * @return An appropriate stack manipulation.
108          */

109         private static StackManipulation methodConstant(Origin origin, MethodDescription.InDefinedShape methodDescription) {
110             MethodConstant.CanCache methodConstant = origin.privileged()
111                     ? MethodConstant.ofPrivileged(methodDescription)
112                     : MethodConstant.of(methodDescription);
113             return origin.cache() ? methodConstant.cached() : methodConstant;
114         }
115
116         /**
117          * {@inheritDoc}
118          */

119         public Class<Origin> getHandledType() {
120             return Origin.class;
121         }
122
123         /**
124          * {@inheritDoc}
125          */

126         public MethodDelegationBinder.ParameterBinding<?> bind(AnnotationDescription.Loadable<Origin> annotation,
127                                                                MethodDescription source,
128                                                                ParameterDescription target,
129                                                                Implementation.Target implementationTarget,
130                                                                Assigner assigner,
131                                                                Assigner.Typing typing) {
132             TypeDescription parameterType = target.getType().asErasure();
133             if (parameterType.represents(Class.class)) {
134                 return new MethodDelegationBinder.ParameterBinding.Anonymous(ClassConstant.of(implementationTarget.getOriginType().asErasure()));
135             } else if (parameterType.represents(Method.class)) {
136                 return source.isMethod()
137                         ? new MethodDelegationBinder.ParameterBinding.Anonymous(methodConstant(annotation.load(), source.asDefined()))
138                         : MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
139             } else if (parameterType.represents(Constructor.class)) {
140                 return source.isConstructor()
141                         ? new MethodDelegationBinder.ParameterBinding.Anonymous(methodConstant(annotation.load(), source.asDefined()))
142                         : MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
143             } else if (JavaType.EXECUTABLE.getTypeStub().equals(parameterType)) {
144                 return new MethodDelegationBinder.ParameterBinding.Anonymous(methodConstant(annotation.load(), source.asDefined()));
145             } else if (parameterType.represents(String.class)) {
146                 return new MethodDelegationBinder.ParameterBinding.Anonymous(new TextConstant(source.toString()));
147             } else if (parameterType.represents(int.class)) {
148                 return new MethodDelegationBinder.ParameterBinding.Anonymous(IntegerConstant.forValue(source.getModifiers()));
149             } else if (parameterType.equals(JavaType.METHOD_HANDLE.getTypeStub())) {
150                 return new MethodDelegationBinder.ParameterBinding.Anonymous(new JavaConstantValue(JavaConstant.MethodHandle.of(source.asDefined())));
151             } else if (parameterType.equals(JavaType.METHOD_TYPE.getTypeStub())) {
152                 return new MethodDelegationBinder.ParameterBinding.Anonymous(new JavaConstantValue(JavaConstant.MethodType.of(source.asDefined())));
153             } else {
154                 throw new IllegalStateException("The " + target + " method's " + target.getIndex() +
155                         " parameter is annotated with a Origin annotation with an argument not representing a Class," +
156                         " Method, Constructor, String, int, MethodType or MethodHandle type");
157             }
158         }
159     }
160 }
161