2 * ASM: a very small and fast Java bytecode manipulation framework
3 * Copyright (c) 2000-2005 INRIA, France Telecom
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holders nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGE.
30 package org.objectweb.asm.tree.analysis;
32 import java.util.ArrayList;
33 import java.util.List;
35 import org.objectweb.asm.Opcodes;
36 import org.objectweb.asm.Type;
37 import org.objectweb.asm.tree.AbstractInsnNode;
38 import org.objectweb.asm.tree.IincInsnNode;
39 import org.objectweb.asm.tree.MethodInsnNode;
40 import org.objectweb.asm.tree.MultiANewArrayInsnNode;
41 import org.objectweb.asm.tree.VarInsnNode;
44 * A symbolic execution stack frame. A stack frame contains a set of local
45 * variable slots, and an operand stack. Warning: long and double values are
46 * represented by <i>two</i> slots in local variables, and by <i>one</i> slot
47 * in the operand stack.
49 * @author Eric Bruneton
54 * The local variables and operand stack of this frame.
56 private Value[] values;
59 * The number of local variables of this frame.
64 * The number of elements in the operand stack.
69 * Constructs a new frame with the given size.
71 * @param nLocals the maximum number of local variables of the frame.
72 * @param nStack the maximum stack size of the frame.
74 public Frame(final int nLocals, final int nStack) {
75 this.values = new Value[nLocals + nStack];
76 this.locals = nLocals;
80 * Constructs a new frame that is identical to the given frame.
84 public Frame(final Frame src) {
85 this(src.locals, src.values.length - src.locals);
90 * Copies the state of the given frame into this frame.
95 public Frame init(final Frame src) {
96 System.arraycopy(src.values, 0, values, 0, values.length);
102 * Returns the maximum number of local variables of this frame.
104 * @return the maximum number of local variables of this frame.
106 public int getLocals() {
111 * Returns the value of the given local variable.
113 * @param i a local variable index.
114 * @return the value of the given local variable.
115 * @throws IndexOutOfBoundsException if the variable does not exist.
117 public Value getLocal(final int i) throws IndexOutOfBoundsException {
119 throw new IndexOutOfBoundsException("Trying to access an inexistant local variable");
125 * Sets the value of the given local variable.
127 * @param i a local variable index.
128 * @param value the new value of this local variable.
129 * @throws IndexOutOfBoundsException if the variable does not exist.
131 public void setLocal(final int i, final Value value)
132 throws IndexOutOfBoundsException
135 throw new IndexOutOfBoundsException("Trying to access an inexistant local variable");
141 * Returns the number of values in the operand stack of this frame. Long and
142 * double values are treated as single values.
144 * @return the number of values in the operand stack of this frame.
146 public int getStackSize() {
151 * Returns the value of the given operand stack slot.
153 * @param i the index of an operand stack slot.
154 * @return the value of the given operand stack slot.
155 * @throws IndexOutOfBoundsException if the operand stack slot does not
158 public Value getStack(final int i) throws IndexOutOfBoundsException {
160 throw new IndexOutOfBoundsException("Trying to access an inexistant stack element");
162 return values[i + locals];
166 * Clears the operand stack of this frame.
168 public void clearStack() {
173 * Pops a value from the operand stack of this frame.
175 * @return the value that has been popped from the stack.
176 * @throws IndexOutOfBoundsException if the operand stack is empty.
178 public Value pop() throws IndexOutOfBoundsException {
180 throw new IndexOutOfBoundsException("Cannot pop operand off an empty stack.");
182 return values[--top + locals];
186 * Pushes a value into the operand stack of this frame.
188 * @param value the value that must be pushed into the stack.
189 * @throws IndexOutOfBoundsException if the operand stack is full.
191 public void push(final Value value) throws IndexOutOfBoundsException {
192 if (top + locals >= values.length) {
193 throw new IndexOutOfBoundsException("Insufficient maximum stack size.");
195 values[top++ + locals] = value;
199 final AbstractInsnNode insn,
200 final Interpreter interpreter) throws AnalyzerException
202 Value value1, value2, value3, value4;
206 switch (insn.getOpcode()) {
209 case Opcodes.ACONST_NULL:
210 case Opcodes.ICONST_M1:
211 case Opcodes.ICONST_0:
212 case Opcodes.ICONST_1:
213 case Opcodes.ICONST_2:
214 case Opcodes.ICONST_3:
215 case Opcodes.ICONST_4:
216 case Opcodes.ICONST_5:
217 case Opcodes.LCONST_0:
218 case Opcodes.LCONST_1:
219 case Opcodes.FCONST_0:
220 case Opcodes.FCONST_1:
221 case Opcodes.FCONST_2:
222 case Opcodes.DCONST_0:
223 case Opcodes.DCONST_1:
227 push(interpreter.newOperation(insn));
234 push(interpreter.copyOperation(insn,
235 getLocal(((VarInsnNode) insn).var)));
247 push(interpreter.binaryOperation(insn, value1, value2));
254 value1 = interpreter.copyOperation(insn, pop());
255 var = ((VarInsnNode) insn).var;
256 setLocal(var, value1);
257 if (value1.getSize() == 2) {
258 setLocal(var + 1, interpreter.newValue(null));
261 Value local = getLocal(var - 1);
262 if (local != null && local.getSize() == 2) {
263 setLocal(var + 1, interpreter.newValue(null));
267 case Opcodes.IASTORE:
268 case Opcodes.LASTORE:
269 case Opcodes.FASTORE:
270 case Opcodes.DASTORE:
271 case Opcodes.AASTORE:
272 case Opcodes.BASTORE:
273 case Opcodes.CASTORE:
274 case Opcodes.SASTORE:
278 interpreter.ternaryOperation(insn, value1, value2, value3);
281 if (pop().getSize() == 2) {
282 throw new AnalyzerException("Illegal use of POP");
286 if (pop().getSize() == 1) {
287 if (pop().getSize() != 1) {
288 throw new AnalyzerException("Illegal use of POP2");
294 if (value1.getSize() != 1) {
295 throw new AnalyzerException("Illegal use of DUP");
297 push(interpreter.copyOperation(insn, value1));
298 push(interpreter.copyOperation(insn, value1));
303 if (value1.getSize() != 1 || value2.getSize() != 1) {
304 throw new AnalyzerException("Illegal use of DUP_X1");
306 push(interpreter.copyOperation(insn, value1));
307 push(interpreter.copyOperation(insn, value2));
308 push(interpreter.copyOperation(insn, value1));
312 if (value1.getSize() == 1) {
314 if (value2.getSize() == 1) {
316 if (value3.getSize() == 1) {
317 push(interpreter.copyOperation(insn, value1));
318 push(interpreter.copyOperation(insn, value3));
319 push(interpreter.copyOperation(insn, value2));
320 push(interpreter.copyOperation(insn, value1));
324 push(interpreter.copyOperation(insn, value1));
325 push(interpreter.copyOperation(insn, value2));
326 push(interpreter.copyOperation(insn, value1));
330 throw new AnalyzerException("Illegal use of DUP_X2");
333 if (value1.getSize() == 1) {
335 if (value2.getSize() == 1) {
336 push(interpreter.copyOperation(insn, value2));
337 push(interpreter.copyOperation(insn, value1));
338 push(interpreter.copyOperation(insn, value2));
339 push(interpreter.copyOperation(insn, value1));
343 push(interpreter.copyOperation(insn, value1));
344 push(interpreter.copyOperation(insn, value1));
347 throw new AnalyzerException("Illegal use of DUP2");
348 case Opcodes.DUP2_X1:
350 if (value1.getSize() == 1) {
352 if (value2.getSize() == 1) {
354 if (value3.getSize() == 1) {
355 push(interpreter.copyOperation(insn, value2));
356 push(interpreter.copyOperation(insn, value1));
357 push(interpreter.copyOperation(insn, value3));
358 push(interpreter.copyOperation(insn, value2));
359 push(interpreter.copyOperation(insn, value1));
365 if (value2.getSize() == 1) {
366 push(interpreter.copyOperation(insn, value1));
367 push(interpreter.copyOperation(insn, value2));
368 push(interpreter.copyOperation(insn, value1));
372 throw new AnalyzerException("Illegal use of DUP2_X1");
373 case Opcodes.DUP2_X2:
375 if (value1.getSize() == 1) {
377 if (value2.getSize() == 1) {
379 if (value3.getSize() == 1) {
381 if (value4.getSize() == 1) {
382 push(interpreter.copyOperation(insn, value2));
383 push(interpreter.copyOperation(insn, value1));
384 push(interpreter.copyOperation(insn, value4));
385 push(interpreter.copyOperation(insn, value3));
386 push(interpreter.copyOperation(insn, value2));
387 push(interpreter.copyOperation(insn, value1));
391 push(interpreter.copyOperation(insn, value2));
392 push(interpreter.copyOperation(insn, value1));
393 push(interpreter.copyOperation(insn, value3));
394 push(interpreter.copyOperation(insn, value2));
395 push(interpreter.copyOperation(insn, value1));
401 if (value2.getSize() == 1) {
403 if (value3.getSize() == 1) {
404 push(interpreter.copyOperation(insn, value1));
405 push(interpreter.copyOperation(insn, value3));
406 push(interpreter.copyOperation(insn, value2));
407 push(interpreter.copyOperation(insn, value1));
411 push(interpreter.copyOperation(insn, value1));
412 push(interpreter.copyOperation(insn, value2));
413 push(interpreter.copyOperation(insn, value1));
417 throw new AnalyzerException("Illegal use of DUP2_X2");
421 if (value1.getSize() != 1 || value2.getSize() != 1) {
422 throw new AnalyzerException("Illegal use of SWAP");
424 push(interpreter.copyOperation(insn, value2));
425 push(interpreter.copyOperation(insn, value1));
449 push(interpreter.binaryOperation(insn, value1, value2));
455 push(interpreter.unaryOperation(insn, pop()));
471 push(interpreter.binaryOperation(insn, value1, value2));
474 var = ((IincInsnNode) insn).var;
475 setLocal(var, interpreter.unaryOperation(insn, getLocal(var)));
492 push(interpreter.unaryOperation(insn, pop()));
501 push(interpreter.binaryOperation(insn, value1, value2));
509 interpreter.unaryOperation(insn, pop());
511 case Opcodes.IF_ICMPEQ:
512 case Opcodes.IF_ICMPNE:
513 case Opcodes.IF_ICMPLT:
514 case Opcodes.IF_ICMPGE:
515 case Opcodes.IF_ICMPGT:
516 case Opcodes.IF_ICMPLE:
517 case Opcodes.IF_ACMPEQ:
518 case Opcodes.IF_ACMPNE:
521 interpreter.binaryOperation(insn, value1, value2);
526 push(interpreter.newOperation(insn));
530 case Opcodes.TABLESWITCH:
531 case Opcodes.LOOKUPSWITCH:
532 case Opcodes.IRETURN:
533 case Opcodes.LRETURN:
534 case Opcodes.FRETURN:
535 case Opcodes.DRETURN:
536 case Opcodes.ARETURN:
537 interpreter.unaryOperation(insn, pop());
541 case Opcodes.GETSTATIC:
542 push(interpreter.newOperation(insn));
544 case Opcodes.PUTSTATIC:
545 interpreter.unaryOperation(insn, pop());
547 case Opcodes.GETFIELD:
548 push(interpreter.unaryOperation(insn, pop()));
550 case Opcodes.PUTFIELD:
553 interpreter.binaryOperation(insn, value1, value2);
555 case Opcodes.INVOKEVIRTUAL:
556 case Opcodes.INVOKESPECIAL:
557 case Opcodes.INVOKESTATIC:
558 case Opcodes.INVOKEINTERFACE:
559 values = new ArrayList();
560 String desc = ((MethodInsnNode) insn).desc;
561 for (int i = Type.getArgumentTypes(desc).length; i > 0; --i) {
562 values.add(0, pop());
564 if (insn.getOpcode() != Opcodes.INVOKESTATIC) {
565 values.add(0, pop());
567 if (Type.getReturnType(desc) == Type.VOID_TYPE) {
568 interpreter.naryOperation(insn, values);
570 push(interpreter.naryOperation(insn, values));
574 push(interpreter.newOperation(insn));
576 case Opcodes.NEWARRAY:
577 case Opcodes.ANEWARRAY:
578 case Opcodes.ARRAYLENGTH:
579 push(interpreter.unaryOperation(insn, pop()));
582 interpreter.unaryOperation(insn, pop());
584 case Opcodes.CHECKCAST:
585 case Opcodes.INSTANCEOF:
586 push(interpreter.unaryOperation(insn, pop()));
588 case Opcodes.MONITORENTER:
589 case Opcodes.MONITOREXIT:
590 interpreter.unaryOperation(insn, pop());
592 case Opcodes.MULTIANEWARRAY:
593 values = new ArrayList();
594 for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) {
595 values.add(0, pop());
597 push(interpreter.naryOperation(insn, values));
600 case Opcodes.IFNONNULL:
601 interpreter.unaryOperation(insn, pop());
604 throw new RuntimeException("Illegal opcode");
609 * Merges this frame with the given frame.
611 * @param frame a frame.
612 * @param interpreter the interpreter used to merge values.
613 * @return <tt>true</tt> if this frame has been changed as a result of the
614 * merge operation, or <tt>false</tt> otherwise.
615 * @throws AnalyzerException if the frames have incompatible sizes.
617 public boolean merge(final Frame frame, final Interpreter interpreter)
618 throws AnalyzerException
620 if (top != frame.top) {
621 throw new AnalyzerException("Incompatible stack heights");
623 boolean changes = false;
624 for (int i = 0; i < locals + top; ++i) {
625 Value v = interpreter.merge(values[i], frame.values[i]);
626 if (v != values[i]) {
635 * Merges this frame with the given frame (case of a RET instruction).
637 * @param frame a frame
638 * @param access the local variables that have been accessed by the
639 * subroutine to which the RET instruction corresponds.
640 * @return <tt>true</tt> if this frame has been changed as a result of the
641 * merge operation, or <tt>false</tt> otherwise.
643 public boolean merge(final Frame frame, final boolean[] access) {
644 boolean changes = false;
645 for (int i = 0; i < locals; ++i) {
646 if (!access[i] && !values[i].equals(frame.values[i])) {
647 values[i] = frame.values[i];
655 * Returns a string representation of this frame.
657 * @return a string representation of this frame.
659 public String toString() {
660 StringBuffer b = new StringBuffer();
661 for (int i = 0; i < locals; ++i) {
662 b.append(values[i]).append(' ');
665 for (int i = 0; i < top; ++i) {
666 b.append(values[i + locals].toString()).append(' ');