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.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.annotation.AnnotationDescription;
20 import net.bytebuddy.description.method.MethodDescription;
21 import net.bytebuddy.description.method.MethodList;
22 import net.bytebuddy.description.method.ParameterDescription;
23 import net.bytebuddy.description.type.TypeDescription;
24 import net.bytebuddy.implementation.Implementation;
25 import net.bytebuddy.implementation.auxiliary.TypeProxy;
26 import net.bytebuddy.implementation.bind.MethodDelegationBinder;
27 import net.bytebuddy.implementation.bytecode.assign.Assigner;
28
29 import java.lang.annotation.*;
30
31 import static net.bytebuddy.matcher.ElementMatchers.named;
32
33 /**
34 * Parameters that are annotated with this annotation are assigned an instance of an auxiliary proxy type that allows calling
35 * any default method of an interface of the instrumented type where the parameter type must be an interface that is
36 * directly implemented by the instrumented type. The generated proxy will directly implement the parameter's
37 * interface. If the interface of the annotation is not implemented by the instrumented type, the method with this
38 * parameter is not considered as a binding target.
39 *
40 * @see net.bytebuddy.implementation.MethodDelegation
41 * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder
42 */
43 @Documented
44 @Retention(RetentionPolicy.RUNTIME)
45 @Target(ElementType.PARAMETER)
46 public @interface Default {
47
48 /**
49 * Determines if the generated proxy should be {@link java.io.Serializable}. If the annotated type
50 * already is serializable, such an explicit specification is not required.
51 *
52 * @return {@code true} if the generated proxy should be {@link java.io.Serializable}.
53 */
54 boolean serializableProxy() default false;
55
56 /**
57 * Determines the type that is implemented by the proxy. When this value is set to its default value
58 * {@code void}, the proxy is created as an instance of the parameter's type. It is <b>not</b> possible to
59 * set the value of this property to {@link net.bytebuddy.dynamic.TargetType} as a interface cannot implement itself.
60 *
61 * @return The type of the proxy or an indicator type, i.e. {@code void}.
62 */
63 Class<?> proxyType() default void.class;
64
65 /**
66 * A binder for the {@link net.bytebuddy.implementation.bind.annotation.Default} annotation.
67 */
68 enum Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder<Default> {
69
70 /**
71 * The singleton instance.
72 */
73 INSTANCE;
74
75 /**
76 * A method reference to the serializable proxy property.
77 */
78 private static final MethodDescription.InDefinedShape SERIALIZABLE_PROXY;
79
80 /**
81 * A method reference to the proxy type property.
82 */
83 private static final MethodDescription.InDefinedShape PROXY_TYPE;
84
85 /*
86 * Extracts method references of the default annotation.
87 */
88 static {
89 MethodList<MethodDescription.InDefinedShape> annotationProperties = TypeDescription.ForLoadedType.of(Default.class).getDeclaredMethods();
90 SERIALIZABLE_PROXY = annotationProperties.filter(named("serializableProxy")).getOnly();
91 PROXY_TYPE = annotationProperties.filter(named("proxyType")).getOnly();
92 }
93
94 /**
95 * {@inheritDoc}
96 */
97 public Class<Default> getHandledType() {
98 return Default.class;
99 }
100
101 /**
102 * {@inheritDoc}
103 */
104 public MethodDelegationBinder.ParameterBinding<?> bind(AnnotationDescription.Loadable<Default> annotation,
105 MethodDescription source,
106 ParameterDescription target,
107 Implementation.Target implementationTarget,
108 Assigner assigner,
109 Assigner.Typing typing) {
110 TypeDescription proxyType = TypeLocator.ForType.of(annotation.getValue(PROXY_TYPE).resolve(TypeDescription.class)).resolve(target.getType());
111 if (!proxyType.isInterface()) {
112 throw new IllegalStateException(target + " uses the @Default annotation on an invalid type");
113 }
114 if (source.isStatic() || !implementationTarget.getInstrumentedType().getInterfaces().asErasures().contains(proxyType)) {
115 return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
116 } else {
117 return new MethodDelegationBinder.ParameterBinding.Anonymous(new TypeProxy.ForDefaultMethod(proxyType,
118 implementationTarget,
119 annotation.getValue(SERIALIZABLE_PROXY).resolve(Boolean.class)));
120 }
121 }
122
123 /**
124 * Locates the type which should be the base type of the created proxy.
125 */
126 protected interface TypeLocator {
127
128 /**
129 * Resolves the target type.
130 *
131 * @param parameterType The type of the target parameter.
132 * @return The proxy type.
133 */
134 TypeDescription resolve(TypeDescription.Generic parameterType);
135
136 /**
137 * A type locator that yields the target parameter's type.
138 */
139 enum ForParameterType implements TypeLocator {
140
141 /**
142 * The singleton instance.
143 */
144 INSTANCE;
145
146 /**
147 * {@inheritDoc}
148 */
149 public TypeDescription resolve(TypeDescription.Generic parameterType) {
150 return parameterType.asErasure();
151 }
152 }
153
154 /**
155 * A type locator that returns a given type.
156 */
157 @HashCodeAndEqualsPlugin.Enhance
158 class ForType implements TypeLocator {
159
160 /**
161 * The type to be returned upon resolution.
162 */
163 private final TypeDescription typeDescription;
164
165 /**
166 * Creates a new type locator for a given type.
167 *
168 * @param typeDescription The type to be returned upon resolution.
169 */
170 protected ForType(TypeDescription typeDescription) {
171 this.typeDescription = typeDescription;
172 }
173
174 /**
175 * Resolves a type locator based upon an annotation value.
176 *
177 * @param typeDescription The annotation's value.
178 * @return The appropriate type locator.
179 */
180 protected static TypeLocator of(TypeDescription typeDescription) {
181 if (typeDescription.represents(void.class)) {
182 return ForParameterType.INSTANCE;
183 } else if (!typeDescription.isInterface()) {
184 throw new IllegalStateException("Cannot assign proxy to " + typeDescription);
185 } else {
186 return new ForType(typeDescription);
187 }
188 }
189
190 /**
191 * {@inheritDoc}
192 */
193 public TypeDescription resolve(TypeDescription.Generic parameterType) {
194 if (!typeDescription.isAssignableTo(parameterType.asErasure())) {
195 throw new IllegalStateException("Impossible to assign " + typeDescription + " to parameter of type " + parameterType);
196 }
197 return typeDescription;
198 }
199 }
200 }
201 }
202 }
203
204