1/* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25/* 26 * This file is available under and governed by the GNU General Public 27 * License version 2 only, as published by the Free Software Foundation. 28 * However, the following notice accompanied the original version of this 29 * file: 30 * 31 * ASM: a very small and fast Java bytecode manipulation framework 32 * Copyright (c) 2000-2011 INRIA, France Telecom 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. Neither the name of the copyright holders nor the names of its 44 * contributors may be used to endorse or promote products derived from 45 * this software without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 57 * THE POSSIBILITY OF SUCH DAMAGE. 58 */ 59package jdk.internal.org.objectweb.asm.tree.analysis; 60 61import java.util.ArrayList; 62import java.util.List; 63 64import jdk.internal.org.objectweb.asm.Opcodes; 65import jdk.internal.org.objectweb.asm.Type; 66import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; 67import jdk.internal.org.objectweb.asm.tree.IincInsnNode; 68import jdk.internal.org.objectweb.asm.tree.InvokeDynamicInsnNode; 69import jdk.internal.org.objectweb.asm.tree.MethodInsnNode; 70import jdk.internal.org.objectweb.asm.tree.MultiANewArrayInsnNode; 71import jdk.internal.org.objectweb.asm.tree.VarInsnNode; 72 73/** 74 * A symbolic execution stack frame. A stack frame contains a set of local 75 * variable slots, and an operand stack. Warning: long and double values are 76 * represented by <i>two</i> slots in local variables, and by <i>one</i> slot in 77 * the operand stack. 78 * 79 * @param <V> 80 * type of the Value used for the analysis. 81 * 82 * @author Eric Bruneton 83 */ 84public class Frame<V extends Value> { 85 86 /** 87 * The expected return type of the analyzed method, or <tt>null</tt> if the 88 * method returns void. 89 */ 90 private V returnValue; 91 92 /** 93 * The local variables and operand stack of this frame. 94 */ 95 private V[] values; 96 97 /** 98 * The number of local variables of this frame. 99 */ 100 private int locals; 101 102 /** 103 * The number of elements in the operand stack. 104 */ 105 private int top; 106 107 /** 108 * Constructs a new frame with the given size. 109 * 110 * @param nLocals 111 * the maximum number of local variables of the frame. 112 * @param nStack 113 * the maximum stack size of the frame. 114 */ 115 @SuppressWarnings("unchecked") 116 public Frame(final int nLocals, final int nStack) { 117 this.values = (V[]) new Value[nLocals + nStack]; 118 this.locals = nLocals; 119 } 120 121 /** 122 * Constructs a new frame that is identical to the given frame. 123 * 124 * @param src 125 * a frame. 126 */ 127 public Frame(final Frame<? extends V> src) { 128 this(src.locals, src.values.length - src.locals); 129 init(src); 130 } 131 132 /** 133 * Copies the state of the given frame into this frame. 134 * 135 * @param src 136 * a frame. 137 * @return this frame. 138 */ 139 public Frame<V> init(final Frame<? extends V> src) { 140 returnValue = src.returnValue; 141 System.arraycopy(src.values, 0, values, 0, values.length); 142 top = src.top; 143 return this; 144 } 145 146 /** 147 * Sets the expected return type of the analyzed method. 148 * 149 * @param v 150 * the expected return type of the analyzed method, or 151 * <tt>null</tt> if the method returns void. 152 */ 153 public void setReturn(final V v) { 154 returnValue = v; 155 } 156 157 /** 158 * Returns the maximum number of local variables of this frame. 159 * 160 * @return the maximum number of local variables of this frame. 161 */ 162 public int getLocals() { 163 return locals; 164 } 165 166 /** 167 * Returns the maximum stack size of this frame. 168 * 169 * @return the maximum stack size of this frame. 170 */ 171 public int getMaxStackSize() { 172 return values.length - locals; 173 } 174 175 /** 176 * Returns the value of the given local variable. 177 * 178 * @param i 179 * a local variable index. 180 * @return the value of the given local variable. 181 * @throws IndexOutOfBoundsException 182 * if the variable does not exist. 183 */ 184 public V getLocal(final int i) throws IndexOutOfBoundsException { 185 if (i >= locals) { 186 throw new IndexOutOfBoundsException( 187 "Trying to access an inexistant local variable"); 188 } 189 return values[i]; 190 } 191 192 /** 193 * Sets the value of the given local variable. 194 * 195 * @param i 196 * a local variable index. 197 * @param value 198 * the new value of this local variable. 199 * @throws IndexOutOfBoundsException 200 * if the variable does not exist. 201 */ 202 public void setLocal(final int i, final V value) 203 throws IndexOutOfBoundsException { 204 if (i >= locals) { 205 throw new IndexOutOfBoundsException( 206 "Trying to access an inexistant local variable " + i); 207 } 208 values[i] = value; 209 } 210 211 /** 212 * Returns the number of values in the operand stack of this frame. Long and 213 * double values are treated as single values. 214 * 215 * @return the number of values in the operand stack of this frame. 216 */ 217 public int getStackSize() { 218 return top; 219 } 220 221 /** 222 * Returns the value of the given operand stack slot. 223 * 224 * @param i 225 * the index of an operand stack slot. 226 * @return the value of the given operand stack slot. 227 * @throws IndexOutOfBoundsException 228 * if the operand stack slot does not exist. 229 */ 230 public V getStack(final int i) throws IndexOutOfBoundsException { 231 return values[i + locals]; 232 } 233 234 /** 235 * Clears the operand stack of this frame. 236 */ 237 public void clearStack() { 238 top = 0; 239 } 240 241 /** 242 * Pops a value from the operand stack of this frame. 243 * 244 * @return the value that has been popped from the stack. 245 * @throws IndexOutOfBoundsException 246 * if the operand stack is empty. 247 */ 248 public V pop() throws IndexOutOfBoundsException { 249 if (top == 0) { 250 throw new IndexOutOfBoundsException( 251 "Cannot pop operand off an empty stack."); 252 } 253 return values[--top + locals]; 254 } 255 256 /** 257 * Pushes a value into the operand stack of this frame. 258 * 259 * @param value 260 * the value that must be pushed into the stack. 261 * @throws IndexOutOfBoundsException 262 * if the operand stack is full. 263 */ 264 public void push(final V value) throws IndexOutOfBoundsException { 265 if (top + locals >= values.length) { 266 throw new IndexOutOfBoundsException( 267 "Insufficient maximum stack size."); 268 } 269 values[top++ + locals] = value; 270 } 271 272 public void execute(final AbstractInsnNode insn, 273 final Interpreter<V> interpreter) throws AnalyzerException { 274 V value1, value2, value3, value4; 275 List<V> values; 276 int var; 277 278 switch (insn.getOpcode()) { 279 case Opcodes.NOP: 280 break; 281 case Opcodes.ACONST_NULL: 282 case Opcodes.ICONST_M1: 283 case Opcodes.ICONST_0: 284 case Opcodes.ICONST_1: 285 case Opcodes.ICONST_2: 286 case Opcodes.ICONST_3: 287 case Opcodes.ICONST_4: 288 case Opcodes.ICONST_5: 289 case Opcodes.LCONST_0: 290 case Opcodes.LCONST_1: 291 case Opcodes.FCONST_0: 292 case Opcodes.FCONST_1: 293 case Opcodes.FCONST_2: 294 case Opcodes.DCONST_0: 295 case Opcodes.DCONST_1: 296 case Opcodes.BIPUSH: 297 case Opcodes.SIPUSH: 298 case Opcodes.LDC: 299 push(interpreter.newOperation(insn)); 300 break; 301 case Opcodes.ILOAD: 302 case Opcodes.LLOAD: 303 case Opcodes.FLOAD: 304 case Opcodes.DLOAD: 305 case Opcodes.ALOAD: 306 push(interpreter.copyOperation(insn, 307 getLocal(((VarInsnNode) insn).var))); 308 break; 309 case Opcodes.IALOAD: 310 case Opcodes.LALOAD: 311 case Opcodes.FALOAD: 312 case Opcodes.DALOAD: 313 case Opcodes.AALOAD: 314 case Opcodes.BALOAD: 315 case Opcodes.CALOAD: 316 case Opcodes.SALOAD: 317 value2 = pop(); 318 value1 = pop(); 319 push(interpreter.binaryOperation(insn, value1, value2)); 320 break; 321 case Opcodes.ISTORE: 322 case Opcodes.LSTORE: 323 case Opcodes.FSTORE: 324 case Opcodes.DSTORE: 325 case Opcodes.ASTORE: 326 value1 = interpreter.copyOperation(insn, pop()); 327 var = ((VarInsnNode) insn).var; 328 setLocal(var, value1); 329 if (value1.getSize() == 2) { 330 setLocal(var + 1, interpreter.newValue(null)); 331 } 332 if (var > 0) { 333 Value local = getLocal(var - 1); 334 if (local != null && local.getSize() == 2) { 335 setLocal(var - 1, interpreter.newValue(null)); 336 } 337 } 338 break; 339 case Opcodes.IASTORE: 340 case Opcodes.LASTORE: 341 case Opcodes.FASTORE: 342 case Opcodes.DASTORE: 343 case Opcodes.AASTORE: 344 case Opcodes.BASTORE: 345 case Opcodes.CASTORE: 346 case Opcodes.SASTORE: 347 value3 = pop(); 348 value2 = pop(); 349 value1 = pop(); 350 interpreter.ternaryOperation(insn, value1, value2, value3); 351 break; 352 case Opcodes.POP: 353 if (pop().getSize() == 2) { 354 throw new AnalyzerException(insn, "Illegal use of POP"); 355 } 356 break; 357 case Opcodes.POP2: 358 if (pop().getSize() == 1) { 359 if (pop().getSize() != 1) { 360 throw new AnalyzerException(insn, "Illegal use of POP2"); 361 } 362 } 363 break; 364 case Opcodes.DUP: 365 value1 = pop(); 366 if (value1.getSize() != 1) { 367 throw new AnalyzerException(insn, "Illegal use of DUP"); 368 } 369 push(value1); 370 push(interpreter.copyOperation(insn, value1)); 371 break; 372 case Opcodes.DUP_X1: 373 value1 = pop(); 374 value2 = pop(); 375 if (value1.getSize() != 1 || value2.getSize() != 1) { 376 throw new AnalyzerException(insn, "Illegal use of DUP_X1"); 377 } 378 push(interpreter.copyOperation(insn, value1)); 379 push(value2); 380 push(value1); 381 break; 382 case Opcodes.DUP_X2: 383 value1 = pop(); 384 if (value1.getSize() == 1) { 385 value2 = pop(); 386 if (value2.getSize() == 1) { 387 value3 = pop(); 388 if (value3.getSize() == 1) { 389 push(interpreter.copyOperation(insn, value1)); 390 push(value3); 391 push(value2); 392 push(value1); 393 break; 394 } 395 } else { 396 push(interpreter.copyOperation(insn, value1)); 397 push(value2); 398 push(value1); 399 break; 400 } 401 } 402 throw new AnalyzerException(insn, "Illegal use of DUP_X2"); 403 case Opcodes.DUP2: 404 value1 = pop(); 405 if (value1.getSize() == 1) { 406 value2 = pop(); 407 if (value2.getSize() == 1) { 408 push(value2); 409 push(value1); 410 push(interpreter.copyOperation(insn, value2)); 411 push(interpreter.copyOperation(insn, value1)); 412 break; 413 } 414 } else { 415 push(value1); 416 push(interpreter.copyOperation(insn, value1)); 417 break; 418 } 419 throw new AnalyzerException(insn, "Illegal use of DUP2"); 420 case Opcodes.DUP2_X1: 421 value1 = pop(); 422 if (value1.getSize() == 1) { 423 value2 = pop(); 424 if (value2.getSize() == 1) { 425 value3 = pop(); 426 if (value3.getSize() == 1) { 427 push(interpreter.copyOperation(insn, value2)); 428 push(interpreter.copyOperation(insn, value1)); 429 push(value3); 430 push(value2); 431 push(value1); 432 break; 433 } 434 } 435 } else { 436 value2 = pop(); 437 if (value2.getSize() == 1) { 438 push(interpreter.copyOperation(insn, value1)); 439 push(value2); 440 push(value1); 441 break; 442 } 443 } 444 throw new AnalyzerException(insn, "Illegal use of DUP2_X1"); 445 case Opcodes.DUP2_X2: 446 value1 = pop(); 447 if (value1.getSize() == 1) { 448 value2 = pop(); 449 if (value2.getSize() == 1) { 450 value3 = pop(); 451 if (value3.getSize() == 1) { 452 value4 = pop(); 453 if (value4.getSize() == 1) { 454 push(interpreter.copyOperation(insn, value2)); 455 push(interpreter.copyOperation(insn, value1)); 456 push(value4); 457 push(value3); 458 push(value2); 459 push(value1); 460 break; 461 } 462 } else { 463 push(interpreter.copyOperation(insn, value2)); 464 push(interpreter.copyOperation(insn, value1)); 465 push(value3); 466 push(value2); 467 push(value1); 468 break; 469 } 470 } 471 } else { 472 value2 = pop(); 473 if (value2.getSize() == 1) { 474 value3 = pop(); 475 if (value3.getSize() == 1) { 476 push(interpreter.copyOperation(insn, value1)); 477 push(value3); 478 push(value2); 479 push(value1); 480 break; 481 } 482 } else { 483 push(interpreter.copyOperation(insn, value1)); 484 push(value2); 485 push(value1); 486 break; 487 } 488 } 489 throw new AnalyzerException(insn, "Illegal use of DUP2_X2"); 490 case Opcodes.SWAP: 491 value2 = pop(); 492 value1 = pop(); 493 if (value1.getSize() != 1 || value2.getSize() != 1) { 494 throw new AnalyzerException(insn, "Illegal use of SWAP"); 495 } 496 push(interpreter.copyOperation(insn, value2)); 497 push(interpreter.copyOperation(insn, value1)); 498 break; 499 case Opcodes.IADD: 500 case Opcodes.LADD: 501 case Opcodes.FADD: 502 case Opcodes.DADD: 503 case Opcodes.ISUB: 504 case Opcodes.LSUB: 505 case Opcodes.FSUB: 506 case Opcodes.DSUB: 507 case Opcodes.IMUL: 508 case Opcodes.LMUL: 509 case Opcodes.FMUL: 510 case Opcodes.DMUL: 511 case Opcodes.IDIV: 512 case Opcodes.LDIV: 513 case Opcodes.FDIV: 514 case Opcodes.DDIV: 515 case Opcodes.IREM: 516 case Opcodes.LREM: 517 case Opcodes.FREM: 518 case Opcodes.DREM: 519 value2 = pop(); 520 value1 = pop(); 521 push(interpreter.binaryOperation(insn, value1, value2)); 522 break; 523 case Opcodes.INEG: 524 case Opcodes.LNEG: 525 case Opcodes.FNEG: 526 case Opcodes.DNEG: 527 push(interpreter.unaryOperation(insn, pop())); 528 break; 529 case Opcodes.ISHL: 530 case Opcodes.LSHL: 531 case Opcodes.ISHR: 532 case Opcodes.LSHR: 533 case Opcodes.IUSHR: 534 case Opcodes.LUSHR: 535 case Opcodes.IAND: 536 case Opcodes.LAND: 537 case Opcodes.IOR: 538 case Opcodes.LOR: 539 case Opcodes.IXOR: 540 case Opcodes.LXOR: 541 value2 = pop(); 542 value1 = pop(); 543 push(interpreter.binaryOperation(insn, value1, value2)); 544 break; 545 case Opcodes.IINC: 546 var = ((IincInsnNode) insn).var; 547 setLocal(var, interpreter.unaryOperation(insn, getLocal(var))); 548 break; 549 case Opcodes.I2L: 550 case Opcodes.I2F: 551 case Opcodes.I2D: 552 case Opcodes.L2I: 553 case Opcodes.L2F: 554 case Opcodes.L2D: 555 case Opcodes.F2I: 556 case Opcodes.F2L: 557 case Opcodes.F2D: 558 case Opcodes.D2I: 559 case Opcodes.D2L: 560 case Opcodes.D2F: 561 case Opcodes.I2B: 562 case Opcodes.I2C: 563 case Opcodes.I2S: 564 push(interpreter.unaryOperation(insn, pop())); 565 break; 566 case Opcodes.LCMP: 567 case Opcodes.FCMPL: 568 case Opcodes.FCMPG: 569 case Opcodes.DCMPL: 570 case Opcodes.DCMPG: 571 value2 = pop(); 572 value1 = pop(); 573 push(interpreter.binaryOperation(insn, value1, value2)); 574 break; 575 case Opcodes.IFEQ: 576 case Opcodes.IFNE: 577 case Opcodes.IFLT: 578 case Opcodes.IFGE: 579 case Opcodes.IFGT: 580 case Opcodes.IFLE: 581 interpreter.unaryOperation(insn, pop()); 582 break; 583 case Opcodes.IF_ICMPEQ: 584 case Opcodes.IF_ICMPNE: 585 case Opcodes.IF_ICMPLT: 586 case Opcodes.IF_ICMPGE: 587 case Opcodes.IF_ICMPGT: 588 case Opcodes.IF_ICMPLE: 589 case Opcodes.IF_ACMPEQ: 590 case Opcodes.IF_ACMPNE: 591 value2 = pop(); 592 value1 = pop(); 593 interpreter.binaryOperation(insn, value1, value2); 594 break; 595 case Opcodes.GOTO: 596 break; 597 case Opcodes.JSR: 598 push(interpreter.newOperation(insn)); 599 break; 600 case Opcodes.RET: 601 break; 602 case Opcodes.TABLESWITCH: 603 case Opcodes.LOOKUPSWITCH: 604 interpreter.unaryOperation(insn, pop()); 605 break; 606 case Opcodes.IRETURN: 607 case Opcodes.LRETURN: 608 case Opcodes.FRETURN: 609 case Opcodes.DRETURN: 610 case Opcodes.ARETURN: 611 value1 = pop(); 612 interpreter.unaryOperation(insn, value1); 613 interpreter.returnOperation(insn, value1, returnValue); 614 break; 615 case Opcodes.RETURN: 616 if (returnValue != null) { 617 throw new AnalyzerException(insn, "Incompatible return type"); 618 } 619 break; 620 case Opcodes.GETSTATIC: 621 push(interpreter.newOperation(insn)); 622 break; 623 case Opcodes.PUTSTATIC: 624 interpreter.unaryOperation(insn, pop()); 625 break; 626 case Opcodes.GETFIELD: 627 push(interpreter.unaryOperation(insn, pop())); 628 break; 629 case Opcodes.PUTFIELD: 630 value2 = pop(); 631 value1 = pop(); 632 interpreter.binaryOperation(insn, value1, value2); 633 break; 634 case Opcodes.INVOKEVIRTUAL: 635 case Opcodes.INVOKESPECIAL: 636 case Opcodes.INVOKESTATIC: 637 case Opcodes.INVOKEINTERFACE: { 638 values = new ArrayList<V>(); 639 String desc = ((MethodInsnNode) insn).desc; 640 for (int i = Type.getArgumentTypes(desc).length; i > 0; --i) { 641 values.add(0, pop()); 642 } 643 if (insn.getOpcode() != Opcodes.INVOKESTATIC) { 644 values.add(0, pop()); 645 } 646 if (Type.getReturnType(desc) == Type.VOID_TYPE) { 647 interpreter.naryOperation(insn, values); 648 } else { 649 push(interpreter.naryOperation(insn, values)); 650 } 651 break; 652 } 653 case Opcodes.INVOKEDYNAMIC: { 654 values = new ArrayList<V>(); 655 String desc = ((InvokeDynamicInsnNode) insn).desc; 656 for (int i = Type.getArgumentTypes(desc).length; i > 0; --i) { 657 values.add(0, pop()); 658 } 659 if (Type.getReturnType(desc) == Type.VOID_TYPE) { 660 interpreter.naryOperation(insn, values); 661 } else { 662 push(interpreter.naryOperation(insn, values)); 663 } 664 break; 665 } 666 case Opcodes.NEW: 667 push(interpreter.newOperation(insn)); 668 break; 669 case Opcodes.NEWARRAY: 670 case Opcodes.ANEWARRAY: 671 case Opcodes.ARRAYLENGTH: 672 push(interpreter.unaryOperation(insn, pop())); 673 break; 674 case Opcodes.ATHROW: 675 interpreter.unaryOperation(insn, pop()); 676 break; 677 case Opcodes.CHECKCAST: 678 case Opcodes.INSTANCEOF: 679 push(interpreter.unaryOperation(insn, pop())); 680 break; 681 case Opcodes.MONITORENTER: 682 case Opcodes.MONITOREXIT: 683 interpreter.unaryOperation(insn, pop()); 684 break; 685 case Opcodes.MULTIANEWARRAY: 686 values = new ArrayList<V>(); 687 for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) { 688 values.add(0, pop()); 689 } 690 push(interpreter.naryOperation(insn, values)); 691 break; 692 case Opcodes.IFNULL: 693 case Opcodes.IFNONNULL: 694 interpreter.unaryOperation(insn, pop()); 695 break; 696 default: 697 throw new RuntimeException("Illegal opcode " + insn.getOpcode()); 698 } 699 } 700 701 /** 702 * Merges this frame with the given frame. 703 * 704 * @param frame 705 * a frame. 706 * @param interpreter 707 * the interpreter used to merge values. 708 * @return <tt>true</tt> if this frame has been changed as a result of the 709 * merge operation, or <tt>false</tt> otherwise. 710 * @throws AnalyzerException 711 * if the frames have incompatible sizes. 712 */ 713 public boolean merge(final Frame<? extends V> frame, 714 final Interpreter<V> interpreter) throws AnalyzerException { 715 if (top != frame.top) { 716 throw new AnalyzerException(null, "Incompatible stack heights"); 717 } 718 boolean changes = false; 719 for (int i = 0; i < locals + top; ++i) { 720 V v = interpreter.merge(values[i], frame.values[i]); 721 if (!v.equals(values[i])) { 722 values[i] = v; 723 changes = true; 724 } 725 } 726 return changes; 727 } 728 729 /** 730 * Merges this frame with the given frame (case of a RET instruction). 731 * 732 * @param frame 733 * a frame 734 * @param access 735 * the local variables that have been accessed by the subroutine 736 * to which the RET instruction corresponds. 737 * @return <tt>true</tt> if this frame has been changed as a result of the 738 * merge operation, or <tt>false</tt> otherwise. 739 */ 740 public boolean merge(final Frame<? extends V> frame, final boolean[] access) { 741 boolean changes = false; 742 for (int i = 0; i < locals; ++i) { 743 if (!access[i] && !values[i].equals(frame.values[i])) { 744 values[i] = frame.values[i]; 745 changes = true; 746 } 747 } 748 return changes; 749 } 750 751 /** 752 * Returns a string representation of this frame. 753 * 754 * @return a string representation of this frame. 755 */ 756 @Override 757 public String toString() { 758 StringBuilder sb = new StringBuilder(); 759 for (int i = 0; i < getLocals(); ++i) { 760 sb.append(getLocal(i)); 761 } 762 sb.append(' '); 763 for (int i = 0; i < getStackSize(); ++i) { 764 sb.append(getStack(i).toString()); 765 } 766 return sb.toString(); 767 } 768} 769