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.ParameterDescription;
22 import net.bytebuddy.description.type.TypeDescription;
23 import net.bytebuddy.implementation.Implementation;
24 import net.bytebuddy.implementation.MethodAccessorFactory;
25 import net.bytebuddy.implementation.bind.MethodDelegationBinder;
26 import net.bytebuddy.implementation.bytecode.StackManipulation;
27 import net.bytebuddy.implementation.bytecode.assign.Assigner;
28 import net.bytebuddy.implementation.bytecode.constant.MethodConstant;
29 import net.bytebuddy.implementation.bytecode.constant.NullConstant;
30 import net.bytebuddy.implementation.bytecode.member.FieldAccess;
31 import net.bytebuddy.jar.asm.MethodVisitor;
32
33 import java.lang.annotation.*;
34 import java.lang.reflect.Method;
35
36 /**
37  * A parameter with this annotation is assigned an instance of {@link Method} which invokes the super implementation of this method.
38  * If such a method is not available, this annotation causes that this delegation target cannot be bound unless {@link SuperMethod#nullIfImpossible()}
39  * is set to {@code true}. The method is declared as {@code public} and is invokable unless the instrumented type itself is not visible. Note that
40  * requesting such a method exposes the super method to reflection.
41  *
42  * @see net.bytebuddy.implementation.MethodDelegation
43  * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder
44  */

45 @Documented
46 @Retention(RetentionPolicy.RUNTIME)
47 @Target(ElementType.PARAMETER)
48 public @interface SuperMethod {
49
50     /**
51      * Indicates if the instance assigned to this parameter should be stored in a static field for reuse.
52      *
53      * @return {@code trueif this method instance should be cached.
54      */

55     boolean cached() default true;
56
57     /**
58      * Indicates if the instance assigned to this parameter should be looked up using an {@link java.security.AccessController}.
59      *
60      * @return {@code trueif this method should be looked up using an {@link java.security.AccessController}.
61      */

62     boolean privileged() default false;
63
64     /**
65      * Indicates that the assigned method should attempt the invocation of an unambiguous default method if no super method is available.
66      *
67      * @return {@code trueif a default method should be invoked if it is not ambiguous and no super class method is available.
68      */

69     boolean fallbackToDefault() default true;
70
71     /**
72      * Indicates that {@code null} should be assigned to this parameter if no super method is invokable.
73      *
74      * @return {@code trueif {@code null} should be assigned if no valid method can be assigned.
75      */

76     boolean nullIfImpossible() default false;
77
78     /**
79      * A binder for the {@link SuperMethod} annotation.
80      */

81     enum Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder<SuperMethod> {
82
83         /**
84          * The singleton instance.
85          */

86         INSTANCE;
87
88         /**
89          * {@inheritDoc}
90          */

91         public Class<SuperMethod> getHandledType() {
92             return SuperMethod.class;
93         }
94
95         /**
96          * {@inheritDoc}
97          */

98         public MethodDelegationBinder.ParameterBinding<?> bind(final AnnotationDescription.Loadable<SuperMethod> annotation,
99                                                                MethodDescription source,
100                                                                ParameterDescription target,
101                                                                Implementation.Target implementationTarget,
102                                                                Assigner assigner,
103                                                                Assigner.Typing typing) {
104             if (!target.getType().asErasure().isAssignableFrom(Method.class)) {
105                 throw new IllegalStateException("Cannot assign Method type to " + target);
106             } else if (source.isMethod()) {
107                 Implementation.SpecialMethodInvocation specialMethodInvocation = (annotation.load().fallbackToDefault()
108                         ? implementationTarget.invokeDominant(source.asSignatureToken())
109                         : implementationTarget.invokeSuper(source.asSignatureToken())).withCheckedCompatibilityTo(source.asTypeToken());
110                 if (specialMethodInvocation.isValid()) {
111                     return new MethodDelegationBinder.ParameterBinding.Anonymous(new DelegationMethod(specialMethodInvocation,
112                             annotation.load().cached(),
113                             annotation.load().privileged()));
114                 } else if (annotation.load().nullIfImpossible()) {
115                     return new MethodDelegationBinder.ParameterBinding.Anonymous(NullConstant.INSTANCE);
116                 } else {
117                     return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
118                 }
119             } else if (annotation.load().nullIfImpossible()) {
120                 return new MethodDelegationBinder.ParameterBinding.Anonymous(NullConstant.INSTANCE);
121             } else {
122                 return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
123             }
124         }
125
126         /**
127          * Loads the delegation method constant onto the stack.
128          */

129         @HashCodeAndEqualsPlugin.Enhance
130         protected static class DelegationMethod implements StackManipulation {
131
132             /**
133              * The special method invocation that represents the super method call.
134              */

135             private final Implementation.SpecialMethodInvocation specialMethodInvocation;
136
137             /**
138              * {@code trueif the method constant should be cached.
139              */

140             private final boolean cached;
141
142             /**
143              * {@code trueif this method should be looked up using an {@link java.security.AccessController}.
144              */

145             private final boolean privileged;
146
147             /**
148              * Creates a new delegation method.
149              *
150              * @param specialMethodInvocation The special method invocation that represents the super method call.
151              * @param cached                  {@code trueif the method constant should be cached.
152              * @param privileged              {@code trueif this method should be looked up using an {@link java.security.AccessController}.
153              */

154             protected DelegationMethod(Implementation.SpecialMethodInvocation specialMethodInvocation, boolean cached, boolean privileged) {
155                 this.specialMethodInvocation = specialMethodInvocation;
156                 this.cached = cached;
157                 this.privileged = privileged;
158             }
159
160             /**
161              * {@inheritDoc}
162              */

163             public boolean isValid() {
164                 return specialMethodInvocation.isValid();
165             }
166
167             /**
168              * {@inheritDoc}
169              */

170             public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
171                 StackManipulation methodConstant = privileged
172                         ? MethodConstant.ofPrivileged(implementationContext.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.PUBLIC))
173                         : MethodConstant.of(implementationContext.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.PUBLIC));
174                 return (cached
175                         ? FieldAccess.forField(implementationContext.cache(methodConstant, TypeDescription.ForLoadedType.of(Method.class))).read()
176                         : methodConstant).apply(methodVisitor, implementationContext);
177             }
178         }
179     }
180 }
181