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