Home | History | Annotate | Download | only in structurals
      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  */
     18 package org.apache.bcel.verifier.structurals;
     19 
     20 
     21 import org.apache.bcel.generic.ReferenceType;
     22 import org.apache.bcel.generic.Type;
     23 import org.apache.bcel.verifier.exc.AssertionViolatedException;
     24 import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
     25 
     26 /**
     27  * This class implements an array of local variables used for symbolic JVM
     28  * simulation.
     29  *
     30  * @version $Id$
     31  */
     32 public class LocalVariables implements Cloneable {
     33     /** The Type[] containing the local variable slots. */
     34     private final Type[] locals;
     35 
     36     /**
     37      * Creates a new LocalVariables object.
     38      */
     39     public LocalVariables(final int maxLocals) {
     40         locals = new Type[maxLocals];
     41         for (int i=0; i<maxLocals; i++) {
     42             locals[i] = Type.UNKNOWN;
     43         }
     44     }
     45 
     46     /**
     47      * Returns a deep copy of this object; i.e. the clone
     48      * operates on a new local variable array.
     49      * However, the Type objects in the array are shared.
     50      */
     51     @Override
     52     public Object clone() {
     53         final LocalVariables lvs = new LocalVariables(locals.length);
     54         for (int i=0; i<locals.length; i++) {
     55             lvs.locals[i] = this.locals[i];
     56         }
     57         return lvs;
     58     }
     59 
     60     /**
     61      * Returns the type of the local variable slot i.
     62      */
     63     public Type get(final int i) {
     64         return locals[i];
     65     }
     66 
     67     /**
     68      * Returns a (correctly typed) clone of this object.
     69      * This is equivalent to ((LocalVariables) this.clone()).
     70      */
     71     public LocalVariables getClone() {
     72         return (LocalVariables) this.clone();
     73     }
     74 
     75     /**
     76      * Returns the number of local variable slots this
     77      * LocalVariables instance has.
     78      */
     79     public int maxLocals() {
     80         return locals.length;
     81     }
     82 
     83     /**
     84      * Sets a new Type for the given local variable slot.
     85      */
     86     public void set(final int i, final Type type) { // TODO could be package-protected?
     87         if (type == Type.BYTE || type == Type.SHORT || type == Type.BOOLEAN || type == Type.CHAR) {
     88             throw new AssertionViolatedException("LocalVariables do not know about '"+type+"'. Use Type.INT instead.");
     89         }
     90         locals[i] = type;
     91     }
     92 
     93     /** @return a hash code value for the object.
     94      */
     95     @Override
     96     public int hashCode() { return locals.length; }
     97 
     98     /*
     99      * Fulfills the general contract of Object.equals().
    100      */
    101     @Override
    102     public boolean equals(final Object o) {
    103         if (!(o instanceof LocalVariables)) {
    104             return false;
    105         }
    106         final LocalVariables lv = (LocalVariables) o;
    107         if (this.locals.length != lv.locals.length) {
    108             return false;
    109         }
    110         for (int i=0; i<this.locals.length; i++) {
    111             if (!this.locals[i].equals(lv.locals[i])) {
    112                 //System.out.println(this.locals[i]+" is not "+lv.locals[i]);
    113                 return false;
    114             }
    115         }
    116         return true;
    117     }
    118 
    119     /**
    120      * Merges two local variables sets as described in the Java Virtual Machine Specification,
    121      * Second Edition, section 4.9.2, page 146.
    122      */
    123     public void merge(final LocalVariables lv) {
    124 
    125         if (this.locals.length != lv.locals.length) {
    126             throw new AssertionViolatedException("Merging LocalVariables of different size?!? From different methods or what?!?");
    127         }
    128 
    129         for (int i=0; i<locals.length; i++) {
    130             merge(lv, i);
    131         }
    132     }
    133 
    134     /**
    135      * Merges a single local variable.
    136      *
    137      * @see #merge(LocalVariables)
    138      */
    139     private void merge(final LocalVariables lv, final int i) {
    140         try {
    141 
    142         // We won't accept an unitialized object if we know it was initialized;
    143         // compare vmspec2, 4.9.4, last paragraph.
    144         if ( (!(locals[i] instanceof UninitializedObjectType)) && (lv.locals[i] instanceof UninitializedObjectType) ) {
    145             throw new StructuralCodeConstraintException(
    146                 "Backwards branch with an uninitialized object in the local variables detected.");
    147         }
    148         // Even harder, what about _different_ uninitialized object types?!
    149         if ( (!(locals[i].equals(lv.locals[i]))) && (locals[i] instanceof UninitializedObjectType) &&
    150                 (lv.locals[i] instanceof UninitializedObjectType) ) {
    151             throw new StructuralCodeConstraintException(
    152                 "Backwards branch with an uninitialized object in the local variables detected.");
    153         }
    154         // If we just didn't know that it was initialized, we have now learned.
    155         if (locals[i] instanceof UninitializedObjectType) {
    156             if (! (lv.locals[i] instanceof UninitializedObjectType)) {
    157                 locals[i] = ((UninitializedObjectType) locals[i]).getInitialized();
    158             }
    159         }
    160         if ((locals[i] instanceof ReferenceType) && (lv.locals[i] instanceof ReferenceType)) {
    161             if (! locals[i].equals(lv.locals[i])) { // needed in case of two UninitializedObjectType instances
    162                 final Type sup = ((ReferenceType) locals[i]).getFirstCommonSuperclass((ReferenceType) (lv.locals[i]));
    163 
    164                 if (sup != null) {
    165                     locals[i] = sup;
    166                 }
    167                 else{
    168                     // We should have checked this in Pass2!
    169                     throw new AssertionViolatedException(
    170                         "Could not load all the super classes of '"+locals[i]+"' and '"+lv.locals[i]+"'.");
    171                 }
    172             }
    173         }
    174         else{
    175             if (! (locals[i].equals(lv.locals[i])) ) {
    176 /*TODO
    177                 if ((locals[i] instanceof org.apache.bcel.generic.ReturnaddressType) &&
    178                     (lv.locals[i] instanceof org.apache.bcel.generic.ReturnaddressType)) {
    179                     //System.err.println("merging "+locals[i]+" and "+lv.locals[i]);
    180                     throw new AssertionViolatedException("Merging different ReturnAddresses: '"+locals[i]+"' and '"+lv.locals[i]+"'.");
    181                 }
    182 */
    183                 locals[i] = Type.UNKNOWN;
    184             }
    185         }
    186         } catch (final ClassNotFoundException e) {
    187         // FIXME: maybe not the best way to handle this
    188         throw new AssertionViolatedException("Missing class: " + e, e);
    189         }
    190     }
    191 
    192     /**
    193      * Returns a String representation of this object.
    194      */
    195     @Override
    196     public String toString() {
    197         final StringBuilder sb = new StringBuilder();
    198         for (int i=0; i<locals.length; i++) {
    199             sb.append(Integer.toString(i));
    200             sb.append(": ");
    201             sb.append(locals[i]);
    202             sb.append("\n");
    203         }
    204         return sb.toString();
    205     }
    206 
    207     /**
    208      * Replaces all occurences of u in this local variables set
    209      * with an "initialized" ObjectType.
    210      */
    211     public void initializeObject(final UninitializedObjectType u) {
    212         for (int i=0; i<locals.length; i++) {
    213             if (locals[i] == u) {
    214                 locals[i] = u.getInitialized();
    215             }
    216         }
    217     }
    218 }
    219