summaryrefslogtreecommitdiffstats
path: root/dx
diff options
context:
space:
mode:
authorJesse Wilson <jessewilson@google.com>2011-09-15 15:49:25 -0400
committerBrian Carlstrom <bdc@google.com>2013-04-29 18:21:55 -0700
commit7e85b634fbafa4a3b927c80665dea6fac337d886 (patch)
tree51197d2d020fb9a69962542ae59e5810ed0b5f89 /dx
parent33a977ec6a12deb22bcc87f8c294439b3aba16ab (diff)
downloadandroid_dalvik-7e85b634fbafa4a3b927c80665dea6fac337d886.tar.gz
android_dalvik-7e85b634fbafa4a3b927c80665dea6fac337d886.tar.bz2
android_dalvik-7e85b634fbafa4a3b927c80665dea6fac337d886.zip
Teach dex how to parse encoded values from .dex files.
Previously primitive values were treated as opaque byte arrays. Now the encoded values can be interpreted as their proper types: integers, indices, floats, chars, etc. This gets complicated pretty fast due to the dense packing dx performs when encoding the values. The encoding code is moved from ValueEncoder. The decoding code is ported from C++ to Java from Annotation.cpp. There's a bunch of new tests to make sure the decoding is correct; porting from C++ to Java was tricky because of the '>>' shifts on unsigned values. The test input data is generated by dex! Change-Id: I83b2fc3e16115d667fa94b3dab782d1a9687f3ad (cherry picked from commit 27847605b9255358f0577ffec28886c450263898)
Diffstat (limited to 'dx')
-rw-r--r--dx/junit-tests/com/android/dx/io/EncodedValueReaderTest.java127
-rw-r--r--dx/src/com/android/dx/command/grep/Grep.java17
-rw-r--r--dx/src/com/android/dx/dex/file/ValueEncoder.java122
-rw-r--r--dx/src/com/android/dx/io/DexBuffer.java4
-rw-r--r--dx/src/com/android/dx/io/EncodedValueReader.java253
-rw-r--r--dx/src/com/android/dx/merge/IndexMap.java163
-rw-r--r--dx/src/com/android/dx/util/EncodedValueUtils.java184
7 files changed, 619 insertions, 251 deletions
diff --git a/dx/junit-tests/com/android/dx/io/EncodedValueReaderTest.java b/dx/junit-tests/com/android/dx/io/EncodedValueReaderTest.java
new file mode 100644
index 000000000..63689ec03
--- /dev/null
+++ b/dx/junit-tests/com/android/dx/io/EncodedValueReaderTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed 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 com.android.dx.io;
+
+import com.android.dx.util.ByteArrayByteInput;
+import junit.framework.TestCase;
+
+public final class EncodedValueReaderTest extends TestCase {
+
+ public void testReadByte() {
+ assertEquals((byte) 0x80, readerOf(0, 0x80).readByte());
+ assertEquals((byte) 0xff, readerOf(0, 0xff).readByte());
+ assertEquals((byte) 0x00, readerOf(0, 0x00).readByte());
+ assertEquals((byte) 0x01, readerOf(0, 0x01).readByte());
+ assertEquals((byte) 0x7f, readerOf(0, 0x7f).readByte());
+ }
+
+ public void testReadShort() {
+ assertEquals((short) 0x8000, readerOf(34, 0x00, 0x80).readShort());
+ assertEquals((short) 0, readerOf( 2, 0x00).readShort());
+ assertEquals((short) 0xab, readerOf(34, 0xab, 0x00).readShort());
+ assertEquals((short) 0xabcd, readerOf(34, 0xcd, 0xab).readShort());
+ assertEquals((short) 0x7FFF, readerOf(34, 0xff, 0x7f).readShort());
+ }
+
+ public void testReadInt() {
+ assertEquals(0x80000000, readerOf(100, 0x00, 0x00, 0x00, 0x80).readInt());
+ assertEquals( 0x00, readerOf( 4, 0x00).readInt());
+ assertEquals( 0xab, readerOf( 36, 0xab, 0x00).readInt());
+ assertEquals( 0xabcd, readerOf( 68, 0xcd, 0xab, 0x00).readInt());
+ assertEquals( 0xabcdef, readerOf(100, 0xef, 0xcd, 0xab, 0x00).readInt());
+ assertEquals(0xabcdef01, readerOf(100, 0x01, 0xef, 0xcd, 0xab).readInt());
+ assertEquals(0x7fffffff, readerOf(100, 0xff, 0xff, 0xff, 127).readInt());
+ }
+
+ public void testReadLong() {
+ assertEquals(0x8000000000000000L, readerOf( -26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80).readLong());
+ assertEquals( 0x00L, readerOf( 6, 0x00).readLong());
+ assertEquals( 0xabL, readerOf( 38, 0xab, 0x00).readLong());
+ assertEquals( 0xabcdL, readerOf( 70, 0xcd, 0xab, 0x00).readLong());
+ assertEquals( 0xabcdefL, readerOf( 102, 0xef, 0xcd, 0xab, 0x00).readLong());
+ assertEquals( 0xabcdef01L, readerOf(-122, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong());
+ assertEquals( 0xabcdef0123L, readerOf( -90, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong());
+ assertEquals( 0xabcdef012345L, readerOf( -58, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong());
+ assertEquals( 0xabcdef01234567L, readerOf( -26, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x00).readLong());
+ assertEquals(0xabcdef0123456789L, readerOf( -26, 0x89, 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab).readLong());
+ assertEquals(0x7fffffffffffffffL, readerOf( -26, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f).readLong());
+ }
+
+ public void testReadFloat() {
+ assertEquals(Float.NEGATIVE_INFINITY, readerOf(48, -128, -1).readFloat());
+ assertEquals(Float.POSITIVE_INFINITY, readerOf(48, -128, 127).readFloat());
+ assertEquals(Float.NaN, readerOf(48, -64, 127).readFloat());
+ assertEquals(-0.0f, readerOf(16, -128).readFloat());
+ assertEquals(0.0f, readerOf(16, 0).readFloat());
+ assertEquals(0.5f, readerOf(16, 63).readFloat());
+ assertEquals(1f, readerOf(48, -128, 63).readFloat());
+ assertEquals(1.0E06f, readerOf(80, 36, 116, 73).readFloat());
+ assertEquals(1.0E12f, readerOf(112, -91, -44, 104, 83).readFloat());
+ }
+
+ public void testReadDouble() {
+ assertEquals(Double.NEGATIVE_INFINITY, readerOf(49, -16, -1).readDouble());
+ assertEquals(Double.POSITIVE_INFINITY, readerOf(49, -16, 127).readDouble());
+ assertEquals(Double.NaN, readerOf(49, -8, 127).readDouble());
+ assertEquals(-0.0, readerOf(17, -128).readDouble());
+ assertEquals(0.0, readerOf(17, 0).readDouble());
+ assertEquals(0.5, readerOf(49, -32, 63).readDouble());
+ assertEquals(1.0, readerOf(49, -16, 63).readDouble());
+ assertEquals(1.0E06, readerOf(113, -128, -124, 46, 65).readDouble());
+ assertEquals(1.0E12, readerOf(-111, -94, -108, 26, 109, 66).readDouble());
+ assertEquals(1.0E24, readerOf(-15, -76, -99, -39, 121, 67, 120, -22, 68).readDouble());
+ }
+
+ public void testReadChar() {
+ assertEquals('\u0000', readerOf( 3, 0x00).readChar());
+ assertEquals('\u00ab', readerOf( 3, 0xab).readChar());
+ assertEquals('\uabcd', readerOf(35, 0xcd, 0xab).readChar());
+ assertEquals('\uffff', readerOf(35, 0xff, 0xff).readChar());
+ }
+
+ public void testReadBoolean() {
+ assertEquals(true, readerOf(63).readBoolean());
+ assertEquals(false, readerOf(31).readBoolean());
+ }
+
+ public void testReadNull() {
+ readerOf(30).readNull();
+ }
+
+ public void testReadReference() {
+ assertEquals( 0xab, readerOf(0x17, 0xab).readString());
+ assertEquals( 0xabcd, readerOf(0x37, 0xcd, 0xab).readString());
+ assertEquals( 0xabcdef, readerOf(0x57, 0xef, 0xcd, 0xab).readString());
+ assertEquals(0xabcdef01, readerOf(0x77, 0x01, 0xef, 0xcd, 0xab).readString());
+ }
+
+ public void testReadWrongType() {
+ try {
+ readerOf(0x17, 0xab).readField();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ private EncodedValueReader readerOf(int... bytes) {
+ byte[] data = new byte[bytes.length];
+ for (int i = 0; i < bytes.length; i++) {
+ data[i] = (byte) bytes[i];
+ }
+ return new EncodedValueReader(new ByteArrayByteInput(data));
+ }
+}
diff --git a/dx/src/com/android/dx/command/grep/Grep.java b/dx/src/com/android/dx/command/grep/Grep.java
index 741de9750..14c46d7cc 100644
--- a/dx/src/com/android/dx/command/grep/Grep.java
+++ b/dx/src/com/android/dx/command/grep/Grep.java
@@ -52,12 +52,17 @@ public final class Grep {
});
}
- private EncodedValueReader newEncodedValueReader(DexBuffer.Section section) {
- return new EncodedValueReader(section) {
- @Override protected void visitString(int type, int index) {
- encounterString(index);
+ private void readArray(EncodedValueReader reader) {
+ for (int i = 0, size = reader.readArray(); i < size; i++) {
+ switch (reader.peek()) {
+ case EncodedValueReader.ENCODED_STRING:
+ encounterString(reader.readString());
+ break;
+ case EncodedValueReader.ENCODED_ARRAY:
+ readArray(reader);
+ break;
}
- };
+ }
}
private void encounterString(int index) {
@@ -94,7 +99,7 @@ public final class Grep {
// find the strings in encoded constants
int staticValuesOffset = classDef.getStaticValuesOffset();
if (staticValuesOffset != 0) {
- newEncodedValueReader(dex.open(staticValuesOffset)).readArray();
+ readArray(new EncodedValueReader(dex.open(staticValuesOffset)));
}
// find the strings in method bodies
diff --git a/dx/src/com/android/dx/dex/file/ValueEncoder.java b/dx/src/com/android/dx/dex/file/ValueEncoder.java
index 9c433a3de..aafb42945 100644
--- a/dx/src/com/android/dx/dex/file/ValueEncoder.java
+++ b/dx/src/com/android/dx/dex/file/ValueEncoder.java
@@ -37,6 +37,7 @@ import com.android.dx.rop.cst.CstShort;
import com.android.dx.rop.cst.CstString;
import com.android.dx.rop.cst.CstType;
import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.EncodedValueUtils;
import com.android.dx.util.Hex;
import java.util.Collection;
@@ -133,49 +134,49 @@ public final class ValueEncoder {
case VALUE_INT:
case VALUE_LONG: {
long value = ((CstLiteralBits) cst).getLongBits();
- writeSignedIntegralValue(type, value);
+ EncodedValueUtils.writeSignedIntegralValue(out, type, value);
break;
}
case VALUE_CHAR: {
long value = ((CstLiteralBits) cst).getLongBits();
- writeUnsignedIntegralValue(type, value);
+ EncodedValueUtils.writeUnsignedIntegralValue(out, type, value);
break;
}
case VALUE_FLOAT: {
// Shift value left 32 so that right-zero-extension works.
long value = ((CstFloat) cst).getLongBits() << 32;
- writeRightZeroExtendedValue(type, value);
+ EncodedValueUtils.writeRightZeroExtendedValue(out, type, value);
break;
}
case VALUE_DOUBLE: {
long value = ((CstDouble) cst).getLongBits();
- writeRightZeroExtendedValue(type, value);
+ EncodedValueUtils.writeRightZeroExtendedValue(out, type, value);
break;
}
case VALUE_STRING: {
int index = file.getStringIds().indexOf((CstString) cst);
- writeUnsignedIntegralValue(type, (long) index);
+ EncodedValueUtils.writeUnsignedIntegralValue(out, type, (long) index);
break;
}
case VALUE_TYPE: {
int index = file.getTypeIds().indexOf((CstType) cst);
- writeUnsignedIntegralValue(type, (long) index);
+ EncodedValueUtils.writeUnsignedIntegralValue(out, type, (long) index);
break;
}
case VALUE_FIELD: {
int index = file.getFieldIds().indexOf((CstFieldRef) cst);
- writeUnsignedIntegralValue(type, (long) index);
+ EncodedValueUtils.writeUnsignedIntegralValue(out, type, (long) index);
break;
}
case VALUE_METHOD: {
int index = file.getMethodIds().indexOf((CstMethodRef) cst);
- writeUnsignedIntegralValue(type, (long) index);
+ EncodedValueUtils.writeUnsignedIntegralValue(out, type, (long) index);
break;
}
case VALUE_ENUM: {
CstFieldRef fieldRef = ((CstEnumRef) cst).getFieldRef();
int index = file.getFieldIds().indexOf(fieldRef);
- writeUnsignedIntegralValue(type, (long) index);
+ EncodedValueUtils.writeUnsignedIntegralValue(out, type, (long) index);
break;
}
case VALUE_ARRAY: {
@@ -379,109 +380,6 @@ public final class ValueEncoder {
}
/**
- * Helper for {@link #writeConstant}, which writes out the value
- * for any signed integral type.
- *
- * @param type the type constant
- * @param value {@code long} bits of the value
- */
- private void writeSignedIntegralValue(int type, long value) {
- /*
- * Figure out how many bits are needed to represent the value,
- * including a sign bit: The bit count is subtracted from 65
- * and not 64 to account for the sign bit. The xor operation
- * has the effect of leaving non-negative values alone and
- * unary complementing negative values (so that a leading zero
- * count always returns a useful number for our present
- * purpose).
- */
- int requiredBits =
- 65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
-
- // Round up the requiredBits to a number of bytes.
- int requiredBytes = (requiredBits + 0x07) >> 3;
-
- /*
- * Write the header byte, which includes the type and
- * requiredBytes - 1.
- */
- out.writeByte(type | ((requiredBytes - 1) << 5));
-
- // Write the value, per se.
- while (requiredBytes > 0) {
- out.writeByte((byte) value);
- value >>= 8;
- requiredBytes--;
- }
- }
-
- /**
- * Helper for {@link #writeConstant}, which writes out the value
- * for any unsigned integral type.
- *
- * @param type the type constant
- * @param value {@code long} bits of the value
- */
- private void writeUnsignedIntegralValue(int type, long value) {
- // Figure out how many bits are needed to represent the value.
- int requiredBits = 64 - Long.numberOfLeadingZeros(value);
- if (requiredBits == 0) {
- requiredBits = 1;
- }
-
- // Round up the requiredBits to a number of bytes.
- int requiredBytes = (requiredBits + 0x07) >> 3;
-
- /*
- * Write the header byte, which includes the type and
- * requiredBytes - 1.
- */
- out.writeByte(type | ((requiredBytes - 1) << 5));
-
- // Write the value, per se.
- while (requiredBytes > 0) {
- out.writeByte((byte) value);
- value >>= 8;
- requiredBytes--;
- }
- }
-
- /**
- * Helper for {@link #writeConstant}, which writes out a
- * right-zero-extended value.
- *
- * @param type the type constant
- * @param value {@code long} bits of the value
- */
- private void writeRightZeroExtendedValue(int type, long value) {
- // Figure out how many bits are needed to represent the value.
- int requiredBits = 64 - Long.numberOfTrailingZeros(value);
- if (requiredBits == 0) {
- requiredBits = 1;
- }
-
- // Round up the requiredBits to a number of bytes.
- int requiredBytes = (requiredBits + 0x07) >> 3;
-
- // Scootch the first bits to be written down to the low-order bits.
- value >>= 64 - (requiredBytes * 8);
-
- /*
- * Write the header byte, which includes the type and
- * requiredBytes - 1.
- */
- out.writeByte(type | ((requiredBytes - 1) << 5));
-
- // Write the value, per se.
- while (requiredBytes > 0) {
- out.writeByte((byte) value);
- value >>= 8;
- requiredBytes--;
- }
- }
-
-
- /**
* Helper for {@code addContents()} methods, which adds
* contents for a particular {@link Annotation}, calling itself
* recursively should it encounter a nested annotation.
diff --git a/dx/src/com/android/dx/io/DexBuffer.java b/dx/src/com/android/dx/io/DexBuffer.java
index 9fbc78cac..c338ef43e 100644
--- a/dx/src/com/android/dx/io/DexBuffer.java
+++ b/dx/src/com/android/dx/io/DexBuffer.java
@@ -565,14 +565,14 @@ public final class DexBuffer {
public EncodedValue readEncodedValue() {
int start = position;
- new EncodedValueReader(this).readValue();
+ new EncodedValueReader(this).skipValue();
int end = position;
return new EncodedValue(Arrays.copyOfRange(data, start, end));
}
public EncodedValue readEncodedArray() {
int start = position;
- new EncodedValueReader(this).readArray();
+ new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue();
int end = position;
return new EncodedValue(Arrays.copyOfRange(data, start, end));
}
diff --git a/dx/src/com/android/dx/io/EncodedValueReader.java b/dx/src/com/android/dx/io/EncodedValueReader.java
index ec84898ba..c53e5b892 100644
--- a/dx/src/com/android/dx/io/EncodedValueReader.java
+++ b/dx/src/com/android/dx/io/EncodedValueReader.java
@@ -17,13 +17,14 @@
package com.android.dx.io;
import com.android.dx.util.ByteInput;
+import com.android.dx.util.DexException;
+import com.android.dx.util.EncodedValueUtils;
import com.android.dx.util.Leb128Utils;
/**
- * SAX-style reader for encoded values.
- * TODO: convert this to a pull-style reader
+ * Pull parser for encoded values.
*/
-public class EncodedValueReader {
+public final class EncodedValueReader {
public static final int ENCODED_BYTE = 0x00;
public static final int ENCODED_SHORT = 0x02;
public static final int ENCODED_CHAR = 0x03;
@@ -41,7 +42,13 @@ public class EncodedValueReader {
public static final int ENCODED_NULL = 0x1e;
public static final int ENCODED_BOOLEAN = 0x1f;
+ /** placeholder type if the type is not yet known */
+ private static final int MUST_READ = -1;
+
protected final ByteInput in;
+ private int type = MUST_READ;
+ private int annotationType;
+ private int arg;
public EncodedValueReader(ByteInput in) {
this.in = in;
@@ -51,96 +58,228 @@ public class EncodedValueReader {
this(in.asByteInput());
}
- public final void readArray() {
- int size = Leb128Utils.readUnsignedLeb128(in);
- visitArray(size);
+ /**
+ * Creates a new encoded value reader whose only value is the specified
+ * known type. This is useful for class_def_item, which references an
+ * encoded array that doesn't contain a type+arg prefix.
+ */
+ public EncodedValueReader(ByteInput in, int knownType) {
+ this.in = in;
+ this.type = knownType;
+ }
- for (int i = 0; i < size; i++) {
- readValue();
+ /**
+ * Returns the type of the next value to read.
+ */
+ public int peek() {
+ if (type == MUST_READ) {
+ int argAndType = in.readByte() & 0xff;
+ type = argAndType & 0x1f;
+ arg = (argAndType & 0xe0) >> 5;
}
+ return type;
}
- public final void readAnnotation() {
- int typeIndex = Leb128Utils.readUnsignedLeb128(in);
- int size = Leb128Utils.readUnsignedLeb128(in);
- visitAnnotation(typeIndex, size);
+ /**
+ * Begins reading the elements of an array, returning the array's size. The
+ * caller must follow up by calling a read method for each element in the
+ * array. For example, this reads a byte array: <pre> {@code
+ * int arraySize = readArray();
+ * for (int i = 0, i < arraySize; i++) {
+ * readByte();
+ * }
+ * }</pre>
+ */
+ public int readArray() {
+ checkType(ENCODED_ARRAY);
+ type = MUST_READ;
+ return Leb128Utils.readUnsignedLeb128(in);
+ }
- for (int i = 0; i < size; i++) {
- visitAnnotationName(Leb128Utils.readUnsignedLeb128(in));
- readValue();
- }
+ /**
+ * Begins reading the fields of an annotation, returning the number of
+ * fields. The caller must follow up by making alternating calls to {@link
+ * #readAnnotationName()} and another read method. For example, this reads
+ * an annotation whose fields are all bytes: <pre> {@code
+ * int fieldCount = readAnnotation();
+ * int annotationType = getAnnotationType();
+ * for (int i = 0; i < fieldCount; i++) {
+ * readAnnotationName();
+ * readByte();
+ * }
+ * }</pre>
+ */
+ public int readAnnotation() {
+ checkType(ENCODED_ANNOTATION);
+ type = MUST_READ;
+ annotationType = Leb128Utils.readUnsignedLeb128(in);
+ return Leb128Utils.readUnsignedLeb128(in);
+ }
+
+ /**
+ * Returns the type of the annotation just returned by {@link
+ * #readAnnotation()}. This method's value is undefined unless the most
+ * recent call was to {@link #readAnnotation()}.
+ */
+ public int getAnnotationType() {
+ return annotationType;
+ }
+
+ public int readAnnotationName() {
+ return Leb128Utils.readUnsignedLeb128(in);
}
- public final void readValue() {
- int argAndType = in.readByte() & 0xff;
- int type = argAndType & 0x1f;
- int arg = (argAndType & 0xe0) >> 5;
- int size = arg + 1;
+ public byte readByte() {
+ checkType(ENCODED_BYTE);
+ type = MUST_READ;
+ return (byte) EncodedValueUtils.readSignedInt(in, arg);
+ }
+
+ public short readShort() {
+ checkType(ENCODED_SHORT);
+ type = MUST_READ;
+ return (short) EncodedValueUtils.readSignedInt(in, arg);
+ }
- switch (type) {
+ public char readChar() {
+ checkType(ENCODED_CHAR);
+ type = MUST_READ;
+ return (char) EncodedValueUtils.readUnsignedInt(in, arg, false);
+ }
+
+ public int readInt() {
+ checkType(ENCODED_INT);
+ type = MUST_READ;
+ return EncodedValueUtils.readSignedInt(in, arg);
+ }
+
+ public long readLong() {
+ checkType(ENCODED_LONG);
+ type = MUST_READ;
+ return EncodedValueUtils.readSignedLong(in, arg);
+ }
+
+ public float readFloat() {
+ checkType(ENCODED_FLOAT);
+ type = MUST_READ;
+ return Float.intBitsToFloat(EncodedValueUtils.readUnsignedInt(in, arg, true));
+ }
+
+ public double readDouble() {
+ checkType(ENCODED_DOUBLE);
+ type = MUST_READ;
+ return Double.longBitsToDouble(EncodedValueUtils.readUnsignedLong(in, arg, true));
+ }
+
+ public int readString() {
+ checkType(ENCODED_STRING);
+ type = MUST_READ;
+ return EncodedValueUtils.readUnsignedInt(in, arg, false);
+ }
+
+ public int readType() {
+ checkType(ENCODED_TYPE);
+ type = MUST_READ;
+ return EncodedValueUtils.readUnsignedInt(in, arg, false);
+ }
+
+ public int readField() {
+ checkType(ENCODED_FIELD);
+ type = MUST_READ;
+ return EncodedValueUtils.readUnsignedInt(in, arg, false);
+ }
+
+ public int readEnum() {
+ checkType(ENCODED_ENUM);
+ type = MUST_READ;
+ return EncodedValueUtils.readUnsignedInt(in, arg, false);
+ }
+
+ public int readMethod() {
+ checkType(ENCODED_METHOD);
+ type = MUST_READ;
+ return EncodedValueUtils.readUnsignedInt(in, arg, false);
+ }
+
+ public void readNull() {
+ checkType(ENCODED_NULL);
+ type = MUST_READ;
+ }
+
+ public boolean readBoolean() {
+ checkType(ENCODED_BOOLEAN);
+ type = MUST_READ;
+ return arg != 0;
+ }
+
+ /**
+ * Skips a single value, including its nested values if it is an array or
+ * annotation.
+ */
+ public void skipValue() {
+ switch (peek()) {
case ENCODED_BYTE:
+ readByte();
+ break;
case ENCODED_SHORT:
+ readShort();
+ break;
case ENCODED_CHAR:
+ readChar();
+ break;
case ENCODED_INT:
+ readInt();
+ break;
case ENCODED_LONG:
+ readLong();
+ break;
case ENCODED_FLOAT:
+ readFloat();
+ break;
case ENCODED_DOUBLE:
- visitPrimitive(argAndType, type, arg, size);
+ readDouble();
break;
case ENCODED_STRING:
- visitString(type, readIndex(in, size));
+ readString();
break;
case ENCODED_TYPE:
- visitType(type, readIndex(in, size));
+ readType();
break;
case ENCODED_FIELD:
+ readField();
+ break;
case ENCODED_ENUM:
- visitField(type, readIndex(in, size));
+ readEnum();
break;
case ENCODED_METHOD:
- visitMethod(type, readIndex(in, size));
+ readMethod();
break;
case ENCODED_ARRAY:
- visitArrayValue(argAndType);
- readArray();
+ for (int i = 0, size = readArray(); i < size; i++) {
+ skipValue();
+ }
break;
case ENCODED_ANNOTATION:
- visitAnnotationValue(argAndType);
- readAnnotation();
+ for (int i = 0, size = readAnnotation(); i < size; i++) {
+ readAnnotationName();
+ skipValue();
+ }
break;
case ENCODED_NULL:
- visitEncodedNull(argAndType);
+ readNull();
break;
case ENCODED_BOOLEAN:
- visitEncodedBoolean(argAndType);
+ readBoolean();
break;
+ default:
+ throw new DexException("Unexpected type: " + Integer.toHexString(type));
}
}
- protected void visitArray(int size) {}
- protected void visitAnnotation(int typeIndex, int size) {}
- protected void visitAnnotationName(int nameIndex) {}
- protected void visitPrimitive(int argAndType, int type, int arg, int size) {
- for (int i = 0; i < size; i++) {
- in.readByte();
- }
- }
- protected void visitString(int type, int index) {}
- protected void visitType(int type, int index) {}
- protected void visitField(int type, int index) {}
- protected void visitMethod(int type, int index) {}
- protected void visitArrayValue(int argAndType) {}
- protected void visitAnnotationValue(int argAndType) {}
- protected void visitEncodedBoolean(int argAndType) {}
- protected void visitEncodedNull(int argAndType) {}
-
- private int readIndex(ByteInput in, int byteCount) {
- int result = 0;
- int shift = 0;
- for (int i = 0; i < byteCount; i++) {
- result += (in.readByte() & 0xff) << shift;
- shift += 8;
+ private void checkType(int expected) {
+ if (peek() != expected) {
+ throw new IllegalStateException("Expected array but was "
+ + Integer.toHexString(peek()));
}
- return result;
}
}
diff --git a/dx/src/com/android/dx/merge/IndexMap.java b/dx/src/com/android/dx/merge/IndexMap.java
index 89d2dd671..0a870cfae 100644
--- a/dx/src/com/android/dx/merge/IndexMap.java
+++ b/dx/src/com/android/dx/merge/IndexMap.java
@@ -22,14 +22,15 @@ import com.android.dx.io.ClassDef;
import com.android.dx.io.DexBuffer;
import com.android.dx.io.EncodedValue;
import com.android.dx.io.EncodedValueReader;
+import static com.android.dx.io.EncodedValueReader.*;
import com.android.dx.io.FieldId;
import com.android.dx.io.MethodId;
import com.android.dx.io.ProtoId;
import com.android.dx.util.ByteArrayAnnotatedOutput;
-import com.android.dx.util.ByteInput;
import com.android.dx.util.ByteOutput;
+import com.android.dx.util.DexException;
+import com.android.dx.util.EncodedValueUtils;
import com.android.dx.util.Leb128Utils;
-import com.android.dx.util.Unsigned;
import java.util.HashMap;
/**
@@ -208,13 +209,14 @@ public final class IndexMap {
public EncodedValue adjustEncodedValue(EncodedValue encodedValue) {
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32);
- new EncodedValueTransformer(encodedValue, out).readValue();
+ new EncodedValueTransformer(out).transform(new EncodedValueReader(encodedValue));
return new EncodedValue(out.toByteArray());
}
public EncodedValue adjustEncodedArray(EncodedValue encodedArray) {
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32);
- new EncodedValueTransformer(encodedArray, out).readArray();
+ new EncodedValueTransformer(out).transformArray(
+ new EncodedValueReader(encodedArray.asByteInput(), ENCODED_ARRAY));
return new EncodedValue(out.toByteArray());
}
@@ -232,88 +234,101 @@ public final class IndexMap {
/**
* Adjust an encoded value or array.
*/
- private final class EncodedValueTransformer extends EncodedValueReader {
+ private final class EncodedValueTransformer {
private final ByteOutput out;
- public EncodedValueTransformer(EncodedValue encodedValue, ByteOutput out) {
- super(encodedValue);
+ public EncodedValueTransformer(ByteOutput out) {
this.out = out;
}
- protected void visitArray(int size) {
- Leb128Utils.writeUnsignedLeb128(out, size);
- }
-
- protected void visitAnnotation(int typeIndex, int size) {
- Leb128Utils.writeUnsignedLeb128(out, adjustType(typeIndex));
- Leb128Utils.writeUnsignedLeb128(out, size);
- }
-
- protected void visitAnnotationName(int index) {
- Leb128Utils.writeUnsignedLeb128(out, adjustString(index));
- }
-
- protected void visitPrimitive(int argAndType, int type, int arg, int size) {
- out.writeByte(argAndType);
- copyBytes(in, out, size);
- }
-
- protected void visitString(int type, int index) {
- writeTypeAndSizeAndIndex(type, adjustString(index));
- }
-
- protected void visitType(int type, int index) {
- writeTypeAndSizeAndIndex(type, adjustType(index));
- }
-
- protected void visitField(int type, int index) {
- writeTypeAndSizeAndIndex(type, adjustField(index));
- }
-
- protected void visitMethod(int type, int index) {
- writeTypeAndSizeAndIndex(type, adjustMethod(index));
- }
-
- protected void visitArrayValue(int argAndType) {
- out.writeByte(argAndType);
- }
-
- protected void visitAnnotationValue(int argAndType) {
- out.writeByte(argAndType);
- }
-
- protected void visitEncodedBoolean(int argAndType) {
- out.writeByte(argAndType);
- }
-
- protected void visitEncodedNull(int argAndType) {
- out.writeByte(argAndType);
- }
-
- private void writeTypeAndSizeAndIndex(int type, int index) {
- int byteCount;
- if (Unsigned.compare(index, 0xff) <= 0) {
- byteCount = 1;
- } else if (Unsigned.compare(index, 0xffff) <= 0) {
- byteCount = 2;
- } else if (Unsigned.compare(index, 0xffffff) <= 0) {
- byteCount = 3;
- } else {
- byteCount = 4;
+ public void transform(EncodedValueReader reader) {
+ // TODO: extract this into a helper class, EncodedValueWriter
+ switch (reader.peek()) {
+ case ENCODED_BYTE:
+ EncodedValueUtils.writeSignedIntegralValue(out, ENCODED_BYTE, reader.readByte());
+ break;
+ case ENCODED_SHORT:
+ EncodedValueUtils.writeSignedIntegralValue(out, ENCODED_SHORT, reader.readShort());
+ break;
+ case ENCODED_INT:
+ EncodedValueUtils.writeSignedIntegralValue(out, ENCODED_INT, reader.readInt());
+ break;
+ case ENCODED_LONG:
+ EncodedValueUtils.writeSignedIntegralValue(out, ENCODED_LONG, reader.readLong());
+ break;
+ case ENCODED_CHAR:
+ EncodedValueUtils.writeUnsignedIntegralValue(out, ENCODED_CHAR, reader.readChar());
+ break;
+ case ENCODED_FLOAT:
+ // Shift value left 32 so that right-zero-extension works.
+ long longBits = ((long) Float.floatToIntBits(reader.readFloat())) << 32;
+ EncodedValueUtils.writeRightZeroExtendedValue(out, ENCODED_FLOAT, longBits);
+ break;
+ case ENCODED_DOUBLE:
+ EncodedValueUtils.writeRightZeroExtendedValue(
+ out, ENCODED_DOUBLE, Double.doubleToLongBits(reader.readDouble()));
+ break;
+ case ENCODED_STRING:
+ EncodedValueUtils.writeUnsignedIntegralValue(
+ out, ENCODED_STRING, adjustString(reader.readString()));
+ break;
+ case ENCODED_TYPE:
+ EncodedValueUtils.writeUnsignedIntegralValue(
+ out, ENCODED_TYPE, adjustType(reader.readType()));
+ break;
+ case ENCODED_FIELD:
+ EncodedValueUtils.writeUnsignedIntegralValue(
+ out, ENCODED_FIELD, adjustField(reader.readField()));
+ break;
+ case ENCODED_ENUM:
+ EncodedValueUtils.writeUnsignedIntegralValue(
+ out, ENCODED_ENUM, adjustField(reader.readEnum()));
+ break;
+ case ENCODED_METHOD:
+ EncodedValueUtils.writeUnsignedIntegralValue(
+ out, ENCODED_METHOD, adjustMethod(reader.readMethod()));
+ break;
+ case ENCODED_ARRAY:
+ writeTypeAndArg(ENCODED_ARRAY, 0);
+ transformArray(reader);
+ break;
+ case ENCODED_ANNOTATION:
+ writeTypeAndArg(ENCODED_ANNOTATION, 0);
+ transformAnnotation(reader);
+ break;
+ case ENCODED_NULL:
+ reader.readNull();
+ writeTypeAndArg(ENCODED_NULL, 0);
+ break;
+ case ENCODED_BOOLEAN:
+ boolean value = reader.readBoolean();
+ writeTypeAndArg(ENCODED_BOOLEAN, value ? 1 : 0);
+ break;
+ default:
+ throw new DexException("Unexpected type: " + Integer.toHexString(reader.peek()));
}
- int argAndType = ((byteCount - 1) << 5) | type;
- out.writeByte(argAndType);
+ }
- for (int i = 0; i < byteCount; i++) {
- out.writeByte(index & 0xff);
- index >>>= 8;
+ private void transformAnnotation(EncodedValueReader reader) {
+ int fieldCount = reader.readAnnotation();
+ Leb128Utils.writeUnsignedLeb128(out, adjustType(reader.getAnnotationType()));
+ Leb128Utils.writeUnsignedLeb128(out, fieldCount);
+ for (int i = 0; i < fieldCount; i++) {
+ Leb128Utils.writeUnsignedLeb128(out, adjustString(reader.readAnnotationName()));
+ transform(reader);
}
}
- private void copyBytes(ByteInput in, ByteOutput out, int size) {
+ private void transformArray(EncodedValueReader reader) {
+ int size = reader.readArray();
+ Leb128Utils.writeUnsignedLeb128(out, size);
for (int i = 0; i < size; i++) {
- out.writeByte(in.readByte());
+ transform(reader);
}
}
+
+ private void writeTypeAndArg(int type, int arg) {
+ out.writeByte((arg << 5) | type);
+ }
}
}
diff --git a/dx/src/com/android/dx/util/EncodedValueUtils.java b/dx/src/com/android/dx/util/EncodedValueUtils.java
new file mode 100644
index 000000000..bb92389b7
--- /dev/null
+++ b/dx/src/com/android/dx/util/EncodedValueUtils.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed 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 com.android.dx.util;
+
+/**
+ * Helpers for {@code encoded_values} and parts thereof.
+ */
+public final class EncodedValueUtils {
+ private EncodedValueUtils() {
+ }
+
+ /**
+ * Writes a signed integral to {@code out}.
+ */
+ public static void writeSignedIntegralValue(ByteOutput out, int type, long value) {
+ /*
+ * Figure out how many bits are needed to represent the value,
+ * including a sign bit: The bit count is subtracted from 65
+ * and not 64 to account for the sign bit. The xor operation
+ * has the effect of leaving non-negative values alone and
+ * unary complementing negative values (so that a leading zero
+ * count always returns a useful number for our present
+ * purpose).
+ */
+ int requiredBits = 65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
+
+ // Round up the requiredBits to a number of bytes.
+ int requiredBytes = (requiredBits + 0x07) >> 3;
+
+ /*
+ * Write the header byte, which includes the type and
+ * requiredBytes - 1.
+ */
+ out.writeByte(type | ((requiredBytes - 1) << 5));
+
+ // Write the value, per se.
+ while (requiredBytes > 0) {
+ out.writeByte((byte) value);
+ value >>= 8;
+ requiredBytes--;
+ }
+ }
+
+ /**
+ * Writes an unsigned integral to {@code out}.
+ */
+ public static void writeUnsignedIntegralValue(ByteOutput out, int type, long value) {
+ // Figure out how many bits are needed to represent the value.
+ int requiredBits = 64 - Long.numberOfLeadingZeros(value);
+ if (requiredBits == 0) {
+ requiredBits = 1;
+ }
+
+ // Round up the requiredBits to a number of bytes.
+ int requiredBytes = (requiredBits + 0x07) >> 3;
+
+ /*
+ * Write the header byte, which includes the type and
+ * requiredBytes - 1.
+ */
+ out.writeByte(type | ((requiredBytes - 1) << 5));
+
+ // Write the value, per se.
+ while (requiredBytes > 0) {
+ out.writeByte((byte) value);
+ value >>= 8;
+ requiredBytes--;
+ }
+ }
+
+ /**
+ * Writes a right-zero-extended value to {@code out}.
+ */
+ public static void writeRightZeroExtendedValue(ByteOutput out, int type, long value) {
+ // Figure out how many bits are needed to represent the value.
+ int requiredBits = 64 - Long.numberOfTrailingZeros(value);
+ if (requiredBits == 0) {
+ requiredBits = 1;
+ }
+
+ // Round up the requiredBits to a number of bytes.
+ int requiredBytes = (requiredBits + 0x07) >> 3;
+
+ // Scootch the first bits to be written down to the low-order bits.
+ value >>= 64 - (requiredBytes * 8);
+
+ /*
+ * Write the header byte, which includes the type and
+ * requiredBytes - 1.
+ */
+ out.writeByte(type | ((requiredBytes - 1) << 5));
+
+ // Write the value, per se.
+ while (requiredBytes > 0) {
+ out.writeByte((byte) value);
+ value >>= 8;
+ requiredBytes--;
+ }
+ }
+
+ /**
+ * Read a signed integer.
+ *
+ * @param zwidth byte count minus one
+ */
+ public static int readSignedInt(ByteInput in, int zwidth) {
+ int result = 0;
+ for (int i = zwidth; i >= 0; i--) {
+ result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
+ }
+ result >>= (3 - zwidth) * 8;
+ return result;
+ }
+
+ /**
+ * Read an unsigned integer.
+ *
+ * @param zwidth byte count minus one
+ * @param fillOnRight true to zero fill on the right; false on the left
+ */
+ public static int readUnsignedInt(ByteInput in, int zwidth, boolean fillOnRight) {
+ int result = 0;
+ if (!fillOnRight) {
+ for (int i = zwidth; i >= 0; i--) {
+ result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
+ }
+ result >>>= (3 - zwidth) * 8;
+ } else {
+ for (int i = zwidth; i >= 0; i--) {
+ result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Read a signed long.
+ *
+ * @param zwidth byte count minus one
+ */
+ public static long readSignedLong(ByteInput in, int zwidth) {
+ long result = 0;
+ for (int i = zwidth; i >= 0; i--) {
+ result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
+ }
+ result >>= (7 - zwidth) * 8;
+ return result;
+ }
+
+ /**
+ * Read an unsigned long.
+ *
+ * @param zwidth byte count minus one
+ * @param fillOnRight true to zero fill on the right; false on the left
+ */
+ public static long readUnsignedLong(ByteInput in, int zwidth, boolean fillOnRight) {
+ long result = 0;
+ if (!fillOnRight) {
+ for (int i = zwidth; i >= 0; i--) {
+ result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
+ }
+ result >>>= (7 - zwidth) * 8;
+ } else {
+ for (int i = zwidth; i >= 0; i--) {
+ result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
+ }
+ }
+ return result;
+ }
+}