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 trueif 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