Specialize Value types and make Value more type safe.
Saves memory by removing an extra layer of boxing for primitive
values.
Test: m ahat-test
Change-Id: I9d96d8ff0bd55cbeaa7ce51700133aca9f43621b
diff --git a/tools/ahat/src/heapdump/AhatArrayInstance.java b/tools/ahat/src/heapdump/AhatArrayInstance.java
index 6d4485d..4b18bf7 100644
--- a/tools/ahat/src/heapdump/AhatArrayInstance.java
+++ b/tools/ahat/src/heapdump/AhatArrayInstance.java
@@ -58,8 +58,7 @@
}
@Override public Value get(int index) {
- AhatInstance obj = insts[index];
- return obj == null ? null : new Value(insts[index]);
+ return Value.pack(insts[index]);
}
};
break;
@@ -73,7 +72,7 @@
}
@Override public Value get(int index) {
- return new Value(chars[index]);
+ return Value.pack(chars[index]);
}
};
break;
@@ -87,7 +86,7 @@
}
@Override public Value get(int index) {
- return new Value(bytes[index]);
+ return Value.pack(bytes[index]);
}
};
break;
@@ -100,8 +99,7 @@
}
@Override public Value get(int index) {
- Object obj = values[index];
- return obj == null ? null : new Value(obj);
+ return Value.pack(values[index]);
}
};
break;
diff --git a/tools/ahat/src/heapdump/AhatSnapshot.java b/tools/ahat/src/heapdump/AhatSnapshot.java
index 2f0b30d..fa41362 100644
--- a/tools/ahat/src/heapdump/AhatSnapshot.java
+++ b/tools/ahat/src/heapdump/AhatSnapshot.java
@@ -271,7 +271,7 @@
if (value instanceof Instance) {
value = findInstance(((Instance)value).getId());
}
- return value == null ? null : new Value(value);
+ return Value.pack(value);
}
public void setBaseline(AhatSnapshot baseline) {
diff --git a/tools/ahat/src/heapdump/Value.java b/tools/ahat/src/heapdump/Value.java
index c1f3022..38a6815 100644
--- a/tools/ahat/src/heapdump/Value.java
+++ b/tools/ahat/src/heapdump/Value.java
@@ -20,19 +20,71 @@
* Value represents a field value in a heap dump. The field value is either a
* subclass of AhatInstance or a primitive Java type.
*/
-public class Value {
- private Object mObject;
+public abstract class Value {
+ public static Value pack(AhatInstance value) {
+ return value == null ? null : new InstanceValue(value);
+ }
/**
* Constructs a value from a generic Java Object.
* The Object must either be a boxed Java primitive type or a subclass of
* AhatInstance. The object must not be null.
*/
- public Value(Object object) {
- // TODO: Check that the Object is either an AhatSnapshot or boxed Java
- // primitive type?
- assert object != null;
- mObject = object;
+ public static Value pack(Object object) {
+ if (object == null) {
+ return null;
+ } else if (object instanceof AhatInstance) {
+ return Value.pack((AhatInstance)object);
+ } else if (object instanceof Boolean) {
+ return Value.pack(((Boolean)object).booleanValue());
+ } else if (object instanceof Character) {
+ return Value.pack(((Character)object).charValue());
+ } else if (object instanceof Float) {
+ return Value.pack(((Float)object).floatValue());
+ } else if (object instanceof Double) {
+ return Value.pack(((Double)object).doubleValue());
+ } else if (object instanceof Byte) {
+ return Value.pack(((Byte)object).byteValue());
+ } else if (object instanceof Short) {
+ return Value.pack(((Short)object).shortValue());
+ } else if (object instanceof Integer) {
+ return Value.pack(((Integer)object).intValue());
+ } else if (object instanceof Long) {
+ return Value.pack(((Long)object).longValue());
+ }
+ throw new IllegalArgumentException("AhatInstance or primitive type required");
+ }
+
+ public static Value pack(boolean value) {
+ return new BooleanValue(value);
+ }
+
+ public static Value pack(char value) {
+ return new CharValue(value);
+ }
+
+ public static Value pack(float value) {
+ return new FloatValue(value);
+ }
+
+ public static Value pack(double value) {
+ return new DoubleValue(value);
+ }
+
+ public static Value pack(byte value) {
+ return new ByteValue(value);
+ }
+
+ public static Value pack(short value) {
+ return new ShortValue(value);
+ }
+
+ public static Value pack(int value) {
+ return new IntValue(value);
+ }
+
+ public static Value pack(long value) {
+ return new LongValue(value);
}
/**
@@ -40,7 +92,7 @@
* primitive value.
*/
public boolean isAhatInstance() {
- return mObject instanceof AhatInstance;
+ return false;
}
/**
@@ -48,9 +100,6 @@
* Returns null if the Value represents a Java primitive value.
*/
public AhatInstance asAhatInstance() {
- if (isAhatInstance()) {
- return (AhatInstance)mObject;
- }
return null;
}
@@ -58,7 +107,7 @@
* Returns true if the Value is an Integer.
*/
public boolean isInteger() {
- return mObject instanceof Integer;
+ return false;
}
/**
@@ -66,9 +115,6 @@
* Returns null if the Value does not represent an Integer.
*/
public Integer asInteger() {
- if (isInteger()) {
- return (Integer)mObject;
- }
return null;
}
@@ -76,7 +122,7 @@
* Returns true if the Value is an Long.
*/
public boolean isLong() {
- return mObject instanceof Long;
+ return false;
}
/**
@@ -84,9 +130,6 @@
* Returns null if the Value does not represent an Long.
*/
public Long asLong() {
- if (isLong()) {
- return (Long)mObject;
- }
return null;
}
@@ -95,9 +138,6 @@
* Returns null if the Value does not represent a Byte.
*/
public Byte asByte() {
- if (mObject instanceof Byte) {
- return (Byte)mObject;
- }
return null;
}
@@ -106,28 +146,255 @@
* Returns null if the Value does not represent a Char.
*/
public Character asChar() {
- if (mObject instanceof Character) {
- return (Character)mObject;
- }
return null;
}
- public String toString() {
- return mObject.toString();
+ @Override
+ public abstract String toString();
+
+ public Value getBaseline() {
+ return this;
}
public static Value getBaseline(Value value) {
- if (value == null || !value.isAhatInstance()) {
- return value;
- }
- return new Value(value.asAhatInstance().getBaseline());
+ return value == null ? null : value.getBaseline();
}
- @Override public boolean equals(Object other) {
- if (other instanceof Value) {
- Value value = (Value)other;
- return mObject.equals(value.mObject);
+ @Override
+ public abstract boolean equals(Object other);
+
+ private static class BooleanValue extends Value {
+ private boolean mBool;
+
+ BooleanValue(boolean bool) {
+ mBool = bool;
}
- return false;
+
+ @Override
+ public String toString() {
+ return Boolean.toString(mBool);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof BooleanValue) {
+ BooleanValue value = (BooleanValue)other;
+ return mBool == value.mBool;
+ }
+ return false;
+ }
+ }
+
+ private static class ByteValue extends Value {
+ private byte mByte;
+
+ ByteValue(byte b) {
+ mByte = b;
+ }
+
+ @Override
+ public Byte asByte() {
+ return mByte;
+ }
+
+ @Override
+ public String toString() {
+ return Byte.toString(mByte);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof ByteValue) {
+ ByteValue value = (ByteValue)other;
+ return mByte == value.mByte;
+ }
+ return false;
+ }
+ }
+
+ private static class CharValue extends Value {
+ private char mChar;
+
+ CharValue(char c) {
+ mChar = c;
+ }
+
+ @Override
+ public Character asChar() {
+ return mChar;
+ }
+
+ @Override
+ public String toString() {
+ return Character.toString(mChar);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof CharValue) {
+ CharValue value = (CharValue)other;
+ return mChar == value.mChar;
+ }
+ return false;
+ }
+ }
+
+ private static class DoubleValue extends Value {
+ private double mDouble;
+
+ DoubleValue(double d) {
+ mDouble = d;
+ }
+
+ @Override
+ public String toString() {
+ return Double.toString(mDouble);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof DoubleValue) {
+ DoubleValue value = (DoubleValue)other;
+ return mDouble == value.mDouble;
+ }
+ return false;
+ }
+ }
+
+ private static class FloatValue extends Value {
+ private float mFloat;
+
+ FloatValue(float f) {
+ mFloat = f;
+ }
+
+ @Override
+ public String toString() {
+ return Float.toString(mFloat);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof FloatValue) {
+ FloatValue value = (FloatValue)other;
+ return mFloat == value.mFloat;
+ }
+ return false;
+ }
+ }
+
+ private static class InstanceValue extends Value {
+ private AhatInstance mInstance;
+
+ InstanceValue(AhatInstance inst) {
+ assert(inst != null);
+ mInstance = inst;
+ }
+
+ @Override
+ public boolean isAhatInstance() {
+ return true;
+ }
+
+ @Override
+ public AhatInstance asAhatInstance() {
+ return mInstance;
+ }
+
+ @Override
+ public String toString() {
+ return mInstance.toString();
+ }
+
+ @Override
+ public Value getBaseline() {
+ return InstanceValue.pack(mInstance.getBaseline());
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof InstanceValue) {
+ InstanceValue value = (InstanceValue)other;
+ return mInstance.equals(value.mInstance);
+ }
+ return false;
+ }
+ }
+
+ private static class IntValue extends Value {
+ private int mInt;
+
+ IntValue(int i) {
+ mInt = i;
+ }
+
+ @Override
+ public boolean isInteger() {
+ return true;
+ }
+
+ @Override
+ public Integer asInteger() {
+ return mInt;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(mInt);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof IntValue) {
+ IntValue value = (IntValue)other;
+ return mInt == value.mInt;
+ }
+ return false;
+ }
+ }
+
+ private static class LongValue extends Value {
+ private long mLong;
+
+ LongValue(long l) {
+ mLong = l;
+ }
+
+ @Override
+ public boolean isLong() {
+ return true;
+ }
+
+ @Override
+ public Long asLong() {
+ return mLong;
+ }
+
+ @Override
+ public String toString() {
+ return Long.toString(mLong);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof LongValue) {
+ LongValue value = (LongValue)other;
+ return mLong == value.mLong;
+ }
+ return false;
+ }
+ }
+
+ private static class ShortValue extends Value {
+ private short mShort;
+
+ ShortValue(short s) {
+ mShort = s;
+ }
+
+ @Override
+ public String toString() {
+ return Short.toString(mShort);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof ShortValue) {
+ ShortValue value = (ShortValue)other;
+ return mShort == value.mShort;
+ }
+ return false;
+ }
}
}
diff --git a/tools/ahat/test/DiffFieldsTest.java b/tools/ahat/test/DiffFieldsTest.java
index 6abdd47..7dc519d 100644
--- a/tools/ahat/test/DiffFieldsTest.java
+++ b/tools/ahat/test/DiffFieldsTest.java
@@ -30,26 +30,26 @@
public class DiffFieldsTest {
@Test
public void normalMatchedDiffedFieldValues() {
- FieldValue normal1 = new FieldValue("name", "type", new Value(1));
- FieldValue normal2 = new FieldValue("name", "type", new Value(2));
+ FieldValue normal1 = new FieldValue("name", "type", Value.pack(1));
+ FieldValue normal2 = new FieldValue("name", "type", Value.pack(2));
DiffedFieldValue x = DiffedFieldValue.matched(normal1, normal2);
assertEquals("name", x.name);
assertEquals("type", x.type);
- assertEquals(new Value(1), x.current);
- assertEquals(new Value(2), x.baseline);
+ assertEquals(Value.pack(1), x.current);
+ assertEquals(Value.pack(2), x.baseline);
assertEquals(DiffedFieldValue.Status.MATCHED, x.status);
}
@Test
public void nulledMatchedDiffedFieldValues() {
- FieldValue normal = new FieldValue("name", "type", new Value(1));
+ FieldValue normal = new FieldValue("name", "type", Value.pack(1));
FieldValue nulled = new FieldValue("name", "type", null);
DiffedFieldValue x = DiffedFieldValue.matched(normal, nulled);
assertEquals("name", x.name);
assertEquals("type", x.type);
- assertEquals(new Value(1), x.current);
+ assertEquals(Value.pack(1), x.current);
assertNull(x.baseline);
assertEquals(DiffedFieldValue.Status.MATCHED, x.status);
@@ -57,18 +57,18 @@
assertEquals("name", y.name);
assertEquals("type", y.type);
assertNull(y.current);
- assertEquals(new Value(1), y.baseline);
+ assertEquals(Value.pack(1), y.baseline);
assertEquals(DiffedFieldValue.Status.MATCHED, y.status);
}
@Test
public void normalAddedDiffedFieldValues() {
- FieldValue normal = new FieldValue("name", "type", new Value(1));
+ FieldValue normal = new FieldValue("name", "type", Value.pack(1));
DiffedFieldValue x = DiffedFieldValue.added(normal);
assertEquals("name", x.name);
assertEquals("type", x.type);
- assertEquals(new Value(1), x.current);
+ assertEquals(Value.pack(1), x.current);
assertEquals(DiffedFieldValue.Status.ADDED, x.status);
}
@@ -85,12 +85,12 @@
@Test
public void normalDeletedDiffedFieldValues() {
- FieldValue normal = new FieldValue("name", "type", new Value(1));
+ FieldValue normal = new FieldValue("name", "type", Value.pack(1));
DiffedFieldValue x = DiffedFieldValue.deleted(normal);
assertEquals("name", x.name);
assertEquals("type", x.type);
- assertEquals(new Value(1), x.baseline);
+ assertEquals(Value.pack(1), x.baseline);
assertEquals(DiffedFieldValue.Status.DELETED, x.status);
}