Home | History | Annotate | Download | only in analysis
      1 /***
      2  * ASM: a very small and fast Java bytecode manipulation framework
      3  * Copyright (c) 2000-2007 INRIA, France Telecom
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      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.
     17  *
     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.
     29  */
     30 package org.mockito.asm.tree.analysis;
     31 
     32 import java.util.ArrayList;
     33 import java.util.List;
     34 
     35 import org.mockito.asm.Opcodes;
     36 import org.mockito.asm.Type;
     37 import org.mockito.asm.tree.AbstractInsnNode;
     38 import org.mockito.asm.tree.IincInsnNode;
     39 import org.mockito.asm.tree.MethodInsnNode;
     40 import org.mockito.asm.tree.MultiANewArrayInsnNode;
     41 import org.mockito.asm.tree.VarInsnNode;
     42 
     43 /**
     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.
     48  *
     49  * @author Eric Bruneton
     50  */
     51 public class Frame {
     52 
     53     /**
     54      * The local variables and operand stack of this frame.
     55      */
     56     private Value[] values;
     57 
     58     /**
     59      * The number of local variables of this frame.
     60      */
     61     private int locals;
     62 
     63     /**
     64      * The number of elements in the operand stack.
     65      */
     66     private int top;
     67 
     68     /**
     69      * Constructs a new frame with the given size.
     70      *
     71      * @param nLocals the maximum number of local variables of the frame.
     72      * @param nStack the maximum stack size of the frame.
     73      */
     74     public Frame(final int nLocals, final int nStack) {
     75         this.values = new Value[nLocals + nStack];
     76         this.locals = nLocals;
     77     }
     78 
     79     /**
     80      * Constructs a new frame that is identical to the given frame.
     81      *
     82      * @param src a frame.
     83      */
     84     public Frame(final Frame src) {
     85         this(src.locals, src.values.length - src.locals);
     86         init(src);
     87     }
     88 
     89     /**
     90      * Copies the state of the given frame into this frame.
     91      *
     92      * @param src a frame.
     93      * @return this frame.
     94      */
     95     public Frame init(final Frame src) {
     96         System.arraycopy(src.values, 0, values, 0, values.length);
     97         top = src.top;
     98         return this;
     99     }
    100 
    101     /**
    102      * Returns the maximum number of local variables of this frame.
    103      *
    104      * @return the maximum number of local variables of this frame.
    105      */
    106     public int getLocals() {
    107         return locals;
    108     }
    109 
    110     /**
    111      * Returns the value of the given local variable.
    112      *
    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.
    116      */
    117     public Value getLocal(final int i) throws IndexOutOfBoundsException {
    118         if (i >= locals) {
    119             throw new IndexOutOfBoundsException("Trying to access an inexistant local variable");
    120         }
    121         return values[i];
    122     }
    123 
    124     /**
    125      * Sets the value of the given local variable.
    126      *
    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.
    130      */
    131     public void setLocal(final int i, final Value value)
    132             throws IndexOutOfBoundsException
    133     {
    134         if (i >= locals) {
    135             throw new IndexOutOfBoundsException("Trying to access an inexistant local variable");
    136         }
    137         values[i] = value;
    138     }
    139 
    140     /**
    141      * Returns the number of values in the operand stack of this frame. Long and
    142      * double values are treated as single values.
    143      *
    144      * @return the number of values in the operand stack of this frame.
    145      */
    146     public int getStackSize() {
    147         return top;
    148     }
    149 
    150     /**
    151      * Returns the value of the given operand stack slot.
    152      *
    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
    156      *         exist.
    157      */
    158     public Value getStack(final int i) throws IndexOutOfBoundsException {
    159         return values[i + locals];
    160     }
    161 
    162     /**
    163      * Clears the operand stack of this frame.
    164      */
    165     public void clearStack() {
    166         top = 0;
    167     }
    168 
    169     /**
    170      * Pops a value from the operand stack of this frame.
    171      *
    172      * @return the value that has been popped from the stack.
    173      * @throws IndexOutOfBoundsException if the operand stack is empty.
    174      */
    175     public Value pop() throws IndexOutOfBoundsException {
    176         if (top == 0) {
    177             throw new IndexOutOfBoundsException("Cannot pop operand off an empty stack.");
    178         }
    179         return values[--top + locals];
    180     }
    181 
    182     /**
    183      * Pushes a value into the operand stack of this frame.
    184      *
    185      * @param value the value that must be pushed into the stack.
    186      * @throws IndexOutOfBoundsException if the operand stack is full.
    187      */
    188     public void push(final Value value) throws IndexOutOfBoundsException {
    189         if (top + locals >= values.length) {
    190             throw new IndexOutOfBoundsException("Insufficient maximum stack size.");
    191         }
    192         values[top++ + locals] = value;
    193     }
    194 
    195     public void execute(
    196         final AbstractInsnNode insn,
    197         final Interpreter interpreter) throws AnalyzerException
    198     {
    199         Value value1, value2, value3, value4;
    200         List values;
    201         int var;
    202 
    203         switch (insn.getOpcode()) {
    204             case Opcodes.NOP:
    205                 break;
    206             case Opcodes.ACONST_NULL:
    207             case Opcodes.ICONST_M1:
    208             case Opcodes.ICONST_0:
    209             case Opcodes.ICONST_1:
    210             case Opcodes.ICONST_2:
    211             case Opcodes.ICONST_3:
    212             case Opcodes.ICONST_4:
    213             case Opcodes.ICONST_5:
    214             case Opcodes.LCONST_0:
    215             case Opcodes.LCONST_1:
    216             case Opcodes.FCONST_0:
    217             case Opcodes.FCONST_1:
    218             case Opcodes.FCONST_2:
    219             case Opcodes.DCONST_0:
    220             case Opcodes.DCONST_1:
    221             case Opcodes.BIPUSH:
    222             case Opcodes.SIPUSH:
    223             case Opcodes.LDC:
    224                 push(interpreter.newOperation(insn));
    225                 break;
    226             case Opcodes.ILOAD:
    227             case Opcodes.LLOAD:
    228             case Opcodes.FLOAD:
    229             case Opcodes.DLOAD:
    230             case Opcodes.ALOAD:
    231                 push(interpreter.copyOperation(insn,
    232                         getLocal(((VarInsnNode) insn).var)));
    233                 break;
    234             case Opcodes.IALOAD:
    235             case Opcodes.LALOAD:
    236             case Opcodes.FALOAD:
    237             case Opcodes.DALOAD:
    238             case Opcodes.AALOAD:
    239             case Opcodes.BALOAD:
    240             case Opcodes.CALOAD:
    241             case Opcodes.SALOAD:
    242                 value2 = pop();
    243                 value1 = pop();
    244                 push(interpreter.binaryOperation(insn, value1, value2));
    245                 break;
    246             case Opcodes.ISTORE:
    247             case Opcodes.LSTORE:
    248             case Opcodes.FSTORE:
    249             case Opcodes.DSTORE:
    250             case Opcodes.ASTORE:
    251                 value1 = interpreter.copyOperation(insn, pop());
    252                 var = ((VarInsnNode) insn).var;
    253                 setLocal(var, value1);
    254                 if (value1.getSize() == 2) {
    255                     setLocal(var + 1, interpreter.newValue(null));
    256                 }
    257                 if (var > 0) {
    258                     Value local = getLocal(var - 1);
    259                     if (local != null && local.getSize() == 2) {
    260                         setLocal(var - 1, interpreter.newValue(null));
    261                     }
    262                 }
    263                 break;
    264             case Opcodes.IASTORE:
    265             case Opcodes.LASTORE:
    266             case Opcodes.FASTORE:
    267             case Opcodes.DASTORE:
    268             case Opcodes.AASTORE:
    269             case Opcodes.BASTORE:
    270             case Opcodes.CASTORE:
    271             case Opcodes.SASTORE:
    272                 value3 = pop();
    273                 value2 = pop();
    274                 value1 = pop();
    275                 interpreter.ternaryOperation(insn, value1, value2, value3);
    276                 break;
    277             case Opcodes.POP:
    278                 if (pop().getSize() == 2) {
    279                     throw new AnalyzerException("Illegal use of POP");
    280                 }
    281                 break;
    282             case Opcodes.POP2:
    283                 if (pop().getSize() == 1) {
    284                     if (pop().getSize() != 1) {
    285                         throw new AnalyzerException("Illegal use of POP2");
    286                     }
    287                 }
    288                 break;
    289             case Opcodes.DUP:
    290                 value1 = pop();
    291                 if (value1.getSize() != 1) {
    292                     throw new AnalyzerException("Illegal use of DUP");
    293                 }
    294                 push(interpreter.copyOperation(insn, value1));
    295                 push(interpreter.copyOperation(insn, value1));
    296                 break;
    297             case Opcodes.DUP_X1:
    298                 value1 = pop();
    299                 value2 = pop();
    300                 if (value1.getSize() != 1 || value2.getSize() != 1) {
    301                     throw new AnalyzerException("Illegal use of DUP_X1");
    302                 }
    303                 push(interpreter.copyOperation(insn, value1));
    304                 push(interpreter.copyOperation(insn, value2));
    305                 push(interpreter.copyOperation(insn, value1));
    306                 break;
    307             case Opcodes.DUP_X2:
    308                 value1 = pop();
    309                 if (value1.getSize() == 1) {
    310                     value2 = pop();
    311                     if (value2.getSize() == 1) {
    312                         value3 = pop();
    313                         if (value3.getSize() == 1) {
    314                             push(interpreter.copyOperation(insn, value1));
    315                             push(interpreter.copyOperation(insn, value3));
    316                             push(interpreter.copyOperation(insn, value2));
    317                             push(interpreter.copyOperation(insn, value1));
    318                             break;
    319                         }
    320                     } else {
    321                         push(interpreter.copyOperation(insn, value1));
    322                         push(interpreter.copyOperation(insn, value2));
    323                         push(interpreter.copyOperation(insn, value1));
    324                         break;
    325                     }
    326                 }
    327                 throw new AnalyzerException("Illegal use of DUP_X2");
    328             case Opcodes.DUP2:
    329                 value1 = pop();
    330                 if (value1.getSize() == 1) {
    331                     value2 = pop();
    332                     if (value2.getSize() == 1) {
    333                         push(interpreter.copyOperation(insn, value2));
    334                         push(interpreter.copyOperation(insn, value1));
    335                         push(interpreter.copyOperation(insn, value2));
    336                         push(interpreter.copyOperation(insn, value1));
    337                         break;
    338                     }
    339                 } else {
    340                     push(interpreter.copyOperation(insn, value1));
    341                     push(interpreter.copyOperation(insn, value1));
    342                     break;
    343                 }
    344                 throw new AnalyzerException("Illegal use of DUP2");
    345             case Opcodes.DUP2_X1:
    346                 value1 = pop();
    347                 if (value1.getSize() == 1) {
    348                     value2 = pop();
    349                     if (value2.getSize() == 1) {
    350                         value3 = pop();
    351                         if (value3.getSize() == 1) {
    352                             push(interpreter.copyOperation(insn, value2));
    353                             push(interpreter.copyOperation(insn, value1));
    354                             push(interpreter.copyOperation(insn, value3));
    355                             push(interpreter.copyOperation(insn, value2));
    356                             push(interpreter.copyOperation(insn, value1));
    357                             break;
    358                         }
    359                     }
    360                 } else {
    361                     value2 = pop();
    362                     if (value2.getSize() == 1) {
    363                         push(interpreter.copyOperation(insn, value1));
    364                         push(interpreter.copyOperation(insn, value2));
    365                         push(interpreter.copyOperation(insn, value1));
    366                         break;
    367                     }
    368                 }
    369                 throw new AnalyzerException("Illegal use of DUP2_X1");
    370             case Opcodes.DUP2_X2:
    371                 value1 = pop();
    372                 if (value1.getSize() == 1) {
    373                     value2 = pop();
    374                     if (value2.getSize() == 1) {
    375                         value3 = pop();
    376                         if (value3.getSize() == 1) {
    377                             value4 = pop();
    378                             if (value4.getSize() == 1) {
    379                                 push(interpreter.copyOperation(insn, value2));
    380                                 push(interpreter.copyOperation(insn, value1));
    381                                 push(interpreter.copyOperation(insn, value4));
    382                                 push(interpreter.copyOperation(insn, value3));
    383                                 push(interpreter.copyOperation(insn, value2));
    384                                 push(interpreter.copyOperation(insn, value1));
    385                                 break;
    386                             }
    387                         } else {
    388                             push(interpreter.copyOperation(insn, value2));
    389                             push(interpreter.copyOperation(insn, value1));
    390                             push(interpreter.copyOperation(insn, value3));
    391                             push(interpreter.copyOperation(insn, value2));
    392                             push(interpreter.copyOperation(insn, value1));
    393                             break;
    394                         }
    395                     }
    396                 } else {
    397                     value2 = pop();
    398                     if (value2.getSize() == 1) {
    399                         value3 = pop();
    400                         if (value3.getSize() == 1) {
    401                             push(interpreter.copyOperation(insn, value1));
    402                             push(interpreter.copyOperation(insn, value3));
    403                             push(interpreter.copyOperation(insn, value2));
    404                             push(interpreter.copyOperation(insn, value1));
    405                             break;
    406                         }
    407                     } else {
    408                         push(interpreter.copyOperation(insn, value1));
    409                         push(interpreter.copyOperation(insn, value2));
    410                         push(interpreter.copyOperation(insn, value1));
    411                         break;
    412                     }
    413                 }
    414                 throw new AnalyzerException("Illegal use of DUP2_X2");
    415             case Opcodes.SWAP:
    416                 value2 = pop();
    417                 value1 = pop();
    418                 if (value1.getSize() != 1 || value2.getSize() != 1) {
    419                     throw new AnalyzerException("Illegal use of SWAP");
    420                 }
    421                 push(interpreter.copyOperation(insn, value2));
    422                 push(interpreter.copyOperation(insn, value1));
    423                 break;
    424             case Opcodes.IADD:
    425             case Opcodes.LADD:
    426             case Opcodes.FADD:
    427             case Opcodes.DADD:
    428             case Opcodes.ISUB:
    429             case Opcodes.LSUB:
    430             case Opcodes.FSUB:
    431             case Opcodes.DSUB:
    432             case Opcodes.IMUL:
    433             case Opcodes.LMUL:
    434             case Opcodes.FMUL:
    435             case Opcodes.DMUL:
    436             case Opcodes.IDIV:
    437             case Opcodes.LDIV:
    438             case Opcodes.FDIV:
    439             case Opcodes.DDIV:
    440             case Opcodes.IREM:
    441             case Opcodes.LREM:
    442             case Opcodes.FREM:
    443             case Opcodes.DREM:
    444                 value2 = pop();
    445                 value1 = pop();
    446                 push(interpreter.binaryOperation(insn, value1, value2));
    447                 break;
    448             case Opcodes.INEG:
    449             case Opcodes.LNEG:
    450             case Opcodes.FNEG:
    451             case Opcodes.DNEG:
    452                 push(interpreter.unaryOperation(insn, pop()));
    453                 break;
    454             case Opcodes.ISHL:
    455             case Opcodes.LSHL:
    456             case Opcodes.ISHR:
    457             case Opcodes.LSHR:
    458             case Opcodes.IUSHR:
    459             case Opcodes.LUSHR:
    460             case Opcodes.IAND:
    461             case Opcodes.LAND:
    462             case Opcodes.IOR:
    463             case Opcodes.LOR:
    464             case Opcodes.IXOR:
    465             case Opcodes.LXOR:
    466                 value2 = pop();
    467                 value1 = pop();
    468                 push(interpreter.binaryOperation(insn, value1, value2));
    469                 break;
    470             case Opcodes.IINC:
    471                 var = ((IincInsnNode) insn).var;
    472                 setLocal(var, interpreter.unaryOperation(insn, getLocal(var)));
    473                 break;
    474             case Opcodes.I2L:
    475             case Opcodes.I2F:
    476             case Opcodes.I2D:
    477             case Opcodes.L2I:
    478             case Opcodes.L2F:
    479             case Opcodes.L2D:
    480             case Opcodes.F2I:
    481             case Opcodes.F2L:
    482             case Opcodes.F2D:
    483             case Opcodes.D2I:
    484             case Opcodes.D2L:
    485             case Opcodes.D2F:
    486             case Opcodes.I2B:
    487             case Opcodes.I2C:
    488             case Opcodes.I2S:
    489                 push(interpreter.unaryOperation(insn, pop()));
    490                 break;
    491             case Opcodes.LCMP:
    492             case Opcodes.FCMPL:
    493             case Opcodes.FCMPG:
    494             case Opcodes.DCMPL:
    495             case Opcodes.DCMPG:
    496                 value2 = pop();
    497                 value1 = pop();
    498                 push(interpreter.binaryOperation(insn, value1, value2));
    499                 break;
    500             case Opcodes.IFEQ:
    501             case Opcodes.IFNE:
    502             case Opcodes.IFLT:
    503             case Opcodes.IFGE:
    504             case Opcodes.IFGT:
    505             case Opcodes.IFLE:
    506                 interpreter.unaryOperation(insn, pop());
    507                 break;
    508             case Opcodes.IF_ICMPEQ:
    509             case Opcodes.IF_ICMPNE:
    510             case Opcodes.IF_ICMPLT:
    511             case Opcodes.IF_ICMPGE:
    512             case Opcodes.IF_ICMPGT:
    513             case Opcodes.IF_ICMPLE:
    514             case Opcodes.IF_ACMPEQ:
    515             case Opcodes.IF_ACMPNE:
    516                 value2 = pop();
    517                 value1 = pop();
    518                 interpreter.binaryOperation(insn, value1, value2);
    519                 break;
    520             case Opcodes.GOTO:
    521                 break;
    522             case Opcodes.JSR:
    523                 push(interpreter.newOperation(insn));
    524                 break;
    525             case Opcodes.RET:
    526                 break;
    527             case Opcodes.TABLESWITCH:
    528             case Opcodes.LOOKUPSWITCH:
    529             case Opcodes.IRETURN:
    530             case Opcodes.LRETURN:
    531             case Opcodes.FRETURN:
    532             case Opcodes.DRETURN:
    533             case Opcodes.ARETURN:
    534                 interpreter.unaryOperation(insn, pop());
    535                 break;
    536             case Opcodes.RETURN:
    537                 break;
    538             case Opcodes.GETSTATIC:
    539                 push(interpreter.newOperation(insn));
    540                 break;
    541             case Opcodes.PUTSTATIC:
    542                 interpreter.unaryOperation(insn, pop());
    543                 break;
    544             case Opcodes.GETFIELD:
    545                 push(interpreter.unaryOperation(insn, pop()));
    546                 break;
    547             case Opcodes.PUTFIELD:
    548                 value2 = pop();
    549                 value1 = pop();
    550                 interpreter.binaryOperation(insn, value1, value2);
    551                 break;
    552             case Opcodes.INVOKEVIRTUAL:
    553             case Opcodes.INVOKESPECIAL:
    554             case Opcodes.INVOKESTATIC:
    555             case Opcodes.INVOKEINTERFACE:
    556                 values = new ArrayList();
    557                 String desc = ((MethodInsnNode) insn).desc;
    558                 for (int i = Type.getArgumentTypes(desc).length; i > 0; --i) {
    559                     values.add(0, pop());
    560                 }
    561                 if (insn.getOpcode() != Opcodes.INVOKESTATIC) {
    562                     values.add(0, pop());
    563                 }
    564                 if (Type.getReturnType(desc) == Type.VOID_TYPE) {
    565                     interpreter.naryOperation(insn, values);
    566                 } else {
    567                     push(interpreter.naryOperation(insn, values));
    568                 }
    569                 break;
    570             case Opcodes.NEW:
    571                 push(interpreter.newOperation(insn));
    572                 break;
    573             case Opcodes.NEWARRAY:
    574             case Opcodes.ANEWARRAY:
    575             case Opcodes.ARRAYLENGTH:
    576                 push(interpreter.unaryOperation(insn, pop()));
    577                 break;
    578             case Opcodes.ATHROW:
    579                 interpreter.unaryOperation(insn, pop());
    580                 break;
    581             case Opcodes.CHECKCAST:
    582             case Opcodes.INSTANCEOF:
    583                 push(interpreter.unaryOperation(insn, pop()));
    584                 break;
    585             case Opcodes.MONITORENTER:
    586             case Opcodes.MONITOREXIT:
    587                 interpreter.unaryOperation(insn, pop());
    588                 break;
    589             case Opcodes.MULTIANEWARRAY:
    590                 values = new ArrayList();
    591                 for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) {
    592                     values.add(0, pop());
    593                 }
    594                 push(interpreter.naryOperation(insn, values));
    595                 break;
    596             case Opcodes.IFNULL:
    597             case Opcodes.IFNONNULL:
    598                 interpreter.unaryOperation(insn, pop());
    599                 break;
    600             default:
    601                 throw new RuntimeException("Illegal opcode");
    602         }
    603     }
    604 
    605     /**
    606      * Merges this frame with the given frame.
    607      *
    608      * @param frame a frame.
    609      * @param interpreter the interpreter used to merge values.
    610      * @return <tt>true</tt> if this frame has been changed as a result of the
    611      *         merge operation, or <tt>false</tt> otherwise.
    612      * @throws AnalyzerException if the frames have incompatible sizes.
    613      */
    614     public boolean merge(final Frame frame, final Interpreter interpreter)
    615             throws AnalyzerException
    616     {
    617         if (top != frame.top) {
    618             throw new AnalyzerException("Incompatible stack heights");
    619         }
    620         boolean changes = false;
    621         for (int i = 0; i < locals + top; ++i) {
    622             Value v = interpreter.merge(values[i], frame.values[i]);
    623             if (v != values[i]) {
    624                 values[i] = v;
    625                 changes |= true;
    626             }
    627         }
    628         return changes;
    629     }
    630 
    631     /**
    632      * Merges this frame with the given frame (case of a RET instruction).
    633      *
    634      * @param frame a frame
    635      * @param access the local variables that have been accessed by the
    636      *        subroutine to which the RET instruction corresponds.
    637      * @return <tt>true</tt> if this frame has been changed as a result of the
    638      *         merge operation, or <tt>false</tt> otherwise.
    639      */
    640     public boolean merge(final Frame frame, final boolean[] access) {
    641         boolean changes = false;
    642         for (int i = 0; i < locals; ++i) {
    643             if (!access[i] && !values[i].equals(frame.values[i])) {
    644                 values[i] = frame.values[i];
    645                 changes = true;
    646             }
    647         }
    648         return changes;
    649     }
    650 
    651     /**
    652      * Returns a string representation of this frame.
    653      *
    654      * @return a string representation of this frame.
    655      */
    656     public String toString() {
    657         StringBuffer b = new StringBuffer();
    658         for (int i = 0; i < getLocals(); ++i) {
    659             b.append(getLocal(i));
    660         }
    661         b.append(' ');
    662         for (int i = 0; i < getStackSize(); ++i) {
    663             b.append(getStack(i).toString());
    664         }
    665         return b.toString();
    666     }
    667 }
    668