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 true} if 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 true} if 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