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();
- }
- }