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