LocalVariables.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.verifier.structurals;

  18. import org.apache.bcel.generic.ReferenceType;
  19. import org.apache.bcel.generic.Type;
  20. import org.apache.bcel.verifier.exc.AssertionViolatedException;
  21. import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
  22. import org.apache.commons.lang3.ArrayFill;

  23. /**
  24.  * This class implements an array of local variables used for symbolic JVM simulation.
  25.  */
  26. public class LocalVariables implements Cloneable {

  27.     /** The Type[] containing the local variable slots. */
  28.     private final Type[] locals;

  29.     /**
  30.      * Creates a new LocalVariables object.
  31.      *
  32.      * @param localVariableCount local variable count.
  33.      */
  34.     public LocalVariables(final int localVariableCount) {
  35.         locals = ArrayFill.fill(new Type[localVariableCount], Type.UNKNOWN);
  36.     }

  37.     /**
  38.      * Returns a deep copy of this object; i.e. the clone operates on a new local variable array. However, the Type objects
  39.      * in the array are shared.
  40.      */
  41.     @Override
  42.     public Object clone() {
  43.         final LocalVariables lvs = new LocalVariables(locals.length);
  44.         System.arraycopy(this.locals, 0, lvs.locals, 0, locals.length);
  45.         return lvs;
  46.     }

  47.     /*
  48.      * Fulfills the general contract of Object.equals().
  49.      */
  50.     @Override
  51.     public boolean equals(final Object o) {
  52.         if (!(o instanceof LocalVariables)) {
  53.             return false;
  54.         }
  55.         final LocalVariables lv = (LocalVariables) o;
  56.         if (this.locals.length != lv.locals.length) {
  57.             return false;
  58.         }
  59.         for (int i = 0; i < this.locals.length; i++) {
  60.             if (!this.locals[i].equals(lv.locals[i])) {
  61.                 // System.out.println(this.locals[i]+" is not "+lv.locals[i]);
  62.                 return false;
  63.             }
  64.         }
  65.         return true;
  66.     }

  67.     /**
  68.      * Returns the type of the local variable slot index.
  69.      *
  70.      * @param slotIndex Slot to look up.
  71.      * @return the type of the local variable slot index.
  72.      */
  73.     public Type get(final int slotIndex) {
  74.         return locals[slotIndex];
  75.     }

  76.     /**
  77.      * Returns a (correctly typed) clone of this object. This is equivalent to ((LocalVariables) this.clone()).
  78.      *
  79.      * @return a (correctly typed) clone of this object.
  80.      */
  81.     public LocalVariables getClone() {
  82.         return (LocalVariables) clone();
  83.     }

  84.     /**
  85.      * @return a hash code value for the object.
  86.      */
  87.     @Override
  88.     public int hashCode() {
  89.         return locals.length;
  90.     }

  91.     /**
  92.      * Replaces all occurrences of {@code uninitializedObjectType} in this local variables set with an "initialized"
  93.      * ObjectType.
  94.      *
  95.      * @param uninitializedObjectType the object to match.
  96.      */
  97.     public void initializeObject(final UninitializedObjectType uninitializedObjectType) {
  98.         for (int i = 0; i < locals.length; i++) {
  99.             if (locals[i] == uninitializedObjectType) {
  100.                 locals[i] = uninitializedObjectType.getInitialized();
  101.             }
  102.         }
  103.     }

  104.     /**
  105.      * Returns the number of local variable slots.
  106.      *
  107.      * @return the number of local variable slots.
  108.      */
  109.     public int maxLocals() {
  110.         return locals.length;
  111.     }

  112.     /**
  113.      * Merges two local variables sets as described in the Java Virtual Machine Specification, Second Edition, section
  114.      * 4.9.2, page 146.
  115.      *
  116.      * @param localVariable other local variable.
  117.      */
  118.     public void merge(final LocalVariables localVariable) {

  119.         if (this.locals.length != localVariable.locals.length) {
  120.             throw new AssertionViolatedException("Merging LocalVariables of different size?!? From different methods or what?!?");
  121.         }

  122.         for (int i = 0; i < locals.length; i++) {
  123.             merge(localVariable, i);
  124.         }
  125.     }

  126.     /**
  127.      * Merges a single local variable.
  128.      *
  129.      * @see #merge(LocalVariables)
  130.      */
  131.     private void merge(final LocalVariables lv, final int i) {
  132.         try {

  133.             // We won't accept an unitialized object if we know it was initialized;
  134.             // compare vmspec2, 4.9.4, last paragraph.
  135.             if (!(locals[i] instanceof UninitializedObjectType) && lv.locals[i] instanceof UninitializedObjectType) {
  136.                 throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object in the local variables detected.");
  137.             }
  138.             // Even harder, what about _different_ uninitialized object types?!
  139.             if (!locals[i].equals(lv.locals[i]) && locals[i] instanceof UninitializedObjectType && lv.locals[i] instanceof UninitializedObjectType) {
  140.                 throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object in the local variables detected.");
  141.             }
  142.             // If we just didn't know that it was initialized, we have now learned.
  143.             if (locals[i] instanceof UninitializedObjectType && !(lv.locals[i] instanceof UninitializedObjectType)) {
  144.                 locals[i] = ((UninitializedObjectType) locals[i]).getInitialized();
  145.             }
  146.             if (locals[i] instanceof ReferenceType && lv.locals[i] instanceof ReferenceType) {
  147.                 if (!locals[i].equals(lv.locals[i])) { // needed in case of two UninitializedObjectType instances
  148.                     final Type sup = ((ReferenceType) locals[i]).getFirstCommonSuperclass((ReferenceType) lv.locals[i]);

  149.                     if (sup == null) {
  150.                         // We should have checked this in Pass2!
  151.                         throw new AssertionViolatedException("Could not load all the super classes of '" + locals[i] + "' and '" + lv.locals[i] + "'.");
  152.                     }
  153.                     locals[i] = sup;
  154.                 }
  155.             } else if (!locals[i].equals(lv.locals[i])) {
  156.                 /*
  157.                  * TODO if ((locals[i] instanceof org.apache.bcel.generic.ReturnaddressType) && (lv.locals[i] instanceof
  158.                  * org.apache.bcel.generic.ReturnaddressType)) { //System.err.println("merging "+locals[i]+" and "+lv.locals[i]); throw
  159.                  * new AssertionViolatedException("Merging different ReturnAddresses: '"+locals[i]+"' and '"+lv.locals[i]+"'."); }
  160.                  */
  161.                 locals[i] = Type.UNKNOWN;
  162.             }
  163.         } catch (final ClassNotFoundException e) {
  164.             // FIXME: maybe not the best way to handle this
  165.             throw new AssertionViolatedException("Missing class: " + e, e);
  166.         }
  167.     }

  168.     /**
  169.      * Sets a new Type for the given local variable slot.
  170.      *
  171.      * @param slotIndex Target slot index.
  172.      * @param type Type to save at the given slot index.
  173.      */
  174.     public void set(final int slotIndex, final Type type) { // TODO could be package-protected?
  175.         if (type == Type.BYTE || type == Type.SHORT || type == Type.BOOLEAN || type == Type.CHAR) {
  176.             throw new AssertionViolatedException("LocalVariables do not know about '" + type + "'. Use Type.INT instead.");
  177.         }
  178.         locals[slotIndex] = type;
  179.     }

  180.     /**
  181.      * Returns a String representation of this object.
  182.      */
  183.     @Override
  184.     public String toString() {
  185.         final StringBuilder sb = new StringBuilder();
  186.         for (int i = 0; i < locals.length; i++) {
  187.             sb.append(Integer.toString(i));
  188.             sb.append(": ");
  189.             sb.append(locals[i]);
  190.             sb.append("\n");
  191.         }
  192.         return sb.toString();
  193.     }
  194. }