LocalVariables.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.bcel.verifier.structurals;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;
import org.apache.bcel.verifier.exc.AssertionViolatedException;
import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
import org.apache.commons.lang3.ArrayFill;
/**
* This class implements an array of local variables used for symbolic JVM simulation.
*/
public class LocalVariables implements Cloneable {
/** The Type[] containing the local variable slots. */
private final Type[] locals;
/**
* Creates a new LocalVariables object.
*
* @param localVariableCount local variable count.
*/
public LocalVariables(final int localVariableCount) {
locals = ArrayFill.fill(new Type[localVariableCount], Type.UNKNOWN);
}
/**
* Returns a deep copy of this object; i.e. the clone operates on a new local variable array. However, the Type objects
* in the array are shared.
*/
@Override
public Object clone() {
final LocalVariables lvs = new LocalVariables(locals.length);
System.arraycopy(this.locals, 0, lvs.locals, 0, locals.length);
return lvs;
}
/*
* Fulfills the general contract of Object.equals().
*/
@Override
public boolean equals(final Object o) {
if (!(o instanceof LocalVariables)) {
return false;
}
final LocalVariables lv = (LocalVariables) o;
if (this.locals.length != lv.locals.length) {
return false;
}
for (int i = 0; i < this.locals.length; i++) {
if (!this.locals[i].equals(lv.locals[i])) {
// System.out.println(this.locals[i]+" is not "+lv.locals[i]);
return false;
}
}
return true;
}
/**
* Returns the type of the local variable slot index.
*
* @param slotIndex Slot to look up.
* @return the type of the local variable slot index.
*/
public Type get(final int slotIndex) {
return locals[slotIndex];
}
/**
* Returns a (correctly typed) clone of this object. This is equivalent to ((LocalVariables) this.clone()).
*
* @return a (correctly typed) clone of this object.
*/
public LocalVariables getClone() {
return (LocalVariables) clone();
}
/**
* @return a hash code value for the object.
*/
@Override
public int hashCode() {
return locals.length;
}
/**
* Replaces all occurrences of {@code uninitializedObjectType} in this local variables set with an "initialized"
* ObjectType.
*
* @param uninitializedObjectType the object to match.
*/
public void initializeObject(final UninitializedObjectType uninitializedObjectType) {
for (int i = 0; i < locals.length; i++) {
if (locals[i] == uninitializedObjectType) {
locals[i] = uninitializedObjectType.getInitialized();
}
}
}
/**
* Returns the number of local variable slots.
*
* @return the number of local variable slots.
*/
public int maxLocals() {
return locals.length;
}
/**
* Merges two local variables sets as described in the Java Virtual Machine Specification, Second Edition, section
* 4.9.2, page 146.
*
* @param localVariable other local variable.
*/
public void merge(final LocalVariables localVariable) {
if (this.locals.length != localVariable.locals.length) {
throw new AssertionViolatedException("Merging LocalVariables of different size?!? From different methods or what?!?");
}
for (int i = 0; i < locals.length; i++) {
merge(localVariable, i);
}
}
/**
* Merges a single local variable.
*
* @see #merge(LocalVariables)
*/
private void merge(final LocalVariables lv, final int i) {
try {
// We won't accept an unitialized object if we know it was initialized;
// compare vmspec2, 4.9.4, last paragraph.
if (!(locals[i] instanceof UninitializedObjectType) && lv.locals[i] instanceof UninitializedObjectType) {
throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object in the local variables detected.");
}
// Even harder, what about _different_ uninitialized object types?!
if (!locals[i].equals(lv.locals[i]) && locals[i] instanceof UninitializedObjectType && lv.locals[i] instanceof UninitializedObjectType) {
throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object in the local variables detected.");
}
// If we just didn't know that it was initialized, we have now learned.
if (locals[i] instanceof UninitializedObjectType && !(lv.locals[i] instanceof UninitializedObjectType)) {
locals[i] = ((UninitializedObjectType) locals[i]).getInitialized();
}
if (locals[i] instanceof ReferenceType && lv.locals[i] instanceof ReferenceType) {
if (!locals[i].equals(lv.locals[i])) { // needed in case of two UninitializedObjectType instances
final Type sup = ((ReferenceType) locals[i]).getFirstCommonSuperclass((ReferenceType) lv.locals[i]);
if (sup == null) {
// We should have checked this in Pass2!
throw new AssertionViolatedException("Could not load all the super classes of '" + locals[i] + "' and '" + lv.locals[i] + "'.");
}
locals[i] = sup;
}
} else if (!locals[i].equals(lv.locals[i])) {
/*
* TODO if ((locals[i] instanceof org.apache.bcel.generic.ReturnaddressType) && (lv.locals[i] instanceof
* org.apache.bcel.generic.ReturnaddressType)) { //System.err.println("merging "+locals[i]+" and "+lv.locals[i]); throw
* new AssertionViolatedException("Merging different ReturnAddresses: '"+locals[i]+"' and '"+lv.locals[i]+"'."); }
*/
locals[i] = Type.UNKNOWN;
}
} catch (final ClassNotFoundException e) {
// FIXME: maybe not the best way to handle this
throw new AssertionViolatedException("Missing class: " + e, e);
}
}
/**
* Sets a new Type for the given local variable slot.
*
* @param slotIndex Target slot index.
* @param type Type to save at the given slot index.
*/
public void set(final int slotIndex, final Type type) { // TODO could be package-protected?
if (type == Type.BYTE || type == Type.SHORT || type == Type.BOOLEAN || type == Type.CHAR) {
throw new AssertionViolatedException("LocalVariables do not know about '" + type + "'. Use Type.INT instead.");
}
locals[slotIndex] = type;
}
/**
* Returns a String representation of this object.
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < locals.length; i++) {
sb.append(Integer.toString(i));
sb.append(": ");
sb.append(locals[i]);
sb.append("\n");
}
return sb.toString();
}
}