summaryrefslogtreecommitdiffstats
path: root/dx
diff options
context:
space:
mode:
authorJesse Wilson <jessewilson@google.com>2011-03-16 14:22:51 -0700
committerJesse Wilson <jessewilson@google.com>2011-03-16 17:47:38 -0700
commitbd3dba4346223593ac6033a3d2a7d8ec6f20738b (patch)
tree173c2479689385298ff6fd1e05038d5f784e1387 /dx
parent1b4822ecb93021e6273c92093f78dcb6ccc0cf91 (diff)
downloadandroid_dalvik-bd3dba4346223593ac6033a3d2a7d8ec6f20738b.tar.gz
android_dalvik-bd3dba4346223593ac6033a3d2a7d8ec6f20738b.tar.bz2
android_dalvik-bd3dba4346223593ac6033a3d2a7d8ec6f20738b.zip
Don't emit multiple copies of annotations when merging dex files.
This change requires all annotations to be loaded into memory so they can be sorted. There does not appear to be a required order to the annotations in .dex files. Loading annotations also requires loading encoded values, which makes this into a large refactoring. Change-Id: Ib7e2656c595018be4e9936eb84a22f1c1de56750 http://b/4090053
Diffstat (limited to 'dx')
-rw-r--r--dx/src/com/android/dx/command/findusages/FindUsages.java4
-rw-r--r--dx/src/com/android/dx/dex/file/DebugInfoDecoder.java10
-rw-r--r--dx/src/com/android/dx/io/Annotation.java104
-rw-r--r--dx/src/com/android/dx/io/Code.java24
-rw-r--r--dx/src/com/android/dx/io/DexBuffer.java155
-rw-r--r--dx/src/com/android/dx/io/EncodedValue.java57
-rw-r--r--dx/src/com/android/dx/io/EncodedValueReader.java21
-rw-r--r--dx/src/com/android/dx/io/FieldId.java14
-rw-r--r--dx/src/com/android/dx/io/MethodId.java14
-rw-r--r--dx/src/com/android/dx/merge/DexMerger.java81
-rw-r--r--dx/src/com/android/dx/merge/EncodedValueTransformer.java108
-rw-r--r--dx/src/com/android/dx/merge/IndexMap.java156
-rw-r--r--dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java25
-rw-r--r--dx/src/com/android/dx/util/ByteArrayByteInput.java31
-rw-r--r--dx/src/com/android/dx/util/ByteInput.java30
-rw-r--r--dx/src/com/android/dx/util/ByteOutput.java30
-rw-r--r--dx/src/com/android/dx/util/Leb128Utils.java32
-rw-r--r--dx/src/com/android/dx/util/Mutf8.java4
-rw-r--r--dx/src/com/android/dx/util/Output.java2
-rw-r--r--dx/tests/116-leb128/com/android/dx/util/Leb128UtilsTest.java50
-rw-r--r--dx/tests/117-modified-utf8/com/android/dx/util/Mutf8Test.java22
21 files changed, 608 insertions, 366 deletions
diff --git a/dx/src/com/android/dx/command/findusages/FindUsages.java b/dx/src/com/android/dx/command/findusages/FindUsages.java
index 1c692ae87..e31f9bb1b 100644
--- a/dx/src/com/android/dx/command/findusages/FindUsages.java
+++ b/dx/src/com/android/dx/command/findusages/FindUsages.java
@@ -141,7 +141,7 @@ public final class FindUsages {
int fieldIndex = 0;
for (FieldId fieldId : dex.fieldIds()) {
if (fieldId.getNameIndex() == memberNameIndex
- && declaringType == (int) fieldId.getDeclaringClassIndex()) {
+ && declaringType == fieldId.getDeclaringClassIndex()) {
fields.add(fieldIndex);
}
fieldIndex++;
@@ -160,7 +160,7 @@ public final class FindUsages {
int methodIndex = 0;
for (MethodId method : dex.methodIds()) {
if (method.getNameIndex() == memberNameIndex
- && subtypes.contains((int) method.getDeclaringClassIndex())) {
+ && subtypes.contains(method.getDeclaringClassIndex())) {
methods.add(methodIndex);
}
methodIndex++;
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
index ee275cebb..781caec91 100644
--- a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
+++ b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
@@ -25,12 +25,11 @@ import com.android.dx.rop.cst.CstUtf8;
import com.android.dx.rop.type.Prototype;
import com.android.dx.rop.type.StdTypeList;
import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArrayByteInput;
+import com.android.dx.util.ByteInput;
import com.android.dx.util.ExceptionWithContext;
import com.android.dx.util.Leb128Utils;
-import java.io.ByteArrayInputStream;
-import java.io.DataInput;
-import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -216,11 +215,10 @@ public class DebugInfoDecoder {
* Reads a string index. String indicies are offset by 1, and a 0 value
* in the stream (-1 as returned by this method) means "null"
*
- * @param bs
* @return index into file's string ids table, -1 means null
* @throws IOException
*/
- private int readStringIndex(DataInput bs) throws IOException {
+ private int readStringIndex(ByteInput bs) throws IOException {
int offsetIndex = Leb128Utils.readUnsignedLeb128(bs);
return offsetIndex - 1;
@@ -239,7 +237,7 @@ public class DebugInfoDecoder {
}
private void decode0() throws IOException {
- DataInput bs = new DataInputStream(new ByteArrayInputStream(encoded));
+ ByteInput bs = new ByteArrayByteInput(encoded);
line = Leb128Utils.readUnsignedLeb128(bs);
int szParams = Leb128Utils.readUnsignedLeb128(bs);
diff --git a/dx/src/com/android/dx/io/Annotation.java b/dx/src/com/android/dx/io/Annotation.java
new file mode 100644
index 000000000..d3be59271
--- /dev/null
+++ b/dx/src/com/android/dx/io/Annotation.java
@@ -0,0 +1,104 @@
+/*
+ * 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.Unsigned;
+
+/**
+ * An annotation.
+ */
+public final class Annotation implements Comparable<Annotation> {
+ private final DexBuffer buffer;
+ private final byte visibility;
+ private final int typeIndex;
+ private final int[] names;
+ private final EncodedValue[] values;
+
+ public Annotation(DexBuffer buffer, byte visibility, int typeIndex, int[] names,
+ EncodedValue[] values) {
+ this.buffer = buffer;
+ this.visibility = visibility;
+ this.typeIndex = typeIndex;
+ this.names = names;
+ this.values = values;
+ }
+
+ public byte getVisibility() {
+ return visibility;
+ }
+
+ public int getTypeIndex() {
+ return typeIndex;
+ }
+
+ public int[] getNames() {
+ return names;
+ }
+
+ public EncodedValue[] getValues() {
+ return values;
+ }
+
+ public void writeTo(DexBuffer.Section out) {
+ out.writeByte(visibility);
+ out.writeUleb128(typeIndex);
+ out.writeUleb128(names.length);
+ for (int i = 0; i < names.length; i++) {
+ out.writeUleb128(names[i]);
+ values[i].writeTo(out);
+ }
+ }
+
+ @Override public int compareTo(Annotation other) {
+ if (typeIndex != other.typeIndex) {
+ return Unsigned.compare(typeIndex, other.typeIndex);
+ }
+ int size = Math.min(names.length, other.names.length);
+ for (int i = 0; i < size; i++) {
+ if (names[i] != other.names[i]) {
+ return Unsigned.compare(names[i], other.names[i]);
+ }
+ int compare = values[i].compareTo(other.values[i]);
+ if (compare != 0) {
+ return compare;
+ }
+ }
+ return names.length - other.names.length;
+ }
+
+ @Override public String toString() {
+ if (buffer == null) {
+ return visibility + " " + typeIndex;
+ }
+
+ StringBuilder result = new StringBuilder();
+ result.append(visibility);
+ result.append(" ");
+ result.append(buffer.typeNames().get(typeIndex));
+ result.append("[");
+ for (int i = 0; i < names.length; i++) {
+ if (i > 0) {
+ result.append(", ");
+ }
+ result.append(buffer.strings().get(names[i]));
+ result.append("=");
+ result.append(values[i]);
+ }
+ result.append("]");
+ return result.toString();
+ }
+}
diff --git a/dx/src/com/android/dx/io/Code.java b/dx/src/com/android/dx/io/Code.java
index 81073f3ba..ba95d1b92 100644
--- a/dx/src/com/android/dx/io/Code.java
+++ b/dx/src/com/android/dx/io/Code.java
@@ -17,15 +17,15 @@
package com.android.dx.io;
public final class Code {
- private final short registersSize;
- private final short insSize;
- private final short outsSize;
+ private final int registersSize;
+ private final int insSize;
+ private final int outsSize;
private final int debugInfoOffset;
private final short[] instructions;
private final Try[] tries;
private final CatchHandler[] catchHandlers;
- public Code(short registersSize, short insSize, short outsSize, int debugInfoOffset,
+ public Code(int registersSize, int insSize, int outsSize, int debugInfoOffset,
short[] instructions, Try[] tries, CatchHandler[] catchHandlers) {
this.registersSize = registersSize;
this.insSize = insSize;
@@ -36,15 +36,15 @@ public final class Code {
this.catchHandlers = catchHandlers;
}
- public short getRegistersSize() {
+ public int getRegistersSize() {
return registersSize;
}
- public short getInsSize() {
+ public int getInsSize() {
return insSize;
}
- public short getOutsSize() {
+ public int getOutsSize() {
return outsSize;
}
@@ -66,10 +66,10 @@ public final class Code {
public static class Try {
final int startAddress;
- final short instructionCount;
- final short handlerOffset;
+ final int instructionCount;
+ final int handlerOffset;
- Try(int startAddress, short instructionCount, short handlerOffset) {
+ Try(int startAddress, int instructionCount, int handlerOffset) {
this.startAddress = startAddress;
this.instructionCount = instructionCount;
this.handlerOffset = handlerOffset;
@@ -79,11 +79,11 @@ public final class Code {
return startAddress;
}
- public short getInstructionCount() {
+ public int getInstructionCount() {
return instructionCount;
}
- public short getHandlerOffset() {
+ public int getHandlerOffset() {
return handlerOffset;
}
}
diff --git a/dx/src/com/android/dx/io/DexBuffer.java b/dx/src/com/android/dx/io/DexBuffer.java
index cc511d6f9..8b9facf40 100644
--- a/dx/src/com/android/dx/io/DexBuffer.java
+++ b/dx/src/com/android/dx/io/DexBuffer.java
@@ -19,17 +19,19 @@ package com.android.dx.io;
import com.android.dx.dex.SizeOf;
import com.android.dx.dex.TableOfContents;
import com.android.dx.merge.TypeList;
+import com.android.dx.util.ByteInput;
+import com.android.dx.util.ByteOutput;
import com.android.dx.util.DexException;
import com.android.dx.util.Leb128Utils;
import com.android.dx.util.Mutf8;
import java.io.ByteArrayOutputStream;
-import java.io.DataInput;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.UTFDataFormatException;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collections;
@@ -159,7 +161,7 @@ public final class DexBuffer {
public Section appendSection(int maxByteCount, String name) {
Section result = new Section(name, length, length + maxByteCount);
- length = fourByteAlign(length + maxByteCount);
+ length += fourByteAlign(maxByteCount);
return result;
}
@@ -254,17 +256,11 @@ public final class DexBuffer {
return open(offset).readCode();
}
- public final class Section {
+ public final class Section implements ByteInput, ByteOutput {
private final String name;
private int position;
private final int limit;
- private final DataInput asDataInput = new DataInputStub() {
- public byte readByte() {
- return Section.this.readByte();
- }
- };
-
private Section(String name, int position, int limit) {
this.name = name;
this.position = position;
@@ -295,6 +291,10 @@ public final class DexBuffer {
return (short) result;
}
+ public int readUnsignedShort() {
+ return readShort() & 0xffff;
+ }
+
public byte readByte() {
return (byte) (data[position++] & 0xff);
}
@@ -314,19 +314,11 @@ public final class DexBuffer {
}
public int readUleb128() {
- try {
- return Leb128Utils.readUnsignedLeb128(asDataInput);
- } catch (IOException e) {
- throw new DexException(e);
- }
+ return Leb128Utils.readUnsignedLeb128(this);
}
public int readSleb128() {
- try {
- return Leb128Utils.readSignedLeb128(asDataInput);
- } catch (IOException e) {
- throw new DexException(e);
- }
+ return Leb128Utils.readSignedLeb128(this);
}
public TypeList readTypeList() {
@@ -345,13 +337,13 @@ public final class DexBuffer {
position = offset;
try {
int expectedLength = readUleb128();
- String result = Mutf8.decode(asDataInput, new char[expectedLength]);
+ String result = Mutf8.decode(this, new char[expectedLength]);
if (result.length() != expectedLength) {
throw new DexException("Declared length " + expectedLength
+ " doesn't match decoded length of " + result.length());
}
return result;
- } catch (IOException e) {
+ } catch (UTFDataFormatException e) {
throw new DexException(e);
} finally {
position = savedPosition;
@@ -359,15 +351,15 @@ public final class DexBuffer {
}
public FieldId readFieldId() {
- short declaringClassIndex = readShort();
- short typeIndex = readShort();
+ int declaringClassIndex = readUnsignedShort();
+ int typeIndex = readUnsignedShort();
int nameIndex = readInt();
return new FieldId(DexBuffer.this, declaringClassIndex, typeIndex, nameIndex);
}
public MethodId readMethodId() {
- short declaringClassIndex = readShort();
- short protoIndex = readShort();
+ int declaringClassIndex = readUnsignedShort();
+ int protoIndex = readUnsignedShort();
int nameIndex = readInt();
return new MethodId(DexBuffer.this, declaringClassIndex, protoIndex, nameIndex);
}
@@ -395,10 +387,10 @@ public final class DexBuffer {
}
private Code readCode() {
- short registersSize = readShort();
- short insSize = readShort();
- short outsSize = readShort();
- short triesSize = readShort();
+ int registersSize = readUnsignedShort();
+ int insSize = readUnsignedShort();
+ int outsSize = readUnsignedShort();
+ int triesSize = readUnsignedShort();
int debugInfoOffset = readInt();
int instructionsSize = readInt();
short[] instructions = readShortArray(instructionsSize);
@@ -411,8 +403,8 @@ public final class DexBuffer {
for (int i = 0; i < triesSize; i++) {
int startAddress = readInt();
- short instructionCount = readShort();
- short handlerOffset = readShort();
+ int instructionCount = readUnsignedShort();
+ int handlerOffset = readUnsignedShort();
tries[i] = new Code.Try(startAddress, instructionCount, handlerOffset);
}
@@ -474,6 +466,33 @@ public final class DexBuffer {
return result;
}
+ public Annotation readAnnotation() {
+ byte visibility = readByte();
+ int typeIndex = readUleb128();
+ int size = readUleb128();
+ int[] names = new int[size];
+ EncodedValue[] values = new EncodedValue[size];
+ for (int i = 0; i < size; i++) {
+ names[i] = readUleb128();
+ values[i] = readEncodedValue();
+ }
+ return new Annotation(DexBuffer.this, visibility, typeIndex, names, values);
+ }
+
+ public EncodedValue readEncodedValue() {
+ int start = position;
+ new EncodedValueReader(this).readValue();
+ int end = position;
+ return new EncodedValue(Arrays.copyOfRange(data, start, end));
+ }
+
+ public EncodedValue readEncodedArray() {
+ int start = position;
+ new EncodedValueReader(this).readArray();
+ int end = position;
+ return new EncodedValue(Arrays.copyOfRange(data, start, end));
+ }
+
private void ensureCapacity(int size) {
if (position + size > limit) {
throw new DexException("Section limit " + limit + " exceeded by " + name);
@@ -515,6 +534,14 @@ public final class DexBuffer {
position += 2;
}
+ public void writeUnsignedShort(int i) {
+ short s = (short) i;
+ if (i != (s & 0xffff)) {
+ throw new IllegalArgumentException("Expected an unsigned short: " + i);
+ }
+ writeShort(s);
+ }
+
public void write(short[] shorts) {
for (short s : shorts) {
writeShort(s);
@@ -531,13 +558,21 @@ public final class DexBuffer {
}
public void writeUleb128(int i) {
- position += Leb128Utils.writeUnsignedLeb128(data, position, i);
- ensureCapacity(0);
+ try {
+ Leb128Utils.writeUnsignedLeb128(this, i);
+ ensureCapacity(0);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new DexException("Section limit " + limit + " exceeded by " + name);
+ }
}
public void writeSleb128(int i) {
- position += Leb128Utils.writeSignedLeb128(data, position, i);
- ensureCapacity(0);
+ try {
+ Leb128Utils.writeSignedLeb128(this, i);
+ ensureCapacity(0);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new DexException("Section limit " + limit + " exceeded by " + name);
+ }
}
public void writeStringData(String value) {
@@ -546,7 +581,7 @@ public final class DexBuffer {
writeUleb128(length);
write(Mutf8.encode(value));
writeByte(0);
- } catch (IOException e) {
+ } catch (UTFDataFormatException e) {
throw new AssertionError();
}
}
@@ -567,52 +602,4 @@ public final class DexBuffer {
return limit - position;
}
}
-
- private static class DataInputStub implements DataInput {
- public byte readByte() throws IOException {
- throw new UnsupportedOperationException();
- }
- public void readFully(byte[] buffer) throws IOException {
- throw new UnsupportedOperationException();
- }
- public void readFully(byte[] buffer, int offset, int count) throws IOException {
- throw new UnsupportedOperationException();
- }
- public int skipBytes(int i) throws IOException {
- throw new UnsupportedOperationException();
- }
- public boolean readBoolean() throws IOException {
- throw new UnsupportedOperationException();
- }
- public int readUnsignedByte() throws IOException {
- throw new UnsupportedOperationException();
- }
- public short readShort() throws IOException {
- throw new UnsupportedOperationException();
- }
- public int readUnsignedShort() throws IOException {
- throw new UnsupportedOperationException();
- }
- public char readChar() throws IOException {
- throw new UnsupportedOperationException();
- }
- public int readInt() throws IOException {
- throw new UnsupportedOperationException();
- }
- public long readLong() throws IOException {
- throw new UnsupportedOperationException();
- }
- public float readFloat() throws IOException {
- throw new UnsupportedOperationException();
- }
- public double readDouble() throws IOException {
- throw new UnsupportedOperationException();
- }
- public String readLine() throws IOException {
- throw new UnsupportedOperationException();
- }
- public String readUTF() throws IOException {
- throw new UnsupportedOperationException();
- }
- }
}
diff --git a/dx/src/com/android/dx/io/EncodedValue.java b/dx/src/com/android/dx/io/EncodedValue.java
new file mode 100644
index 000000000..c83c36448
--- /dev/null
+++ b/dx/src/com/android/dx/io/EncodedValue.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.android.dx.util.ByteInput;
+
+/**
+ * An encoded value or array.
+ */
+public final class EncodedValue implements Comparable<EncodedValue> {
+ private final byte[] data;
+
+ public EncodedValue(byte[] data) {
+ this.data = data;
+ }
+
+ public ByteInput asByteInput() {
+ return new ByteArrayByteInput(data);
+ }
+
+ public byte[] getBytes() {
+ return data;
+ }
+
+ public void writeTo(DexBuffer.Section out) {
+ out.write(data);
+ }
+
+ @Override public int compareTo(EncodedValue other) {
+ int size = Math.min(data.length, other.data.length);
+ for (int i = 0; i < size; i++) {
+ if (data[i] != other.data[i]) {
+ return (data[i] & 0xff) - (other.data[i] & 0xff);
+ }
+ }
+ return data.length - other.data.length;
+ }
+
+ @Override public String toString() {
+ return Integer.toHexString(data[0] & 0xff) + "...(" + data.length + ")";
+ }
+}
diff --git a/dx/src/com/android/dx/io/EncodedValueReader.java b/dx/src/com/android/dx/io/EncodedValueReader.java
index 6c913a15c..ec84898ba 100644
--- a/dx/src/com/android/dx/io/EncodedValueReader.java
+++ b/dx/src/com/android/dx/io/EncodedValueReader.java
@@ -16,6 +16,9 @@
package com.android.dx.io;
+import com.android.dx.util.ByteInput;
+import com.android.dx.util.Leb128Utils;
+
/**
* SAX-style reader for encoded values.
* TODO: convert this to a pull-style reader
@@ -38,14 +41,18 @@ public class EncodedValueReader {
public static final int ENCODED_NULL = 0x1e;
public static final int ENCODED_BOOLEAN = 0x1f;
- protected final DexBuffer.Section in;
+ protected final ByteInput in;
- public EncodedValueReader(DexBuffer.Section in) {
+ public EncodedValueReader(ByteInput in) {
this.in = in;
}
+ public EncodedValueReader(EncodedValue in) {
+ this(in.asByteInput());
+ }
+
public final void readArray() {
- int size = in.readUleb128();
+ int size = Leb128Utils.readUnsignedLeb128(in);
visitArray(size);
for (int i = 0; i < size; i++) {
@@ -54,12 +61,12 @@ public class EncodedValueReader {
}
public final void readAnnotation() {
- int typeIndex = in.readUleb128();
- int size = in.readUleb128();
+ int typeIndex = Leb128Utils.readUnsignedLeb128(in);
+ int size = Leb128Utils.readUnsignedLeb128(in);
visitAnnotation(typeIndex, size);
for (int i = 0; i < size; i++) {
- visitAnnotationName(in.readUleb128());
+ visitAnnotationName(Leb128Utils.readUnsignedLeb128(in));
readValue();
}
}
@@ -127,7 +134,7 @@ public class EncodedValueReader {
protected void visitEncodedBoolean(int argAndType) {}
protected void visitEncodedNull(int argAndType) {}
- private int readIndex(DexBuffer.Section in, int byteCount) {
+ private int readIndex(ByteInput in, int byteCount) {
int result = 0;
int shift = 0;
for (int i = 0; i < byteCount; i++) {
diff --git a/dx/src/com/android/dx/io/FieldId.java b/dx/src/com/android/dx/io/FieldId.java
index ab481e089..712675fdc 100644
--- a/dx/src/com/android/dx/io/FieldId.java
+++ b/dx/src/com/android/dx/io/FieldId.java
@@ -20,22 +20,22 @@ import com.android.dx.util.Unsigned;
public final class FieldId implements Comparable<FieldId> {
private final DexBuffer buffer;
- private final short declaringClassIndex;
- private final short typeIndex;
+ private final int declaringClassIndex;
+ private final int typeIndex;
private final int nameIndex;
- public FieldId(DexBuffer buffer, short declaringClassIndex, short typeIndex, int nameIndex) {
+ public FieldId(DexBuffer buffer, int declaringClassIndex, int typeIndex, int nameIndex) {
this.buffer = buffer;
this.declaringClassIndex = declaringClassIndex;
this.typeIndex = typeIndex;
this.nameIndex = nameIndex;
}
- public short getDeclaringClassIndex() {
+ public int getDeclaringClassIndex() {
return declaringClassIndex;
}
- public short getTypeIndex() {
+ public int getTypeIndex() {
return typeIndex;
}
@@ -54,8 +54,8 @@ public final class FieldId implements Comparable<FieldId> {
}
public void writeTo(DexBuffer.Section out) {
- out.writeShort(declaringClassIndex);
- out.writeShort(typeIndex);
+ out.writeUnsignedShort(declaringClassIndex);
+ out.writeUnsignedShort(typeIndex);
out.writeInt(nameIndex);
}
diff --git a/dx/src/com/android/dx/io/MethodId.java b/dx/src/com/android/dx/io/MethodId.java
index 2934497a3..614e97f28 100644
--- a/dx/src/com/android/dx/io/MethodId.java
+++ b/dx/src/com/android/dx/io/MethodId.java
@@ -20,22 +20,22 @@ import com.android.dx.util.Unsigned;
public final class MethodId implements Comparable<MethodId> {
private final DexBuffer buffer;
- private final short declaringClassIndex;
- private final short protoIndex;
+ private final int declaringClassIndex;
+ private final int protoIndex;
private final int nameIndex;
- public MethodId(DexBuffer buffer, short declaringClassIndex, short protoIndex, int nameIndex) {
+ public MethodId(DexBuffer buffer, int declaringClassIndex, int protoIndex, int nameIndex) {
this.buffer = buffer;
this.declaringClassIndex = declaringClassIndex;
this.protoIndex = protoIndex;
this.nameIndex = nameIndex;
}
- public short getDeclaringClassIndex() {
+ public int getDeclaringClassIndex() {
return declaringClassIndex;
}
- public short getProtoIndex() {
+ public int getProtoIndex() {
return protoIndex;
}
@@ -54,8 +54,8 @@ public final class MethodId implements Comparable<MethodId> {
}
public void writeTo(DexBuffer.Section out) {
- out.writeShort(declaringClassIndex);
- out.writeShort(protoIndex);
+ out.writeUnsignedShort(declaringClassIndex);
+ out.writeUnsignedShort(protoIndex);
out.writeInt(nameIndex);
}
diff --git a/dx/src/com/android/dx/merge/DexMerger.java b/dx/src/com/android/dx/merge/DexMerger.java
index 475670946..8d1415e80 100644
--- a/dx/src/com/android/dx/merge/DexMerger.java
+++ b/dx/src/com/android/dx/merge/DexMerger.java
@@ -18,6 +18,7 @@ package com.android.dx.merge;
import com.android.dx.dex.SizeOf;
import com.android.dx.dex.TableOfContents;
+import com.android.dx.io.Annotation;
import com.android.dx.io.ClassData;
import com.android.dx.io.ClassDef;
import com.android.dx.io.Code;
@@ -26,6 +27,7 @@ import com.android.dx.io.DexHasher;
import com.android.dx.io.FieldId;
import com.android.dx.io.MethodId;
import com.android.dx.io.ProtoId;
+import com.android.dx.util.DexException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -104,8 +106,7 @@ public final class DexMerger {
writerSizes.annotationsSetRefList, "annotation set ref list");
contentsOut.annotationSets.off = dexOut.getLength();
- annotationSetOut = dexOut.appendSection(
- writerSizes.annotationsSet, "annotation sets");
+ annotationSetOut = dexOut.appendSection(writerSizes.annotationsSet, "annotation sets");
contentsOut.classDatas.off = dexOut.getLength();
classDataOut = dexOut.appendSection(writerSizes.classData, "class data");
@@ -144,7 +145,8 @@ public final class DexMerger {
mergeProtoIds();
mergeFieldIds();
mergeMethodIds();
- unionAnnotations();
+ mergeAnnotations();
+ unionAnnotationSetsAndDirectories();
mergeClassDefs();
// write the header
@@ -475,6 +477,26 @@ public final class DexMerger {
}.mergeSorted();
}
+ private void mergeAnnotations() {
+ new IdMerger<Annotation>(annotationOut) {
+ @Override TableOfContents.Section getSection(TableOfContents tableOfContents) {
+ return tableOfContents.annotations;
+ }
+
+ @Override Annotation read(DexBuffer.Section in, IndexMap indexMap, int index) {
+ return indexMap.adjust(in.readAnnotation());
+ }
+
+ @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) {
+ indexMap.putAnnotationOffset(offset, annotationOut.getPosition());
+ }
+
+ @Override void write(Annotation value) {
+ value.writeTo(annotationOut);
+ }
+ }.mergeUnsorted();
+ }
+
private void mergeClassDefs() {
SortableType[] types = getSortedTypes();
contentsOut.classDefs.off = idsDefsOut.getPosition();
@@ -543,10 +565,10 @@ public final class DexMerger {
/**
* Copy annotation sets from each input to the output.
*
- * TODO: this may write multiple copies of the same annotation.
- * This should shrink the output by merging rather than unioning
+ * TODO: this may write multiple copies of the same annotation set.
+ * We should shrink the output by merging rather than unioning
*/
- private void unionAnnotations() {
+ private void unionAnnotationSetsAndDirectories() {
transformAnnotationSets(dexA, aIndexMap);
transformAnnotationSets(dexB, bIndexMap);
transformAnnotationDirectories(dexA, aIndexMap);
@@ -558,7 +580,7 @@ public final class DexMerger {
if (section.exists()) {
DexBuffer.Section setIn = in.open(section.off);
for (int i = 0; i < section.size; i++) {
- transformAnnotationSet(in, indexMap, setIn);
+ transformAnnotationSet(indexMap, setIn);
}
}
}
@@ -671,7 +693,7 @@ public final class DexMerger {
/**
* Transform all annotations on a single type, member or parameter.
*/
- private void transformAnnotationSet(DexBuffer in, IndexMap indexMap, DexBuffer.Section setIn) {
+ private void transformAnnotationSet(IndexMap indexMap, DexBuffer.Section setIn) {
contentsOut.annotationSets.size++;
annotationSetOut.assertFourByteAligned();
indexMap.putAnnotationSetOffset(setIn.getPosition(), annotationSetOut.getPosition());
@@ -680,32 +702,7 @@ public final class DexMerger {
annotationSetOut.writeInt(size);
for (int j = 0; j < size; j++) {
- // annotation offset
- annotationSetOut.writeInt(annotationOut.getPosition());
- transformAnnotation(in.open(setIn.readInt()), indexMap);
- }
- }
-
- /**
- * Transform one annotation, which may have multiple fields.
- */
- private void transformAnnotation(DexBuffer.Section in, IndexMap indexMap) {
- contentsOut.annotations.size++;
-
- // visibility
- annotationOut.writeByte(in.readByte());
-
- // type index
- annotationOut.writeUleb128((int) indexMap.adjustType(in.readUleb128()));
-
- // size
- int size = in.readUleb128();
- annotationOut.writeUleb128(size);
-
- // elements
- for (int i = 0; i < size; i++) {
- annotationOut.writeUleb128(indexMap.adjustString(in.readUleb128())); // name
- new EncodedValueTransformer(in, indexMap, annotationOut).readValue(); // value
+ annotationSetOut.writeInt(indexMap.adjustAnnotation(setIn.readInt()));
}
}
@@ -761,12 +758,12 @@ public final class DexMerger {
contentsOut.codes.size++;
codeOut.assertFourByteAligned();
- codeOut.writeShort(code.getRegistersSize());
- codeOut.writeShort(code.getInsSize());
- codeOut.writeShort(code.getOutsSize());
+ codeOut.writeUnsignedShort(code.getRegistersSize());
+ codeOut.writeUnsignedShort(code.getInsSize());
+ codeOut.writeUnsignedShort(code.getOutsSize());
Code.Try[] tries = code.getTries();
- codeOut.writeShort((short) tries.length);
+ codeOut.writeUnsignedShort(tries.length);
// TODO: retain debug info
// code.getDebugInfoOffset();
@@ -786,8 +783,8 @@ public final class DexMerger {
}
for (Code.Try tryItem : tries) {
codeOut.writeInt(tryItem.getStartAddress());
- codeOut.writeShort(tryItem.getInstructionCount());
- codeOut.writeShort(tryItem.getHandlerOffset());
+ codeOut.writeUnsignedShort(tryItem.getInstructionCount());
+ codeOut.writeUnsignedShort(tryItem.getHandlerOffset());
}
Code.CatchHandler[] catchHandlers = code.getCatchHandlers();
codeOut.writeUleb128(catchHandlers.length);
@@ -820,7 +817,7 @@ public final class DexMerger {
private void transformStaticValues(DexBuffer.Section in, IndexMap indexMap) {
contentsOut.encodedArrays.size++;
- new EncodedValueTransformer(in, indexMap, encodedArrayOut).readArray();
+ indexMap.adjustEncodedArray(in.readEncodedArray()).writeTo(encodedArrayOut);
}
/**
@@ -888,7 +885,7 @@ public final class DexMerger {
} else {
classData += (int) Math.ceil(contents.classDatas.byteCount * 1.34);
encodedArray += contents.encodedArrays.byteCount * 2;
- annotation += contents.annotations.byteCount * 2;
+ annotation += (int) Math.ceil(contents.annotations.byteCount * 1.34);
}
}
diff --git a/dx/src/com/android/dx/merge/EncodedValueTransformer.java b/dx/src/com/android/dx/merge/EncodedValueTransformer.java
deleted file mode 100644
index 0959f15a0..000000000
--- a/dx/src/com/android/dx/merge/EncodedValueTransformer.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.merge;
-
-import com.android.dx.io.DexBuffer;
-import com.android.dx.io.EncodedValueReader;
-import com.android.dx.util.Unsigned;
-
-public final class EncodedValueTransformer extends EncodedValueReader {
- private final IndexMap indexMap;
- private final DexBuffer.Section out;
-
- public EncodedValueTransformer(DexBuffer.Section in, IndexMap indexMap, DexBuffer.Section out) {
- super(in);
- this.indexMap = indexMap;
- this.out = out;
- }
-
- protected void visitArray(int size) {
- out.writeUleb128(size);
- }
-
- protected void visitAnnotation(int typeIndex, int size) {
- out.writeUleb128(indexMap.adjustType(typeIndex));
- out.writeUleb128(size);
- }
-
- protected void visitAnnotationName(int index) {
- out.writeUleb128(indexMap.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, indexMap.adjustString(index), out);
- }
-
- protected void visitType(int type, int index) {
- writeTypeAndSizeAndIndex(type, indexMap.adjustType(index), out);
- }
-
- protected void visitField(int type, int index) {
- writeTypeAndSizeAndIndex(type, indexMap.adjustField(index), out);
- }
-
- protected void visitMethod(int type, int index) {
- writeTypeAndSizeAndIndex(type, indexMap.adjustMethod(index), out);
- }
-
- 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, DexBuffer.Section out) {
- 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;
- }
- int argAndType = ((byteCount - 1) << 5) | type;
- out.writeByte(argAndType);
-
- for (int i = 0; i < byteCount; i++) {
- out.writeByte(index & 0xff);
- index >>>= 8;
- }
- }
-
- private void copyBytes(DexBuffer.Section in, DexBuffer.Section out, int size) {
- for (int i = 0; i < size; i++) {
- out.writeByte(in.readByte());
- }
- }
-}
diff --git a/dx/src/com/android/dx/merge/IndexMap.java b/dx/src/com/android/dx/merge/IndexMap.java
index a06bdafb7..a7b20bede 100644
--- a/dx/src/com/android/dx/merge/IndexMap.java
+++ b/dx/src/com/android/dx/merge/IndexMap.java
@@ -17,11 +17,19 @@
package com.android.dx.merge;
import com.android.dx.dex.TableOfContents;
+import com.android.dx.io.Annotation;
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 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.Leb128Utils;
+import com.android.dx.util.Unsigned;
import java.util.HashMap;
/**
@@ -37,6 +45,7 @@ public final class IndexMap {
public final short[] fieldIds;
public final short[] methodIds;
private final HashMap<Integer, Integer> typeListOffsets;
+ private final HashMap<Integer, Integer> annotationOffsets;
private final HashMap<Integer, Integer> annotationSetOffsets;
private final HashMap<Integer, Integer> annotationDirectoryOffsets;
@@ -48,6 +57,7 @@ public final class IndexMap {
this.fieldIds = new short[tableOfContents.fieldIds.size];
this.methodIds = new short[tableOfContents.methodIds.size];
this.typeListOffsets = new HashMap<Integer, Integer>();
+ this.annotationOffsets = new HashMap<Integer, Integer>();
this.annotationSetOffsets = new HashMap<Integer, Integer>();
this.annotationDirectoryOffsets = new HashMap<Integer, Integer>();
@@ -60,12 +70,6 @@ public final class IndexMap {
this.annotationDirectoryOffsets.put(0, 0);
}
- private int[] createIntMap(TableOfContents.Section section) {
- return section.exists()
- ? new int[section.size]
- : new int[0];
- }
-
public void putTypeListOffset(int oldOffset, int newOffset) {
if (oldOffset <= 0 || newOffset <= 0) {
throw new IllegalArgumentException();
@@ -73,6 +77,13 @@ public final class IndexMap {
typeListOffsets.put(oldOffset, newOffset);
}
+ public void putAnnotationOffset(int oldOffset, int newOffset) {
+ if (oldOffset <= 0 || newOffset <= 0) {
+ throw new IllegalArgumentException();
+ }
+ annotationOffsets.put(oldOffset, newOffset);
+ }
+
public void putAnnotationSetOffset(int oldOffset, int newOffset) {
if (oldOffset <= 0 || newOffset <= 0) {
throw new IllegalArgumentException();
@@ -91,8 +102,8 @@ public final class IndexMap {
return stringIndex == ClassDef.NO_INDEX ? ClassDef.NO_INDEX : stringIds[stringIndex];
}
- public short adjustType(int typeIndex) {
- return (typeIndex == ClassDef.NO_INDEX) ? ClassDef.NO_INDEX : typeIds[typeIndex];
+ public int adjustType(int typeIndex) {
+ return (typeIndex == ClassDef.NO_INDEX) ? ClassDef.NO_INDEX : (typeIds[typeIndex] & 0xffff);
}
public TypeList adjustTypeList(TypeList typeList) {
@@ -101,27 +112,31 @@ public final class IndexMap {
}
short[] types = typeList.getTypes().clone();
for (int i = 0; i < types.length; i++) {
- types[i] = adjustType(types[i]);
+ types[i] = (short) adjustType(types[i]);
}
return new TypeList(target, types);
}
- public short adjustProto(int protoIndex) {
- return protoIds[protoIndex];
+ public int adjustProto(int protoIndex) {
+ return protoIds[protoIndex] & 0xffff;
}
- public short adjustField(int fieldIndex) {
- return fieldIds[fieldIndex];
+ public int adjustField(int fieldIndex) {
+ return fieldIds[fieldIndex] & 0xffff;
}
- public short adjustMethod(int methodIndex) {
- return methodIds[methodIndex];
+ public int adjustMethod(int methodIndex) {
+ return methodIds[methodIndex] & 0xffff;
}
public int adjustTypeListOffset(int typeListOffset) {
return typeListOffsets.get(typeListOffset);
}
+ public int adjustAnnotation(int annotationOffset) {
+ return annotationOffsets.get(annotationOffset);
+ }
+
public int adjustAnnotationSet(int annotationSetOffset) {
return annotationSetOffsets.get(annotationSetOffset);
}
@@ -163,4 +178,115 @@ public final class IndexMap {
public SortableType adjust(SortableType sortableType) {
return new SortableType(sortableType.getBuffer(), adjust(sortableType.getClassDef()));
}
+
+ public EncodedValue adjustEncodedValue(EncodedValue encodedValue) {
+ ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32);
+ new EncodedValueTransformer(encodedValue, out).readValue();
+ return new EncodedValue(out.toByteArray());
+ }
+
+ public EncodedValue adjustEncodedArray(EncodedValue encodedArray) {
+ ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(32);
+ new EncodedValueTransformer(encodedArray, out).readArray();
+ return new EncodedValue(out.toByteArray());
+ }
+
+ public Annotation adjust(Annotation annotation) {
+ int[] names = annotation.getNames().clone();
+ EncodedValue[] values = annotation.getValues().clone();
+ for (int i = 0; i < names.length; i++) {
+ names[i] = adjustString(names[i]);
+ values[i] = adjustEncodedValue(values[i]);
+ }
+ return new Annotation(target, annotation.getVisibility(),
+ adjustType(annotation.getTypeIndex()), names, values);
+ }
+
+ /**
+ * Adjust an encoded value or array.
+ */
+ private final class EncodedValueTransformer extends EncodedValueReader {
+ private final ByteOutput out;
+
+ public EncodedValueTransformer(EncodedValue encodedValue, ByteOutput out) {
+ super(encodedValue);
+ 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;
+ }
+ int argAndType = ((byteCount - 1) << 5) | type;
+ out.writeByte(argAndType);
+
+ for (int i = 0; i < byteCount; i++) {
+ out.writeByte(index & 0xff);
+ index >>>= 8;
+ }
+ }
+
+ private void copyBytes(ByteInput in, ByteOutput out, int size) {
+ for (int i = 0; i < size; i++) {
+ out.writeByte(in.readByte());
+ }
+ }
+ }
}
diff --git a/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java
index def49a658..05ec733f8 100644
--- a/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java
+++ b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java
@@ -28,7 +28,7 @@ import java.util.ArrayList;
* writes all use little-endian order.</p>
*/
public final class ByteArrayAnnotatedOutput
- implements AnnotatedOutput {
+ implements AnnotatedOutput, ByteOutput {
/** default size for stretchy instances */
private static final int DEFAULT_SIZE = 1000;
@@ -81,7 +81,16 @@ public final class ByteArrayAnnotatedOutput
* by default.
*/
public ByteArrayAnnotatedOutput() {
- this(new byte[DEFAULT_SIZE], true);
+ this(DEFAULT_SIZE);
+ }
+
+ /**
+ * Constructs a "stretchy" instance with initial size {@code size}. The
+ * underlying array may be reallocated. The constructed instance does not
+ * keep annotations by default.
+ */
+ public ByteArrayAnnotatedOutput(int size) {
+ this(new byte[size], true);
}
/**
@@ -228,9 +237,9 @@ public final class ByteArrayAnnotatedOutput
if (stretchy) {
ensureCapacity(cursor + 5); // pessimistic
}
- int byteCount = Leb128Utils.writeUnsignedLeb128(data, cursor, value);
- cursor += byteCount;
- return byteCount;
+ int cursorBefore = cursor;
+ Leb128Utils.writeUnsignedLeb128(this, value);
+ return (cursor - cursorBefore);
}
/** {@inheritDoc} */
@@ -238,9 +247,9 @@ public final class ByteArrayAnnotatedOutput
if (stretchy) {
ensureCapacity(cursor + 5); // pessimistic
}
- int byteCount = Leb128Utils.writeSignedLeb128(data, cursor, value);
- cursor += byteCount;
- return byteCount;
+ int cursorBefore = cursor;
+ Leb128Utils.writeSignedLeb128(this, value);
+ return (cursor - cursorBefore);
}
/** {@inheritDoc} */
diff --git a/dx/src/com/android/dx/util/ByteArrayByteInput.java b/dx/src/com/android/dx/util/ByteArrayByteInput.java
new file mode 100644
index 000000000..07c543fe3
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteArrayByteInput.java
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+public final class ByteArrayByteInput implements ByteInput {
+
+ private final byte[] bytes;
+ private int position;
+
+ public ByteArrayByteInput(byte... bytes) {
+ this.bytes = bytes;
+ }
+
+ @Override public byte readByte() {
+ return bytes[position++];
+ }
+}
diff --git a/dx/src/com/android/dx/util/ByteInput.java b/dx/src/com/android/dx/util/ByteInput.java
new file mode 100644
index 000000000..8181e3ea3
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteInput.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+/**
+ * A byte source.
+ */
+public interface ByteInput {
+
+ /**
+ * Returns a byte.
+ *
+ * @throws IndexOutOfBoundsException if all bytes have been read.
+ */
+ byte readByte();
+}
diff --git a/dx/src/com/android/dx/util/ByteOutput.java b/dx/src/com/android/dx/util/ByteOutput.java
new file mode 100644
index 000000000..512dcf8d9
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteOutput.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+/**
+ * A byte sink.
+ */
+public interface ByteOutput {
+
+ /**
+ * Writes a byte.
+ *
+ * @throws IndexOutOfBoundsException if all bytes have been written.
+ */
+ void writeByte(int i);
+}
diff --git a/dx/src/com/android/dx/util/Leb128Utils.java b/dx/src/com/android/dx/util/Leb128Utils.java
index 73f7d32da..10374638e 100644
--- a/dx/src/com/android/dx/util/Leb128Utils.java
+++ b/dx/src/com/android/dx/util/Leb128Utils.java
@@ -16,9 +16,6 @@
package com.android.dx.util;
-import java.io.DataInput;
-import java.io.IOException;
-
/**
* Reads and writes DWARFv3 LEB 128 signed and unsigned integers. See DWARF v3
* section 7.6.
@@ -82,7 +79,7 @@ public final class Leb128Utils {
/**
* Reads an signed integer from {@code in}.
*/
- public static int readSignedLeb128(DataInput in) throws IOException {
+ public static int readSignedLeb128(ByteInput in) {
int result = 0;
int cur;
int count = 0;
@@ -96,7 +93,7 @@ public final class Leb128Utils {
} while (((cur & 0x80) == 0x80) && count < 5);
if ((cur & 0x80) == 0x80) {
- throw new IOException("invalid LEB128 sequence");
+ throw new DexException("invalid LEB128 sequence");
}
// Sign extend if appropriate
@@ -110,7 +107,7 @@ public final class Leb128Utils {
/**
* Reads an unsigned integer from {@code in}.
*/
- public static int readUnsignedLeb128(DataInput in) throws IOException {
+ public static int readUnsignedLeb128(ByteInput in) {
int result = 0;
int cur;
int count = 0;
@@ -122,7 +119,7 @@ public final class Leb128Utils {
} while (((cur & 0x80) == 0x80) && count < 5);
if ((cur & 0x80) == 0x80) {
- throw new IOException("invalid LEB128 sequence");
+ throw new DexException("invalid LEB128 sequence");
}
return result;
@@ -132,28 +129,24 @@ public final class Leb128Utils {
* Writes {@code value} as an unsigned integer to {@code out}, starting at
* {@code offset}. Returns the number of bytes written.
*/
- public static int writeUnsignedLeb128(byte[] out, int offset, int value) {
- int remaining = value >> 7;
- int count = 0;
+ public static void writeUnsignedLeb128(ByteOutput out, int value) {
+ int remaining = value >>> 7;
while (remaining != 0) {
- out[offset + count] = (byte) ((value & 0x7f) | 0x80);
+ out.writeByte((byte) ((value & 0x7f) | 0x80));
value = remaining;
- remaining >>= 7;
- count++;
+ remaining >>>= 7;
}
- out[offset + count] = (byte) (value & 0x7f);
- return count + 1;
+ out.writeByte((byte) (value & 0x7f));
}
/**
* Writes {@code value} as a signed integer to {@code out}, starting at
* {@code offset}. Returns the number of bytes written.
*/
- public static int writeSignedLeb128(byte[] out, int offset, int value) {
+ public static void writeSignedLeb128(ByteOutput out, int value) {
int remaining = value >> 7;
- int count = 0;
boolean hasMore = true;
int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
@@ -161,12 +154,9 @@ public final class Leb128Utils {
hasMore = (remaining != end)
|| ((remaining & 1) != ((value >> 6) & 1));
- out[offset + count] = (byte) ((value & 0x7f) | (hasMore ? 0x80 : 0));
+ out.writeByte((byte) ((value & 0x7f) | (hasMore ? 0x80 : 0)));
value = remaining;
remaining >>= 7;
- count++;
}
-
- return count;
}
}
diff --git a/dx/src/com/android/dx/util/Mutf8.java b/dx/src/com/android/dx/util/Mutf8.java
index ffa43a502..fe5572492 100644
--- a/dx/src/com/android/dx/util/Mutf8.java
+++ b/dx/src/com/android/dx/util/Mutf8.java
@@ -16,8 +16,6 @@
package com.android.dx.util;
-import java.io.DataInput;
-import java.io.IOException;
import java.io.UTFDataFormatException;
/**
@@ -32,7 +30,7 @@ public final class Mutf8 {
* Decodes bytes from {@code in} into {@code out} until a delimiter 0x00 is
* encountered. Returns a new string containing the decoded characters.
*/
- public static String decode(DataInput in, char[] out) throws IOException {
+ public static String decode(ByteInput in, char[] out) throws UTFDataFormatException {
int s = 0;
while (true) {
char a = (char) (in.readByte() & 0xff);
diff --git a/dx/src/com/android/dx/util/Output.java b/dx/src/com/android/dx/util/Output.java
index 12eaa4c6f..e5956a25a 100644
--- a/dx/src/com/android/dx/util/Output.java
+++ b/dx/src/com/android/dx/util/Output.java
@@ -21,7 +21,7 @@ package com.android.dx.util;
* {@code java.util.DataOutput}, but no {@code IOExceptions}
* are declared, and multibyte output is defined to be little-endian.
*/
-public interface Output {
+public interface Output extends ByteOutput {
/**
* Gets the current cursor position. This is the same as the number of
* bytes written to this instance.
diff --git a/dx/tests/116-leb128/com/android/dx/util/Leb128UtilsTest.java b/dx/tests/116-leb128/com/android/dx/util/Leb128UtilsTest.java
index f47bc86d1..dbc0ea37b 100644
--- a/dx/tests/116-leb128/com/android/dx/util/Leb128UtilsTest.java
+++ b/dx/tests/116-leb128/com/android/dx/util/Leb128UtilsTest.java
@@ -16,8 +16,6 @@
package com.android.dx.util;
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
import java.io.IOException;
import java.util.Arrays;
import junit.framework.TestCase;
@@ -25,53 +23,53 @@ import junit.framework.TestCase;
public final class Leb128UtilsTest extends TestCase {
public void testDecodeUnsignedLeb() throws IOException {
- assertEquals(0, Leb128Utils.readUnsignedLeb128(newDataInput((byte) 0)));
- assertEquals(1, Leb128Utils.readUnsignedLeb128(newDataInput((byte) 1)));
- assertEquals(127, Leb128Utils.readUnsignedLeb128(newDataInput((byte) 0x7F)));
- assertEquals(16256, Leb128Utils.readUnsignedLeb128(newDataInput((byte) 0x80, (byte) 0x7F)));
+ assertEquals(0, Leb128Utils.readUnsignedLeb128(new ByteArrayByteInput((byte) 0)));
+ assertEquals(1, Leb128Utils.readUnsignedLeb128(new ByteArrayByteInput((byte) 1)));
+ assertEquals(127, Leb128Utils.readUnsignedLeb128(new ByteArrayByteInput((byte) 0x7f)));
+ assertEquals(16256, Leb128Utils.readUnsignedLeb128(
+ new ByteArrayByteInput((byte) 0x80, (byte) 0x7f)));
}
public void testEncodeUnsignedLeb() throws IOException {
assertEquals(new byte[] { 0 }, encodeUnsignedLeb(0));
assertEquals(new byte[] { 1 }, encodeUnsignedLeb(1));
- assertEquals(new byte[] { 0x7F }, encodeUnsignedLeb(127));
- assertEquals(new byte[] { (byte) 0x80, 0x7F }, encodeUnsignedLeb(16256));
+ assertEquals(new byte[] { 0x7f }, encodeUnsignedLeb(127));
+ assertEquals(new byte[] { (byte) 0x80, 0x7f }, encodeUnsignedLeb(16256));
assertEquals(new byte[] { (byte) 0xb4, 0x07 }, encodeUnsignedLeb(0x3b4));
assertEquals(new byte[] { (byte) 0x8c, 0x08 }, encodeUnsignedLeb(0x40c));
+ assertEquals(new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0xf },
+ encodeUnsignedLeb(0xffffffff));
}
public void testDecodeSignedLeb() throws IOException {
- assertEquals(0, Leb128Utils.readSignedLeb128(newDataInput((byte) 0)));
- assertEquals(1, Leb128Utils.readSignedLeb128(newDataInput((byte) 1)));
- assertEquals(-1, Leb128Utils.readSignedLeb128(newDataInput((byte) 0x7F)));
- assertEquals(0x3C, Leb128Utils.readSignedLeb128(newDataInput((byte) 0x3C)));
- assertEquals(-128, Leb128Utils.readSignedLeb128(newDataInput((byte) 0x80, (byte) 0x7F)));
+ assertEquals(0, Leb128Utils.readSignedLeb128(new ByteArrayByteInput((byte) 0)));
+ assertEquals(1, Leb128Utils.readSignedLeb128(new ByteArrayByteInput((byte) 1)));
+ assertEquals(-1, Leb128Utils.readSignedLeb128(new ByteArrayByteInput((byte) 0x7f)));
+ assertEquals(0x3c, Leb128Utils.readSignedLeb128(new ByteArrayByteInput((byte) 0x3c)));
+ assertEquals(-128, Leb128Utils.readSignedLeb128(
+ new ByteArrayByteInput((byte) 0x80, (byte) 0x7f)));
}
public void testEncodeSignedLeb() throws IOException {
assertEquals(new byte[] { 0 }, encodeSignedLeb(0));
assertEquals(new byte[] { 1 }, encodeSignedLeb(1));
- assertEquals(new byte[] { 0x7F }, encodeSignedLeb(-1));
- assertEquals(new byte[] { (byte) 0x80, 0x7F }, encodeSignedLeb(-128));
+ assertEquals(new byte[] { 0x7f }, encodeSignedLeb(-1));
+ assertEquals(new byte[] { (byte) 0x80, 0x7f }, encodeSignedLeb(-128));
}
private byte[] encodeSignedLeb(int value) {
- byte[] buffer = new byte[5];
- int length = Leb128Utils.writeSignedLeb128(buffer, 0, value);
- return Arrays.copyOfRange(buffer, 0, length);
+ ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(5);
+ Leb128Utils.writeSignedLeb128(out, value);
+ return out.toByteArray();
}
private byte[] encodeUnsignedLeb(int value) {
- byte[] buffer = new byte[5];
- int length = Leb128Utils.writeUnsignedLeb128(buffer, 0, value);
- return Arrays.copyOfRange(buffer, 0, length);
- }
-
- public DataInputStream newDataInput(byte... bytes) {
- return new DataInputStream(new ByteArrayInputStream(bytes));
+ ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(5);
+ Leb128Utils.writeUnsignedLeb128(out, value);
+ return out.toByteArray();
}
private void assertEquals(byte[] expected, byte[] actual) {
- assertTrue(Arrays.equals(expected, actual));
+ assertTrue(Arrays.toString(actual), Arrays.equals(expected, actual));
}
}
diff --git a/dx/tests/117-modified-utf8/com/android/dx/util/Mutf8Test.java b/dx/tests/117-modified-utf8/com/android/dx/util/Mutf8Test.java
index 736b1bfe6..8e6188db0 100644
--- a/dx/tests/117-modified-utf8/com/android/dx/util/Mutf8Test.java
+++ b/dx/tests/117-modified-utf8/com/android/dx/util/Mutf8Test.java
@@ -16,34 +16,22 @@
package com.android.dx.util;
-import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.RandomAccessFile;
import java.util.Arrays;
import junit.framework.TestCase;
public final class Mutf8Test extends TestCase {
public void testDecode() throws IOException {
- File file = createTempFile(new byte[] { 'A', 'B', 'C', (byte) 0xc0, (byte) 0x80, 0, 'E' });
- RandomAccessFile f = new RandomAccessFile(file, "r");
- assertEquals('A', f.readByte());
- assertEquals("BC\u0000", Mutf8.decode(f, new char[3]));
- assertEquals('E', f.readByte());
- file.delete();
+ ByteInput in = new ByteArrayByteInput(
+ new byte[] { 'A', 'B', 'C', (byte) 0xc0, (byte) 0x80, 0, 'E' });
+ assertEquals('A', in.readByte());
+ assertEquals("BC\u0000", Mutf8.decode(in, new char[3]));
+ assertEquals('E', in.readByte());
}
public void testEncode() throws IOException {
assertEquals(Arrays.toString(new byte[] { 'B', 'C', (byte) 0xc0, (byte) 0x80 }),
Arrays.toString(Mutf8.encode("BC\u0000")));
}
-
- private File createTempFile(byte[] contents) throws IOException {
- File result = File.createTempFile(getClass().getName(), "test");
- FileOutputStream out = new FileOutputStream(result);
- out.write(contents);
- out.close();
- return result;
- }
}