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;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.method.MethodDescription;
20 import net.bytebuddy.dynamic.scaffold.InstrumentedType;
21 import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
22 import net.bytebuddy.implementation.bytecode.Removal;
23 import net.bytebuddy.implementation.bytecode.StackManipulation;
24 import net.bytebuddy.implementation.bytecode.member.MethodReturn;
25 import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
26 import net.bytebuddy.jar.asm.MethodVisitor;
27
28 /**
29  * This implementation will create a new method which simply calls its super method. If no such method is defined,
30  * an exception will be thrown. Constructors are considered to have a super method if the direct super class defines
31  * a constructor with an identical signature. Default methods are invoked as such if they are non-ambiguous. Static
32  * methods can have a (pseudo) super method if a type that defines such a method is rebased. Rebased types can also
33  * shadow constructors or methods of an actual super class. Besides implementing constructors, this implementation
34  * is useful when a method of a super type is not supposed to be altered but should be equipped with additional
35  * annotations. Furthermore, this implementation allows to hard code a super method call to be performed after
36  * performing another {@link Implementation}.
37  */

38 public enum SuperMethodCall implements Implementation.Composable {
39
40     /**
41      * The singleton instance.
42      */

43     INSTANCE;
44
45     /**
46      * {@inheritDoc}
47      */

48     public InstrumentedType prepare(InstrumentedType instrumentedType) {
49         return instrumentedType;
50     }
51
52     /**
53      * {@inheritDoc}
54      */

55     public ByteCodeAppender appender(Target implementationTarget) {
56         return new Appender(implementationTarget, Appender.TerminationHandler.RETURNING);
57     }
58
59     /**
60      * {@inheritDoc}
61      */

62     public Implementation andThen(Implementation implementation) {
63         return new Compound(WithoutReturn.INSTANCE, implementation);
64     }
65
66     /**
67      * {@inheritDoc}
68      */

69     public Composable andThen(Composable implementation) {
70         return new Compound.Composable(WithoutReturn.INSTANCE, implementation);
71     }
72
73     /**
74      * A super method invocation where the return value is dropped instead of returning from the method.
75      */

76     protected enum WithoutReturn implements Implementation {
77
78         /**
79          * The singleton instance.
80          */

81         INSTANCE;
82
83         /**
84          * {@inheritDoc}
85          */

86         public InstrumentedType prepare(InstrumentedType instrumentedType) {
87             return instrumentedType;
88         }
89
90         /**
91          * {@inheritDoc}
92          */

93         public ByteCodeAppender appender(Target implementationTarget) {
94             return new Appender(implementationTarget, Appender.TerminationHandler.DROPPING);
95         }
96     }
97
98     /**
99      * An appender for implementing a {@link net.bytebuddy.implementation.SuperMethodCall}.
100      */

101     @HashCodeAndEqualsPlugin.Enhance
102     protected static class Appender implements ByteCodeAppender {
103
104         /**
105          * The target of the current implementation.
106          */

107         private final Target implementationTarget;
108
109         /**
110          * The termination handler to apply after invoking the super method.
111          */

112         private final TerminationHandler terminationHandler;
113
114         /**
115          * Creates a new appender.
116          *
117          * @param implementationTarget The implementation target of the current type creation.
118          * @param terminationHandler   The termination handler to apply after invoking the super method.
119          */

120         protected Appender(Target implementationTarget, TerminationHandler terminationHandler) {
121             this.implementationTarget = implementationTarget;
122             this.terminationHandler = terminationHandler;
123         }
124
125         /**
126          * {@inheritDoc}
127          */

128         public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext, MethodDescription instrumentedMethod) {
129             StackManipulation superMethodCall = implementationTarget
130                     .invokeDominant(instrumentedMethod.asSignatureToken())
131                     .withCheckedCompatibilityTo(instrumentedMethod.asTypeToken());
132             if (!superMethodCall.isValid()) {
133                 throw new IllegalStateException("Cannot call super (or default) method for " + instrumentedMethod);
134             }
135             StackManipulation.Size size = new StackManipulation.Compound(
136                     MethodVariableAccess.allArgumentsOf(instrumentedMethod).prependThisReference(),
137                     superMethodCall,
138                     terminationHandler.of(instrumentedMethod)
139             ).apply(methodVisitor, implementationContext);
140             return new Size(size.getMaximalSize(), instrumentedMethod.getStackSize());
141         }
142
143         /**
144          * A handler that determines how to handle the method return value.
145          */

146         protected enum TerminationHandler {
147
148             /**
149              * A termination handler that returns the value of the super method invocation.
150              */

151             RETURNING {
152                 @Override
153                 protected StackManipulation of(MethodDescription methodDescription) {
154                     return MethodReturn.of(methodDescription.getReturnType());
155                 }
156             },
157
158             /**
159              * A termination handler that simply pops the value of the super method invocation off the stack.
160              */

161             DROPPING {
162                 @Override
163                 protected StackManipulation of(MethodDescription methodDescription) {
164                     return Removal.of(methodDescription.getReturnType());
165                 }
166             };
167
168             /**
169              * Creates a stack manipulation that represents this handler's behavior.
170              *
171              * @param methodDescription The method for which this handler is supposed to create a stack
172              *                          manipulation for.
173              * @return The stack manipulation that implements this handler.
174              */

175             protected abstract StackManipulation of(MethodDescription methodDescription);
176         }
177     }
178 }
179