| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| package org.apache.commons.bcel6.verifier.structurals; |
| |
| |
| import org.apache.commons.bcel6.generic.ReferenceType; |
| import org.apache.commons.bcel6.generic.Type; |
| import org.apache.commons.bcel6.verifier.exc.AssertionViolatedException; |
| import org.apache.commons.bcel6.verifier.exc.StructuralCodeConstraintException; |
| |
| /** |
| * This class implements an array of local variables used for symbolic JVM |
| * simulation. |
| * |
| * @version $Id$ |
| * @author Enver Haase |
| */ |
| public class LocalVariables{ |
| /** The Type[] containing the local variable slots. */ |
| private final Type[] locals; |
| |
| /** |
| * Creates a new LocalVariables object. |
| */ |
| public LocalVariables(int maxLocals){ |
| locals = new Type[maxLocals]; |
| for (int i=0; i<maxLocals; i++){ |
| locals[i] = Type.UNKNOWN; |
| } |
| } |
| |
| /** |
| * Returns a deep copy of this object; i.e. the clone |
| * operates on a new local variable array. |
| * However, the Type objects in the array are shared. |
| */ |
| @Override |
| protected Object clone(){ |
| LocalVariables lvs = new LocalVariables(locals.length); |
| for (int i=0; i<locals.length; i++){ |
| lvs.locals[i] = this.locals[i]; |
| } |
| return lvs; |
| } |
| |
| /** |
| * Returns the type of the local variable slot i. |
| */ |
| public Type get(int i){ |
| return locals[i]; |
| } |
| |
| /** |
| * Returns a (correctly typed) clone of this object. |
| * This is equivalent to ((LocalVariables) this.clone()). |
| */ |
| public LocalVariables getClone(){ |
| return (LocalVariables) this.clone(); |
| } |
| |
| /** |
| * Returns the number of local variable slots this |
| * LocalVariables instance has. |
| */ |
| public int maxLocals(){ |
| return locals.length; |
| } |
| |
| /** |
| * Sets a new Type for the given local variable slot. |
| */ |
| public void set(int i, Type type){ |
| if (type == Type.BYTE || type == Type.SHORT || type == Type.BOOLEAN || type == Type.CHAR){ |
| throw new AssertionViolatedException("LocalVariables do not know about '"+type+"'. Use Type.INT instead."); |
| } |
| locals[i] = type; |
| } |
| |
| /** @return a hash code value for the object. |
| */ |
| @Override |
| public int hashCode() { return locals.length; } |
| |
| /* |
| * Fulfills the general contract of Object.equals(). |
| */ |
| @Override |
| public boolean equals(Object o){ |
| if (!(o instanceof LocalVariables)) { |
| return false; |
| } |
| LocalVariables lv = (LocalVariables) o; |
| if (this.locals.length != lv.locals.length) { |
| return false; |
| } |
| for (int i=0; i<this.locals.length; i++){ |
| if (!this.locals[i].equals(lv.locals[i])){ |
| //System.out.println(this.locals[i]+" is not "+lv.locals[i]); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Merges two local variables sets as described in the Java Virtual Machine Specification, |
| * Second Edition, section 4.9.2, page 146. |
| */ |
| public void merge(LocalVariables lv){ |
| |
| if (this.locals.length != lv.locals.length){ |
| throw new AssertionViolatedException("Merging LocalVariables of different size?!? From different methods or what?!?"); |
| } |
| |
| for (int i=0; i<locals.length; i++){ |
| merge(lv, i); |
| } |
| } |
| |
| /** |
| * Merges a single local variable. |
| * |
| * @see #merge(LocalVariables) |
| */ |
| private void merge(LocalVariables lv, int i){ |
| try { |
| |
| // We won't accept an unitialized object if we know it was initialized; |
| // compare vmspec2, 4.9.4, last paragraph. |
| if ( (!(locals[i] instanceof UninitializedObjectType)) && (lv.locals[i] instanceof UninitializedObjectType) ){ |
| throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object in the local variables detected."); |
| } |
| // Even harder, what about _different_ uninitialized object types?! |
| if ( (!(locals[i].equals(lv.locals[i]))) && (locals[i] instanceof UninitializedObjectType) && (lv.locals[i] instanceof UninitializedObjectType) ){ |
| throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object in the local variables detected."); |
| } |
| // If we just didn't know that it was initialized, we have now learned. |
| if (locals[i] instanceof UninitializedObjectType){ |
| if (! (lv.locals[i] instanceof UninitializedObjectType)){ |
| locals[i] = ((UninitializedObjectType) locals[i]).getInitialized(); |
| } |
| } |
| if ((locals[i] instanceof ReferenceType) && (lv.locals[i] instanceof ReferenceType)){ |
| if (! locals[i].equals(lv.locals[i])){ // needed in case of two UninitializedObjectType instances |
| Type sup = ((ReferenceType) locals[i]).getFirstCommonSuperclass((ReferenceType) (lv.locals[i])); |
| |
| if (sup != null){ |
| locals[i] = sup; |
| } |
| else{ |
| // We should have checked this in Pass2! |
| throw new AssertionViolatedException("Could not load all the super classes of '"+locals[i]+"' and '"+lv.locals[i]+"'."); |
| } |
| } |
| } |
| else{ |
| if (! (locals[i].equals(lv.locals[i])) ){ |
| /*TODO |
| if ((locals[i] instanceof org.apache.commons.bcel6.generic.ReturnaddressType) && (lv.locals[i] instanceof org.apache.commons.bcel6.generic.ReturnaddressType)){ |
| //System.err.println("merging "+locals[i]+" and "+lv.locals[i]); |
| throw new AssertionViolatedException("Merging different ReturnAddresses: '"+locals[i]+"' and '"+lv.locals[i]+"'."); |
| } |
| */ |
| locals[i] = Type.UNKNOWN; |
| } |
| } |
| } catch (ClassNotFoundException e) { |
| // FIXME: maybe not the best way to handle this |
| throw new AssertionViolatedException("Missing class: " + e, e); |
| } |
| } |
| |
| /** |
| * Returns a String representation of this object. |
| */ |
| @Override |
| public String toString(){ |
| StringBuilder sb = new StringBuilder(); |
| for (int i=0; i<locals.length; i++){ |
| sb.append(Integer.toString(i)); |
| sb.append(": "); |
| sb.append(locals[i]); |
| sb.append("\n"); |
| } |
| return sb.toString(); |
| } |
| |
| /** |
| * Replaces all occurences of u in this local variables set |
| * with an "initialized" ObjectType. |
| */ |
| public void initializeObject(UninitializedObjectType u){ |
| for (int i=0; i<locals.length; i++){ |
| if (locals[i] == u){ |
| locals[i] = u.getInitialized(); |
| } |
| } |
| } |
| } |