001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 *
017 */ 
018package org.apache.bcel.verifier.structurals;
019
020
021import org.apache.bcel.generic.ReferenceType;
022import org.apache.bcel.generic.Type;
023import org.apache.bcel.verifier.exc.AssertionViolatedException;
024import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
025
026/**
027 * This class implements an array of local variables used for symbolic JVM
028 * simulation.
029 *
030 * @version $Id: LocalVariables.java 1749603 2016-06-21 20:50:19Z ggregory $
031 */
032public class LocalVariables implements Cloneable {
033    /** The Type[] containing the local variable slots. */
034    private final Type[] locals;
035
036    /**
037     * Creates a new LocalVariables object.
038     */
039    public LocalVariables(final int maxLocals) {
040        locals = new Type[maxLocals];
041        for (int i=0; i<maxLocals; i++) {
042            locals[i] = Type.UNKNOWN;
043        }
044    }
045
046    /**
047     * Returns a deep copy of this object; i.e. the clone
048     * operates on a new local variable array.
049     * However, the Type objects in the array are shared.
050     */
051    @Override
052    public Object clone() {
053        final LocalVariables lvs = new LocalVariables(locals.length);
054        for (int i=0; i<locals.length; i++) {
055            lvs.locals[i] = this.locals[i];
056        }
057        return lvs;
058    }
059
060    /**
061     * Returns the type of the local variable slot i.
062     */
063    public Type get(final int i) {
064        return locals[i];
065    }
066
067    /**
068     * Returns a (correctly typed) clone of this object.
069     * This is equivalent to ((LocalVariables) this.clone()).
070     */
071    public LocalVariables getClone() {
072        return (LocalVariables) this.clone();
073    }
074
075    /**
076     * Returns the number of local variable slots this
077     * LocalVariables instance has.
078     */
079    public int maxLocals() {
080        return locals.length;
081    }
082
083    /**
084     * Sets a new Type for the given local variable slot.
085     */
086    public void set(final int i, final Type type) { // TODO could be package-protected?
087        if (type == Type.BYTE || type == Type.SHORT || type == Type.BOOLEAN || type == Type.CHAR) {
088            throw new AssertionViolatedException("LocalVariables do not know about '"+type+"'. Use Type.INT instead.");
089        }
090        locals[i] = type;
091    }
092
093    /** @return a hash code value for the object.
094     */
095    @Override
096    public int hashCode() { return locals.length; }
097
098    /*
099     * 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}