diff options
| author | Jesse Wilson <jessewilson@google.com> | 2011-03-16 14:22:51 -0700 |
|---|---|---|
| committer | Jesse Wilson <jessewilson@google.com> | 2011-03-16 17:47:38 -0700 |
| commit | bd3dba4346223593ac6033a3d2a7d8ec6f20738b (patch) | |
| tree | 173c2479689385298ff6fd1e05038d5f784e1387 /dx | |
| parent | 1b4822ecb93021e6273c92093f78dcb6ccc0cf91 (diff) | |
| download | android_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')
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; - } } |
