summaryrefslogtreecommitdiff
path: root/src/proguard/evaluation/value/ReferenceValue.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/evaluation/value/ReferenceValue.java')
-rw-r--r--src/proguard/evaluation/value/ReferenceValue.java553
1 files changed, 159 insertions, 394 deletions
diff --git a/src/proguard/evaluation/value/ReferenceValue.java b/src/proguard/evaluation/value/ReferenceValue.java
index 4a52e82..1f87382 100644
--- a/src/proguard/evaluation/value/ReferenceValue.java
+++ b/src/proguard/evaluation/value/ReferenceValue.java
@@ -2,7 +2,7 @@
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
- * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu)
+ * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
@@ -20,191 +20,110 @@
*/
package proguard.evaluation.value;
-import proguard.classfile.*;
-import proguard.classfile.util.ClassUtil;
-import proguard.classfile.visitor.ClassCollector;
-
-import java.util.*;
+import proguard.classfile.Clazz;
/**
- * This class represents a partially evaluated reference value. It has a type
- * and a flag that indicates whether the value could be <code>null</code>. If
- * the type is <code>null</code>, the value is <code>null</code>.
+ * This class represents a partially evaluated reference value.
*
* @author Eric Lafortune
*/
-public class ReferenceValue extends Category1Value
+public abstract class ReferenceValue extends Category1Value
{
- private static final boolean DEBUG = false;
+ /**
+ * Returns the type.
+ */
+ public abstract String getType();
+;
+ /**
+ * Returns the class that is referenced by the type.
+ */
+ public abstract Clazz getReferencedClass();
- protected final String type;
- protected final Clazz referencedClass;
- protected final boolean mayBeNull;
+ // Basic unary methods.
/**
- * Creates a new ReferenceValue.
+ * Returns whether the type is <code>null</code>.
*/
- public ReferenceValue(String type,
- Clazz referencedClass,
- boolean mayBeNull)
- {
- this.type = type;
- this.referencedClass = referencedClass;
- this.mayBeNull = mayBeNull;
- }
+ public abstract int isNull();
/**
- * Returns the type.
+ * Returns whether the type is an instance of the given type.
+ */
+ public abstract int instanceOf(String otherType, Clazz otherReferencedClass);
+
+
+ /**
+ * Returns a generalization of this ReferenceValue that may be null,
+ * depending on the flag.
+ */
+ public abstract ReferenceValue generalizeMayBeNull(boolean mayBeNull);
+
+
+ /**
+ * Returns the length of the array, assuming this type is an array.
*/
- public String getType()
+ public IntegerValue arrayLength(ValueFactory valueFactory)
{
- return type;
+ return valueFactory.createIntegerValue();
}
/**
- * Returns the class that is referenced by the type.
+ * Returns the value of the array at the given index, assuming this type
+ * is an integer array.
*/
- public Clazz getReferencedClass()
+ public IntegerValue integerArrayLoad(IntegerValue indexValue, ValueFactory valueFactory)
{
- return referencedClass;
+ return valueFactory.createIntegerValue();
}
- // Basic unary methods.
-
/**
- * Returns whether the type is <code>null</code>.
+ * Returns the value of the array at the given index, assuming this type
+ * is an long array.
*/
- public int isNull()
+ public LongValue longArrayLoad(IntegerValue indexValue, ValueFactory valueFactory)
{
- return type == null ? ALWAYS :
- mayBeNull ? MAYBE :
- NEVER;
+ return valueFactory.createLongValue();
}
/**
- * Returns whether the type is an instance of the given type.
+ * Returns the value of the array at the given index, assuming this type
+ * is an float array.
*/
- public int instanceOf(String otherType, Clazz otherReferencedClass)
+ public FloatValue floatArrayLoad(IntegerValue indexValue, ValueFactory valueFactory)
{
- String thisType = this.type;
-
- // If this type is null, it is never an instance of any class.
- if (thisType == null)
- {
- return NEVER;
- }
-
- // Start taking into account the type dimensions.
- int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType);
- int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType);
- int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount);
-
- // Strip any common array prefixes.
- thisType = thisType.substring(commonDimensionCount);
- otherType = otherType.substring(commonDimensionCount);
-
- // If either stripped type is a primitive type, we can tell right away.
- if (commonDimensionCount > 0 &&
- (ClassUtil.isInternalPrimitiveType(thisType.charAt(0)) ||
- ClassUtil.isInternalPrimitiveType(otherType.charAt(0))))
- {
- return !thisType.equals(otherType) ? NEVER :
- mayBeNull ? MAYBE :
- ALWAYS;
- }
-
- // Strip the class type prefix and suffix of this type, if any.
- if (thisDimensionCount == commonDimensionCount)
- {
- thisType = ClassUtil.internalClassNameFromClassType(thisType);
- }
-
- // Strip the class type prefix and suffix of the other type, if any.
- if (otherDimensionCount == commonDimensionCount)
- {
- otherType = ClassUtil.internalClassNameFromClassType(otherType);
- }
-
- // If this type is an array type, and the other type is not
- // java.lang.Object, java.lang.Cloneable, or java.io.Serializable,
- // this type can never be an instance.
- if (thisDimensionCount > otherDimensionCount &&
- !ClassUtil.isInternalArrayInterfaceName(otherType))
- {
- return NEVER;
- }
-
- // If the other type is an array type, and this type is not
- // java.lang.Object, java.lang.Cloneable, or java.io.Serializable,
- // this type can never be an instance.
- if (thisDimensionCount < otherDimensionCount &&
- !ClassUtil.isInternalArrayInterfaceName(thisType))
- {
- return NEVER;
- }
-
- // If this type may be null, it might not be an instance of any class.
- if (mayBeNull)
- {
- return MAYBE;
- }
-
- // If this type is equal to the other type, or if the other type is
- // java.lang.Object, this type is always an instance.
- if (thisType.equals(otherType) ||
- ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT.equals(otherType))
- {
- return ALWAYS;
- }
-
- // If this type is an array type, it's ok.
- if (thisDimensionCount > otherDimensionCount)
- {
- return ALWAYS;
- }
-
- // If the other type is an array type, it might be ok.
- if (thisDimensionCount < otherDimensionCount)
- {
- return MAYBE;
- }
-
- // If the value extends the type, we're sure.
- return referencedClass != null &&
- otherReferencedClass != null &&
- referencedClass.extendsOrImplements(otherReferencedClass) ?
- ALWAYS :
- MAYBE;
+ return valueFactory.createFloatValue();
}
/**
- * Returns the length of the array, assuming this type is an array.
+ * Returns the value of the array at the given index, assuming this type
+ * is an double array.
*/
- public IntegerValue arrayLength(ValueFactory valueFactory)
+ public DoubleValue doubleArrayLoad(IntegerValue indexValue, ValueFactory valueFactory)
{
- return valueFactory.createIntegerValue();
+ return valueFactory.createDoubleValue();
}
/**
* Returns the value of the array at the given index, assuming this type
- * is an array.
+ * is a reference array.
+ */
+ public abstract ReferenceValue referenceArrayLoad(IntegerValue indexValue, ValueFactory valueFactory);
+
+
+ /**
+ * Stores the given value at the given index in the given array, assuming
+ * this type is an array.
*/
- public Value arrayLoad(IntegerValue integerValue, ValueFactory valueFactory)
+ public void arrayStore(IntegerValue indexValue, Value value)
{
- return
- type == null ? ValueFactory.REFERENCE_VALUE_NULL :
- !ClassUtil.isInternalArrayType(type) ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL :
- valueFactory.createValue(type.substring(1),
- referencedClass,
- true);
}
@@ -214,327 +133,173 @@ public class ReferenceValue extends Category1Value
* Returns the generalization of this ReferenceValue and the given other
* ReferenceValue.
*/
- public ReferenceValue generalize(ReferenceValue other)
+ public abstract ReferenceValue generalize(ReferenceValue other);
+
+
+ /**
+ * Returns whether this ReferenceValue is equal to the given other
+ * ReferenceValue.
+ * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+ */
+ public abstract int equal(ReferenceValue other);
+
+
+ // Derived unary methods.
+
+ /**
+ * Returns whether this ReferenceValue is not <code>null</code>.
+ * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+ */
+ public final int isNotNull()
{
- // If both types are identical, the generalization is the same too.
- if (this.equals(other))
- {
- return this;
- }
-
- String thisType = this.type;
- String otherType = other.type;
-
- // If both types are nul, the generalization is null too.
- if (thisType == null && otherType == null)
- {
- return ValueFactory.REFERENCE_VALUE_NULL;
- }
-
- // If this type is null, the generalization is the other type, maybe null.
- if (thisType == null)
- {
- return other.generalizeMayBeNull(true);
- }
-
- // If the other type is null, the generalization is this type, maybe null.
- if (otherType == null)
- {
- return this.generalizeMayBeNull(true);
- }
-
- boolean mayBeNull = this.mayBeNull || other.mayBeNull;
-
- // If the two types are equal, the generalization remains the same, maybe null.
- if (thisType.equals(otherType))
- {
- return this.generalizeMayBeNull(mayBeNull);
- }
-
- // Start taking into account the type dimensions.
- int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType);
- int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType);
- int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount);
-
- if (thisDimensionCount == otherDimensionCount)
- {
- // See if we can take into account the referenced classes.
- Clazz thisReferencedClass = this.referencedClass;
- Clazz otherReferencedClass = other.referencedClass;
-
- if (thisReferencedClass != null &&
- otherReferencedClass != null)
- {
- if (thisReferencedClass.extendsOrImplements(otherReferencedClass))
- {
- return other.generalizeMayBeNull(mayBeNull);
- }
-
- if (otherReferencedClass.extendsOrImplements(thisReferencedClass))
- {
- return this.generalizeMayBeNull(mayBeNull);
- }
-
- // Collect the superclasses and interfaces of this class.
- Set thisSuperClasses = new HashSet();
- thisReferencedClass.hierarchyAccept(false, true, true, false,
- new ClassCollector(thisSuperClasses));
-
- int thisSuperClassesCount = thisSuperClasses.size();
- if (thisSuperClassesCount == 0 &&
- thisReferencedClass.getSuperName() != null)
- {
- throw new IllegalArgumentException("Can't find any super classes of ["+thisType+"] (not even immediate super class ["+thisReferencedClass.getSuperName()+"])");
- }
-
- // Collect the superclasses and interfaces of the other class.
- Set otherSuperClasses = new HashSet();
- otherReferencedClass.hierarchyAccept(false, true, true, false,
- new ClassCollector(otherSuperClasses));
-
- int otherSuperClassesCount = otherSuperClasses.size();
- if (otherSuperClassesCount == 0 &&
- otherReferencedClass.getSuperName() != null)
- {
- throw new IllegalArgumentException("Can't find any super classes of ["+otherType+"] (not even immediate super class ["+otherReferencedClass.getSuperName()+"])");
- }
-
- if (DEBUG)
- {
- System.out.println("ReferenceValue.generalize this ["+thisReferencedClass.getName()+"] with other ["+otherReferencedClass.getName()+"]");
- System.out.println(" This super classes: "+thisSuperClasses);
- System.out.println(" Other super classes: "+otherSuperClasses);
- }
-
- // Find the common superclasses.
- thisSuperClasses.retainAll(otherSuperClasses);
-
- if (DEBUG)
- {
- System.out.println(" Common super classes: "+thisSuperClasses);
- }
-
- // Find a class that is a subclass of all common superclasses,
- // or that at least has the maximum number of common superclasses.
- Clazz commonClass = null;
-
- int maximumSuperClassCount = -1;
-
- // Go over all common superclasses to find it. In case of
- // multiple subclasses, keep the lowest one alphabetically,
- // in order to ensure that the choice is deterministic.
- Iterator commonSuperClasses = thisSuperClasses.iterator();
- while (commonSuperClasses.hasNext())
- {
- Clazz commonSuperClass = (Clazz)commonSuperClasses.next();
-
- int superClassCount = superClassCount(commonSuperClass, thisSuperClasses);
- if (maximumSuperClassCount < superClassCount ||
- (maximumSuperClassCount == superClassCount &&
- commonClass != null &&
- commonClass.getName().compareTo(commonSuperClass.getName()) > 0))
- {
- commonClass = commonSuperClass;
- maximumSuperClassCount = superClassCount;
- }
- }
-
- if (commonClass == null)
- {
- throw new IllegalArgumentException("Can't find common super class of ["+
- thisType +"] (with "+thisSuperClassesCount +" known super classes) and ["+
- otherType+"] (with "+otherSuperClassesCount+" known super classes)");
- }
-
- if (DEBUG)
- {
- System.out.println(" Best common class: ["+commonClass.getName()+"]");
- }
-
- // TODO: Handle more difficult cases, with multiple global subclasses.
-
- return new ReferenceValue(commonDimensionCount == 0 ?
- commonClass.getName() :
- ClassUtil.internalArrayTypeFromClassName(commonClass.getName(),
- commonDimensionCount),
- commonClass,
- mayBeNull);
- }
- }
- else if (thisDimensionCount > otherDimensionCount)
- {
- // See if the other type is an interface type of arrays.
- if (ClassUtil.isInternalArrayInterfaceName(ClassUtil.internalClassNameFromClassType(otherType)))
- {
- return other.generalizeMayBeNull(mayBeNull);
- }
- }
- else if (thisDimensionCount < otherDimensionCount)
- {
- // See if this type is an interface type of arrays.
- if (ClassUtil.isInternalArrayInterfaceName(ClassUtil.internalClassNameFromClassType(thisType)))
- {
- return this.generalizeMayBeNull(mayBeNull);
- }
- }
-
- // Reduce the common dimension count if either type is an array of
- // primitives type of this dimension.
- if (commonDimensionCount > 0 &&
- (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount))) ||
- ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount)))
- {
- commonDimensionCount--;
- }
-
- // Fall back on a basic Object or array of Objects type.
- return commonDimensionCount == 0 ?
- mayBeNull ?
- ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL :
- ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL :
- new ReferenceValue(ClassUtil.internalArrayTypeFromClassName(ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT,
- commonDimensionCount),
- null,
- mayBeNull);
+ return -isNull();
}
+ // Derived binary methods.
+
/**
- * Returns if the number of superclasses of the given class in the given
- * set of classes.
+ * Returns whether this ReferenceValue and the given ReferenceValue are different.
+ * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
*/
- private int superClassCount(Clazz subClass, Set classes)
+ public final int notEqual(ReferenceValue other)
{
- int count = 0;
+ return -equal(other);
+ }
- Iterator iterator = classes.iterator();
- while (iterator.hasNext())
- {
- Clazz clazz = (Clazz)iterator.next();
- if (subClass.extendsOrImplements(clazz))
- {
- count++;
- }
- }
+ // Similar binary methods, but this time with typed reference arguments.
- return count;
+ /**
+ * Returns the generalization of this ReferenceValue and the given other
+ * TypedReferenceValue.
+ */
+ public ReferenceValue generalize(TypedReferenceValue other)
+ {
+ return generalize((ReferenceValue)other);
}
/**
* Returns whether this ReferenceValue is equal to the given other
- * ReferenceValue.
+ * TypedReferenceValue.
* @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
*/
- public int equal(ReferenceValue other)
+ public int equal(TypedReferenceValue other)
{
- return this.type == null && other.type == null ? ALWAYS : MAYBE;
+ return equal((ReferenceValue)other);
}
- // Derived unary methods.
+ // Similar binary methods, but this time with identified reference
+ // arguments.
/**
- * Returns whether this ReferenceValue is not <code>null</code>.
+ * Returns the generalization of this ReferenceValue and the given other
+ * IdentifiedReferenceValue.
+ */
+ public ReferenceValue generalize(IdentifiedReferenceValue other)
+ {
+ return generalize((TypedReferenceValue)other);
+ }
+
+
+ /**
+ * Returns whether this ReferenceValue is equal to the given other
+ * IdentifiedReferenceValue.
* @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
*/
- public final int isNotNull()
+ public int equal(IdentifiedReferenceValue other)
{
- return -isNull();
+ return equal((TypedReferenceValue)other);
}
+ // Similar binary methods, but this time with array reference arguments.
+
/**
* Returns the generalization of this ReferenceValue and the given other
- * ReferenceValue.
+ * ArrayReferenceValue.
*/
- private ReferenceValue generalizeMayBeNull(boolean mayBeNull)
+ public ReferenceValue generalize(ArrayReferenceValue other)
{
- return this.mayBeNull || !mayBeNull ?
- this :
- new ReferenceValue(this.type, this.referencedClass, true);
+ return generalize((TypedReferenceValue)other);
}
- // Derived binary methods.
-
/**
- * Returns whether this ReferenceValue and the given ReferenceValue are different.
+ * Returns whether this ReferenceValue is equal to the given other
+ * ArrayReferenceValue.
* @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
*/
- public final int notEqual(ReferenceValue other)
+ public int equal(ArrayReferenceValue other)
{
- return -equal(other);
+ return equal((TypedReferenceValue)other);
}
- // Implementations for Value.
+ // Similar binary methods, but this time with identified array reference
+ // arguments.
- public final ReferenceValue referenceValue()
+ /**
+ * Returns the generalization of this ReferenceValue and the given other
+ * IdentifiedArrayReferenceValue.
+ */
+ public ReferenceValue generalize(IdentifiedArrayReferenceValue other)
{
- return this;
+ return generalize((ArrayReferenceValue)other);
}
- public final Value generalize(Value other)
- {
- return this.generalize(other.referenceValue());
- }
- public boolean isParticular()
+ /**
+ * Returns whether this ReferenceValue is equal to the given other
+ * IdentifiedArrayReferenceValue.
+ * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+ */
+ public int equal(IdentifiedArrayReferenceValue other)
{
- return type == null;
+ return equal((ArrayReferenceValue)other);
}
- public final int computationalType()
+
+ // Similar binary methods, but this time with detailed array reference
+ // arguments.
+
+ /**
+ * Returns the generalization of this ReferenceValue and the given other
+ * DetailedArrayReferenceValue.
+ */
+ public ReferenceValue generalize(DetailedArrayReferenceValue other)
{
- return TYPE_REFERENCE;
+ return generalize((IdentifiedArrayReferenceValue)other);
}
- public final String internalType()
+
+ /**
+ * Returns whether this ReferenceValue is equal to the given other
+ * DetailedArrayReferenceValue.
+ * @return <code>NEVER</code>, <code>MAYBE</code>, or <code>ALWAYS</code>.
+ */
+ public int equal(DetailedArrayReferenceValue other)
{
- return
- type == null ? ClassConstants.INTERNAL_TYPE_JAVA_LANG_OBJECT :
- ClassUtil.isInternalArrayType(type) ? type :
- ClassConstants.INTERNAL_TYPE_CLASS_START +
- type +
- ClassConstants.INTERNAL_TYPE_CLASS_END;
+ return equal((IdentifiedArrayReferenceValue)other);
}
- // Implementations for Object.
+ // Implementations for Value.
- public boolean equals(Object object)
+ public final ReferenceValue referenceValue()
{
- if (this == object)
- {
- return true;
- }
-
- if (object == null ||
- this.getClass() != object.getClass())
- {
- return false;
- }
-
- ReferenceValue other = (ReferenceValue)object;
- return this.type == null ? other.type == null :
- (this.mayBeNull == other.mayBeNull &&
- this.type.equals(other.type));
+ return this;
}
-
- public int hashCode()
+ public final Value generalize(Value other)
{
- return this.getClass().hashCode() ^
- (type == null ? 0 : type.hashCode() ^ (mayBeNull ? 0 : 1));
+ return this.generalize(other.referenceValue());
}
-
- public String toString()
+ public final int computationalType()
{
- return type == null ?
- "null" :
- type + (referencedClass == null ? "?" : "") + (mayBeNull ? "" : "!");
+ return TYPE_REFERENCE;
}
}