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.bytecode;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.implementation.Implementation;
20 import net.bytebuddy.jar.asm.MethodVisitor;
21
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.List;
25
26 /**
27  * Describes a manipulation of a method's operand stack that does not affect the frame's variable array.
28  */

29 public interface StackManipulation {
30
31     /**
32      * Determines if this stack manipulation is valid.
33      *
34      * @return If {@code false}, this manipulation cannot be applied and should throw an exception.
35      */

36     boolean isValid();
37
38     /**
39      * Applies the stack manipulation that is described by this instance.
40      *
41      * @param methodVisitor         The method visitor used to write the method implementation to.
42      * @param implementationContext The context of the current implementation.
43      * @return The changes to the size of the operand stack that are implied by this stack manipulation.
44      */

45     Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext);
46
47     /**
48      * Canonical representation of an illegal stack manipulation.
49      */

50     enum Illegal implements StackManipulation {
51
52         /**
53          * The singleton instance.
54          */

55         INSTANCE;
56
57         /**
58          * {@inheritDoc}
59          */

60         public boolean isValid() {
61             return false;
62         }
63
64         /**
65          * {@inheritDoc}
66          */

67         public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
68             throw new IllegalStateException("An illegal stack manipulation must not be applied");
69         }
70     }
71
72     /**
73      * Canonical representation of a legal stack manipulation which does not require any action.
74      */

75     enum Trivial implements StackManipulation {
76
77         /**
78          * The singleton instance.
79          */

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

85         public boolean isValid() {
86             return true;
87         }
88
89         /**
90          * {@inheritDoc}
91          */

92         public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
93             return StackSize.ZERO.toIncreasingSize();
94         }
95     }
96
97     /**
98      * A description of the size change that is imposed by some
99      * {@link StackManipulation}.
100      */

101     @HashCodeAndEqualsPlugin.Enhance
102     class Size {
103
104         /**
105          * The impact of any size operation onto the operand stack. This value can be negative if more values
106          * were consumed from the stack than added to it.
107          */

108         private final int sizeImpact;
109
110         /**
111          * The maximal size of stack slots this stack manipulation ever requires. If an operation for example pushes
112          * five values onto the stack and subsequently consumes three operations, this value should still be five
113          * to express that a stack operation requires at least five slots in order to be applicable.
114          */

115         private final int maximalSize;
116
117         /**
118          * Creates an immutable descriptor of the size change that is implied by some stack manipulation.
119          *
120          * @param sizeImpact  The change of the size of the operand stack that is implied by some stack manipulation.
121          * @param maximalSize The maximal stack size that is required for executing this stack manipulation. Should
122          *                    never be negative number.
123          */

124         public Size(int sizeImpact, int maximalSize) {
125             this.sizeImpact = sizeImpact;
126             this.maximalSize = maximalSize;
127         }
128
129         /**
130          * Returns the size change on the operand stack that is represented by this instance.
131          *
132          * @return The size change on the operand stack that is represented by this instance.
133          */

134         public int getSizeImpact() {
135             return sizeImpact;
136         }
137
138         /**
139          * Returns the maximal interim size of the operand stack that is represented by this instance.
140          *
141          * @return The maximal interim size of the operand stack that is represented by this instance.
142          */

143         public int getMaximalSize() {
144             return maximalSize;
145         }
146
147         /**
148          * Concatenates this size representation with another size representation in order to represent the size
149          * change that is represented by both alterations of the operand stack size.
150          *
151          * @param other The other size representation.
152          * @return A new size representation representing both stack size requirements.
153          */

154         public Size aggregate(Size other) {
155             return aggregate(other.sizeImpact, other.maximalSize);
156         }
157
158         /**
159          * Aggregates a size change with this stack manipulation size.
160          *
161          * @param sizeChange         The change in size the other operation implies.
162          * @param interimMaximalSize The interim maximal size of the operand stack that the other operation requires
163          *                           at least to function.
164          * @return The aggregated size.
165          */

166         private Size aggregate(int sizeChange, int interimMaximalSize) {
167             return new Size(sizeImpact + sizeChange, Math.max(maximalSize, sizeImpact + interimMaximalSize));
168         }
169     }
170
171     /**
172      * An immutable stack manipulation that aggregates a sequence of other stack manipulations.
173      */

174     @HashCodeAndEqualsPlugin.Enhance
175     class Compound implements StackManipulation {
176
177         /**
178          * The stack manipulations this compound operation represents in their application order.
179          */

180         private final List<StackManipulation> stackManipulations;
181
182         /**
183          * Creates a new compound stack manipulation.
184          *
185          * @param stackManipulation The stack manipulations to be composed in the order of their composition.
186          */

187         public Compound(StackManipulation... stackManipulation) {
188             this(Arrays.asList(stackManipulation));
189         }
190
191         /**
192          * Creates a new compound stack manipulation.
193          *
194          * @param stackManipulations The stack manipulations to be composed in the order of their composition.
195          */

196         public Compound(List<? extends StackManipulation> stackManipulations) {
197             this.stackManipulations = new ArrayList<StackManipulation>();
198             for (StackManipulation stackManipulation : stackManipulations) {
199                 if (stackManipulation instanceof Compound) {
200                     this.stackManipulations.addAll(((Compound) stackManipulation).stackManipulations);
201                 } else if (!(stackManipulation instanceof Trivial)) {
202                     this.stackManipulations.add(stackManipulation);
203                 }
204             }
205         }
206
207         /**
208          * {@inheritDoc}
209          */

210         public boolean isValid() {
211             for (StackManipulation stackManipulation : stackManipulations) {
212                 if (!stackManipulation.isValid()) {
213                     return false;
214                 }
215             }
216             return true;
217         }
218
219         /**
220          * {@inheritDoc}
221          */

222         public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
223             Size size = new Size(0, 0);
224             for (StackManipulation stackManipulation : stackManipulations) {
225                 size = size.aggregate(stackManipulation.apply(methodVisitor, implementationContext));
226             }
227             return size;
228         }
229     }
230 }
231