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 */ 030public class LocalVariables implements Cloneable { 031 032 /** The Type[] containing the local variable slots. */ 033 private final Type[] locals; 034 035 /** 036 * Creates a new LocalVariables object. 037 * 038 * @param localVariableCount local variable count. 039 */ 040 public LocalVariables(final int localVariableCount) { 041 locals = new Type[localVariableCount]; 042 for (int i=0; i<localVariableCount; i++) { 043 locals[i] = Type.UNKNOWN; 044 } 045 } 046 047 /** 048 * Returns a deep copy of this object; i.e. the clone 049 * operates on a new local variable array. 050 * However, the Type objects in the array are shared. 051 */ 052 @Override 053 public Object clone() { 054 final LocalVariables lvs = new LocalVariables(locals.length); 055 for (int i=0; i<locals.length; i++) { 056 lvs.locals[i] = this.locals[i]; 057 } 058 return lvs; 059 } 060 061 /** 062 * Returns the type of the local variable slot index. 063 * 064 * @param slotIndex Slot to look up. 065 * @return the type of the local variable slot index. 066 */ 067 public Type get(final int slotIndex) { 068 return locals[slotIndex]; 069 } 070 071 /** 072 * Returns a (correctly typed) clone of this object. 073 * This is equivalent to ((LocalVariables) this.clone()). 074 * 075 * @return a (correctly typed) clone of this object. 076 */ 077 public LocalVariables getClone() { 078 return (LocalVariables) this.clone(); 079 } 080 081 /** 082 * Returns the number of local variable slots. 083 * 084 * @return the number of local variable slots. 085 */ 086 public int maxLocals() { 087 return locals.length; 088 } 089 090 /** 091 * Sets a new Type for the given local variable slot. 092 * 093 * @param slotIndex Target slot index. 094 * @param type Type to save at the given slot index. 095 */ 096 public void set(final int slotIndex, final Type type) { // TODO could be package-protected? 097 if (type == Type.BYTE || type == Type.SHORT || type == Type.BOOLEAN || type == Type.CHAR) { 098 throw new AssertionViolatedException("LocalVariables do not know about '"+type+"'. Use Type.INT instead."); 099 } 100 locals[slotIndex] = type; 101 } 102 103 /** @return a hash code value for the object. 104 */ 105 @Override 106 public int hashCode() { return locals.length; } 107 108 /* 109 * Fulfills the general contract of Object.equals(). 110 */ 111 @Override 112 public boolean equals(final Object o) { 113 if (!(o instanceof LocalVariables)) { 114 return false; 115 } 116 final LocalVariables lv = (LocalVariables) o; 117 if (this.locals.length != lv.locals.length) { 118 return false; 119 } 120 for (int i=0; i<this.locals.length; i++) { 121 if (!this.locals[i].equals(lv.locals[i])) { 122 //System.out.println(this.locals[i]+" is not "+lv.locals[i]); 123 return false; 124 } 125 } 126 return true; 127 } 128 129 /** 130 * Merges two local variables sets as described in the Java Virtual Machine Specification, 131 * Second Edition, section 4.9.2, page 146. 132 * 133 * @param localVariable other local variable. 134 */ 135 public void merge(final LocalVariables localVariable) { 136 137 if (this.locals.length != localVariable.locals.length) { 138 throw new AssertionViolatedException("Merging LocalVariables of different size?!? From different methods or what?!?"); 139 } 140 141 for (int i=0; i<locals.length; i++) { 142 merge(localVariable, i); 143 } 144 } 145 146 /** 147 * Merges a single local variable. 148 * 149 * @see #merge(LocalVariables) 150 */ 151 private void merge(final LocalVariables lv, final int i) { 152 try { 153 154 // We won't accept an unitialized object if we know it was initialized; 155 // compare vmspec2, 4.9.4, last paragraph. 156 if ( (!(locals[i] instanceof UninitializedObjectType)) && (lv.locals[i] instanceof UninitializedObjectType) ) { 157 throw new StructuralCodeConstraintException( 158 "Backwards branch with an uninitialized object in the local variables detected."); 159 } 160 // Even harder, what about _different_ uninitialized object types?! 161 if ( (!(locals[i].equals(lv.locals[i]))) && (locals[i] instanceof UninitializedObjectType) && 162 (lv.locals[i] instanceof UninitializedObjectType) ) { 163 throw new StructuralCodeConstraintException( 164 "Backwards branch with an uninitialized object in the local variables detected."); 165 } 166 // If we just didn't know that it was initialized, we have now learned. 167 if (locals[i] instanceof UninitializedObjectType) { 168 if (! (lv.locals[i] instanceof UninitializedObjectType)) { 169 locals[i] = ((UninitializedObjectType) locals[i]).getInitialized(); 170 } 171 } 172 if ((locals[i] instanceof ReferenceType) && (lv.locals[i] instanceof ReferenceType)) { 173 if (! locals[i].equals(lv.locals[i])) { // needed in case of two UninitializedObjectType instances 174 final Type sup = ((ReferenceType) locals[i]).getFirstCommonSuperclass((ReferenceType) (lv.locals[i])); 175 176 if (sup != null) { 177 locals[i] = sup; 178 } 179 else{ 180 // We should have checked this in Pass2! 181 throw new AssertionViolatedException( 182 "Could not load all the super classes of '"+locals[i]+"' and '"+lv.locals[i]+"'."); 183 } 184 } 185 } 186 else{ 187 if (! (locals[i].equals(lv.locals[i])) ) { 188/*TODO 189 if ((locals[i] instanceof org.apache.bcel.generic.ReturnaddressType) && 190 (lv.locals[i] instanceof org.apache.bcel.generic.ReturnaddressType)) { 191 //System.err.println("merging "+locals[i]+" and "+lv.locals[i]); 192 throw new AssertionViolatedException("Merging different ReturnAddresses: '"+locals[i]+"' and '"+lv.locals[i]+"'."); 193 } 194*/ 195 locals[i] = Type.UNKNOWN; 196 } 197 } 198 } catch (final ClassNotFoundException e) { 199 // FIXME: maybe not the best way to handle this 200 throw new AssertionViolatedException("Missing class: " + e, e); 201 } 202 } 203 204 /** 205 * Returns a String representation of this object. 206 */ 207 @Override 208 public String toString() { 209 final StringBuilder sb = new StringBuilder(); 210 for (int i=0; i<locals.length; i++) { 211 sb.append(Integer.toString(i)); 212 sb.append(": "); 213 sb.append(locals[i]); 214 sb.append("\n"); 215 } 216 return sb.toString(); 217 } 218 219 /** 220 * Replaces all occurrences of {@code uninitializedObjectType} in this local variables set 221 * with an "initialized" ObjectType. 222 * 223 * @param uninitializedObjectType the object to match. 224 */ 225 public void initializeObject(final UninitializedObjectType uninitializedObjectType) { 226 for (int i=0; i<locals.length; i++) { 227 if (locals[i] == uninitializedObjectType) { 228 locals[i] = uninitializedObjectType.getInitialized(); 229 } 230 } 231 } 232}