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