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