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