diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java index b12328f8..80623257 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -419,7 +419,7 @@ public class MethodDefinition { } private void addTries(List methodItems) { - List tryBlocks = methodImpl.getTryBlocks(); + List> tryBlocks = methodImpl.getTryBlocks(); if (tryBlocks.size() == 0) { return; } @@ -427,7 +427,7 @@ public class MethodDefinition { int lastInstructionAddress = instructionOffsetMap.getInstructionCodeOffset(instructions.size() - 1); int codeSize = lastInstructionAddress + instructions.get(instructions.size() - 1).getCodeUnits(); - for (TryBlock tryBlock: tryBlocks) { + for (TryBlock tryBlock: tryBlocks) { int startAddress = tryBlock.getStartCodeAddress(); int endAddress = startAddress + tryBlock.getCodeUnitCount(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java b/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java index 307aad4e..ac95bb51 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java @@ -111,7 +111,7 @@ public final class DexFileFactory { } public static void writeDexFile(String path, DexFile dexFile) throws IOException { - org.jf.dexlib2.writer.DexFile.writeTo(path, dexFile); + org.jf.dexlib2.writer.pool.DexPool.writeTo(path, dexFile); } private DexFileFactory() {} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java index 7cc47fa2..921a410f 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java @@ -1035,7 +1035,7 @@ public class MethodAnalyzer { RegisterType exceptionType = RegisterType.UNKNOWN_TYPE; - for (TryBlock tryBlock: methodImpl.getTryBlocks()) { + for (TryBlock tryBlock: methodImpl.getTryBlocks()) { for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) { if (handler.getHandlerCodeAddress() == instructionAddress) { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/BaseAnnotation.java b/dexlib2/src/main/java/org/jf/dexlib2/base/BaseAnnotation.java index b03d70a5..134f3746 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/base/BaseAnnotation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/BaseAnnotation.java @@ -65,7 +65,7 @@ public abstract class BaseAnnotation implements Annotation { return CollectionUtils.compareAsSet(getElements(), o.getElements()); } - public static final Comparator BY_TYPE = new Comparator() { + public static final Comparator BY_TYPE = new Comparator() { @Override public int compare(Annotation annotation1, Annotation annotation2) { return annotation1.getType().compareTo(annotation2.getType()); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/BaseTryBlock.java b/dexlib2/src/main/java/org/jf/dexlib2/base/BaseTryBlock.java index e89c5312..d1afe9a4 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/base/BaseTryBlock.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/BaseTryBlock.java @@ -31,9 +31,10 @@ package org.jf.dexlib2.base; +import org.jf.dexlib2.iface.ExceptionHandler; import org.jf.dexlib2.iface.TryBlock; -public abstract class BaseTryBlock implements TryBlock { +public abstract class BaseTryBlock implements TryBlock { @Override public boolean equals(Object o) { if (o instanceof TryBlock) { TryBlock other = (TryBlock)o; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedCatchAllExceptionHandler.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedCatchAllExceptionHandler.java index 9b63bbda..36b309d4 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedCatchAllExceptionHandler.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedCatchAllExceptionHandler.java @@ -37,7 +37,7 @@ import org.jf.dexlib2.iface.ExceptionHandler; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class DexBackedCatchAllExceptionHandler extends BaseExceptionHandler implements ExceptionHandler { +public class DexBackedCatchAllExceptionHandler extends DexBackedExceptionHandler { private final int handlerCodeAddress; public DexBackedCatchAllExceptionHandler(@Nonnull DexReader reader) { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedExceptionHandler.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedExceptionHandler.java index a8ce5833..c38a0f45 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedExceptionHandler.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedExceptionHandler.java @@ -32,22 +32,6 @@ package org.jf.dexlib2.dexbacked; import org.jf.dexlib2.base.BaseExceptionHandler; -import org.jf.dexlib2.iface.ExceptionHandler; -import javax.annotation.Nonnull; - -public class DexBackedExceptionHandler extends BaseExceptionHandler implements ExceptionHandler { - @Nonnull private final DexBackedDexFile dexFile; - private final int typeId; - private final int handlerCodeAddress; - - public DexBackedExceptionHandler(@Nonnull DexReader reader) { - // TODO: verify dalvik doesn't accept an exception handler that points in the middle of an instruction - this.dexFile = reader.dexBuf; - this.typeId = reader.readSmallUleb128(); - this.handlerCodeAddress = reader.readSmallUleb128(); - } - - @Nonnull @Override public String getExceptionType() { return dexFile.getType(typeId); } - @Override public int getHandlerCodeAddress() { return handlerCodeAddress; } +public abstract class DexBackedExceptionHandler extends BaseExceptionHandler { } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java index 3cfaaa7c..27ed6c40 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java @@ -38,7 +38,6 @@ import org.jf.dexlib2.dexbacked.util.DebugInfo; import org.jf.dexlib2.dexbacked.util.FixedSizeList; import org.jf.dexlib2.dexbacked.util.VariableSizeLookaheadIterator; import org.jf.dexlib2.iface.MethodImplementation; -import org.jf.dexlib2.iface.TryBlock; import org.jf.dexlib2.iface.debug.DebugItem; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.util.AlignmentUtils; @@ -87,7 +86,7 @@ public class DexBackedMethodImplementation implements MethodImplementation { @Nonnull @Override - public List getTryBlocks() { + public List getTryBlocks() { // TODO: provide utility to put try blocks into a "canonical", easy to use format, which more closely matches java's try blocks final int triesSize = dexFile.readUshort(codeOffset + CodeItem.TRIES_SIZE_OFFSET); if (triesSize > 0) { @@ -96,10 +95,10 @@ public class DexBackedMethodImplementation implements MethodImplementation { codeOffset + CodeItem.INSTRUCTION_START_OFFSET + (instructionsSize*2), 4); final int handlersStartOffset = triesStartOffset + triesSize*CodeItem.TryItem.ITEM_SIZE; - return new FixedSizeList() { + return new FixedSizeList() { @Nonnull @Override - public TryBlock readItem(int index) { + public DexBackedTryBlock readItem(int index) { return new DexBackedTryBlock(dexFile, triesStartOffset + index*CodeItem.TryItem.ITEM_SIZE, handlersStartOffset); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTryBlock.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTryBlock.java index a8ec91e6..045660f6 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTryBlock.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTryBlock.java @@ -34,19 +34,15 @@ package org.jf.dexlib2.dexbacked; import org.jf.dexlib2.base.BaseTryBlock; import org.jf.dexlib2.dexbacked.raw.CodeItem; import org.jf.dexlib2.dexbacked.util.VariableSizeList; -import org.jf.dexlib2.iface.ExceptionHandler; -import org.jf.dexlib2.iface.TryBlock; import javax.annotation.Nonnull; import java.util.List; -public class DexBackedTryBlock extends BaseTryBlock implements TryBlock { +public class DexBackedTryBlock extends BaseTryBlock { @Nonnull public final DexBackedDexFile dexFile; private final int tryItemOffset; private final int handlersStartOffset; - - public DexBackedTryBlock(@Nonnull DexBackedDexFile dexFile, int tryItemOffset, int handlersStartOffset) { @@ -65,31 +61,31 @@ public class DexBackedTryBlock extends BaseTryBlock implements TryBlock { @Nonnull @Override - public List getExceptionHandlers() { + public List getExceptionHandlers() { DexReader reader = dexFile.readerAt( handlersStartOffset + dexFile.readUshort(tryItemOffset + CodeItem.TryItem.HANDLER_OFFSET)); final int encodedSize = reader.readSleb128(); if (encodedSize > 0) { //no catch-all - return new VariableSizeList(dexFile, reader.getOffset(), encodedSize) { + return new VariableSizeList(dexFile, reader.getOffset(), encodedSize) { @Nonnull @Override - protected ExceptionHandler readNextItem(@Nonnull DexReader reader, int index) { - return new DexBackedExceptionHandler(reader); + protected DexBackedTypedExceptionHandler readNextItem(@Nonnull DexReader reader, int index) { + return new DexBackedTypedExceptionHandler(reader); } }; } else { //with catch-all final int sizeWithCatchAll = (-1 * encodedSize) + 1; - return new VariableSizeList(dexFile, reader.getOffset(), sizeWithCatchAll) { + return new VariableSizeList(dexFile, reader.getOffset(), sizeWithCatchAll) { @Nonnull @Override - protected ExceptionHandler readNextItem(@Nonnull DexReader dexReader, int index) { + protected DexBackedExceptionHandler readNextItem(@Nonnull DexReader dexReader, int index) { if (index == sizeWithCatchAll-1) { return new DexBackedCatchAllExceptionHandler(dexReader); } else { - return new DexBackedExceptionHandler(dexReader); + return new DexBackedTypedExceptionHandler(dexReader); } } }; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTypedExceptionHandler.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTypedExceptionHandler.java new file mode 100644 index 00000000..cb3d9fb8 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTypedExceptionHandler.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.dexbacked; + +import org.jf.dexlib2.base.BaseExceptionHandler; +import org.jf.dexlib2.iface.ExceptionHandler; + +import javax.annotation.Nonnull; + +public class DexBackedTypedExceptionHandler extends DexBackedExceptionHandler { + @Nonnull private final DexBackedDexFile dexFile; + private final int typeId; + private final int handlerCodeAddress; + + public DexBackedTypedExceptionHandler(@Nonnull DexReader reader) { + // TODO: verify dalvik doesn't accept an exception handler that points in the middle of an instruction + this.dexFile = reader.dexBuf; + this.typeId = reader.readSmallUleb128(); + this.handlerCodeAddress = reader.readSmallUleb128(); + } + + @Nonnull @Override public String getExceptionType() { return dexFile.getType(typeId); } + @Override public int getHandlerCodeAddress() { return handlerCodeAddress; } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/MethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/MethodImplementation.java index 767b2fe8..a88e56e1 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/iface/MethodImplementation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/MethodImplementation.java @@ -70,7 +70,7 @@ public interface MethodImplementation { * * @return A list of the TryBlock items */ - @Nonnull List getTryBlocks(); + @Nonnull List> getTryBlocks(); /** * Get a list of debug items for this method. diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/TryBlock.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/TryBlock.java index e5cda3fa..b6e4bbd2 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/iface/TryBlock.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/TryBlock.java @@ -38,7 +38,7 @@ import java.util.List; /** * This class represents an individual try block and associated set of handlers. */ -public interface TryBlock { +public interface TryBlock { /** * Gets the code offset of the start of this try block. * @@ -67,7 +67,7 @@ public interface TryBlock { * * @return A list of ExceptionHandler objects */ - @Nonnull List getExceptionHandlers(); + @Nonnull List getExceptionHandlers(); /** * Compares this TryBlock to another TryBlock for equality. diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableMethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableMethodImplementation.java index 99d4f194..c1117306 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableMethodImplementation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableMethodImplementation.java @@ -32,6 +32,7 @@ package org.jf.dexlib2.immutable; import com.google.common.collect.ImmutableList; +import org.jf.dexlib2.iface.ExceptionHandler; import org.jf.dexlib2.iface.MethodImplementation; import org.jf.dexlib2.iface.TryBlock; import org.jf.dexlib2.iface.debug.DebugItem; @@ -52,7 +53,7 @@ public class ImmutableMethodImplementation implements MethodImplementation { public ImmutableMethodImplementation(int registerCount, @Nullable Iterable instructions, - @Nullable List tryBlocks, + @Nullable List> tryBlocks, @Nullable Iterable debugItems) { this.registerCount = registerCount; this.instructions = ImmutableInstruction.immutableListOf(instructions); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableTryBlock.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableTryBlock.java index ac0407ec..aa442bfa 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableTryBlock.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableTryBlock.java @@ -42,7 +42,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; -public class ImmutableTryBlock extends BaseTryBlock implements TryBlock { +public class ImmutableTryBlock extends BaseTryBlock { protected final int startCodeAddress; protected final int codeUnitCount; @Nonnull protected final ImmutableList exceptionHandlers; @@ -63,7 +63,7 @@ public class ImmutableTryBlock extends BaseTryBlock implements TryBlock { this.exceptionHandlers = ImmutableUtils.nullToEmptyList(exceptionHandlers); } - public static ImmutableTryBlock of(TryBlock tryBlock) { + public static ImmutableTryBlock of(TryBlock tryBlock) { if (tryBlock instanceof ImmutableTryBlock) { return (ImmutableTryBlock)tryBlock; } @@ -81,12 +81,13 @@ public class ImmutableTryBlock extends BaseTryBlock implements TryBlock { } @Nonnull - public static ImmutableList immutableListOf(@Nullable List list) { + public static ImmutableList immutableListOf( + @Nullable List> list) { return CONVERTER.toList(list); } - private static final ImmutableConverter CONVERTER = - new ImmutableConverter() { + private static final ImmutableConverter> CONVERTER = + new ImmutableConverter>() { @Override protected boolean isImmutable(@Nonnull TryBlock item) { return item instanceof ImmutableTryBlock; @@ -94,7 +95,7 @@ public class ImmutableTryBlock extends BaseTryBlock implements TryBlock { @Nonnull @Override - protected ImmutableTryBlock makeImmutable(@Nonnull TryBlock item) { + protected ImmutableTryBlock makeImmutable(@Nonnull TryBlock item) { return ImmutableTryBlock.of(item); } }; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstructionFactory.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstructionFactory.java new file mode 100644 index 00000000..c3fd82b9 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstructionFactory.java @@ -0,0 +1,223 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.immutable.instruction; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.instruction.SwitchElement; +import org.jf.dexlib2.iface.reference.Reference; +import org.jf.dexlib2.writer.InstructionFactory; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public class ImmutableInstructionFactory implements InstructionFactory { + public static final ImmutableInstructionFactory INSTANCE = new ImmutableInstructionFactory(); + + private ImmutableInstructionFactory() { + } + + public ImmutableInstruction10t makeInstruction10t(@Nonnull Opcode opcode, + int codeOffset) { + return new ImmutableInstruction10t(opcode, codeOffset); + } + + public ImmutableInstruction10x makeInstruction10x(@Nonnull Opcode opcode) { + return new ImmutableInstruction10x(opcode); + } + + public ImmutableInstruction11n makeInstruction11n(@Nonnull Opcode opcode, + int registerA, + int literal) { + return new ImmutableInstruction11n(opcode, registerA, literal); + } + + public ImmutableInstruction11x makeInstruction11x(@Nonnull Opcode opcode, + int registerA) { + return new ImmutableInstruction11x(opcode, registerA); + } + + public ImmutableInstruction12x makeInstruction12x(@Nonnull Opcode opcode, + int registerA, + int registerB) { + return new ImmutableInstruction12x(opcode, registerA, registerB); + } + + public ImmutableInstruction20bc makeInstruction20bc(@Nonnull Opcode opcode, + int verificationError, + @Nonnull Reference reference) { + return new ImmutableInstruction20bc(opcode, verificationError, reference); + } + + public ImmutableInstruction20t makeInstruction20t(@Nonnull Opcode opcode, + int codeOffset) { + return new ImmutableInstruction20t(opcode, codeOffset); + } + + public ImmutableInstruction21c makeInstruction21c(@Nonnull Opcode opcode, + int registerA, + @Nonnull Reference reference) { + return new ImmutableInstruction21c(opcode, registerA, reference); + } + + public ImmutableInstruction21ih makeInstruction21ih(@Nonnull Opcode opcode, + int registerA, + int literal) { + return new ImmutableInstruction21ih(opcode, registerA, literal); + } + + public ImmutableInstruction21lh makeInstruction21lh(@Nonnull Opcode opcode, + int registerA, + long literal) { + return new ImmutableInstruction21lh(opcode, registerA, literal); + } + + public ImmutableInstruction21s makeInstruction21s(@Nonnull Opcode opcode, + int registerA, + int literal) { + return new ImmutableInstruction21s(opcode, registerA, literal); + } + + public ImmutableInstruction21t makeInstruction21t(@Nonnull Opcode opcode, + int registerA, + int codeOffset) { + return new ImmutableInstruction21t(opcode, registerA, codeOffset); + } + + public ImmutableInstruction22b makeInstruction22b(@Nonnull Opcode opcode, + int registerA, + int registerB, + int literal) { + return new ImmutableInstruction22b(opcode, registerA, registerB, literal); + } + + public ImmutableInstruction22c makeInstruction22c(@Nonnull Opcode opcode, + int registerA, + int registerB, + @Nonnull Reference reference) { + return new ImmutableInstruction22c(opcode, registerA, registerB, reference); + } + + public ImmutableInstruction22s makeInstruction22s(@Nonnull Opcode opcode, + int registerA, + int registerB, + int literal) { + return new ImmutableInstruction22s(opcode, registerA, registerB, literal); + } + + public ImmutableInstruction22t makeInstruction22t(@Nonnull Opcode opcode, + int registerA, + int registerB, + int codeOffset) { + return new ImmutableInstruction22t(opcode, registerA, registerB, codeOffset); + } + + public ImmutableInstruction22x makeInstruction22x(@Nonnull Opcode opcode, + int registerA, + int registerB) { + return new ImmutableInstruction22x(opcode, registerA, registerB); + } + + public ImmutableInstruction23x makeInstruction23x(@Nonnull Opcode opcode, + int registerA, + int registerB, + int registerC) { + return new ImmutableInstruction23x(opcode, registerA, registerB, registerC); + } + + public ImmutableInstruction30t makeInstruction30t(@Nonnull Opcode opcode, + int codeOffset) { + return new ImmutableInstruction30t(opcode, codeOffset); + } + + public ImmutableInstruction31c makeInstruction31c(@Nonnull Opcode opcode, + int registerA, + @Nonnull Reference reference) { + return new ImmutableInstruction31c(opcode, registerA, reference); + } + + public ImmutableInstruction31i makeInstruction31i(@Nonnull Opcode opcode, + int registerA, + int literal) { + return new ImmutableInstruction31i(opcode, registerA, literal); + } + + public ImmutableInstruction31t makeInstruction31t(@Nonnull Opcode opcode, + int registerA, + int codeOffset) { + return new ImmutableInstruction31t(opcode, registerA, codeOffset); + } + + public ImmutableInstruction32x makeInstruction32x(@Nonnull Opcode opcode, + int registerA, + int registerB) { + return new ImmutableInstruction32x(opcode, registerA, registerB); + } + + public ImmutableInstruction35c makeInstruction35c(@Nonnull Opcode opcode, + int registerCount, + int registerC, + int registerD, + int registerE, + int registerF, + int registerG, + @Nonnull Reference reference) { + return new ImmutableInstruction35c(opcode, registerCount, registerC, registerD, registerE, registerF, registerG, + reference); + } + + public ImmutableInstruction3rc makeInstruction3rc(@Nonnull Opcode opcode, + int startRegister, + int registerCount, + @Nonnull Reference reference) { + return new ImmutableInstruction3rc(opcode, startRegister, registerCount, reference); + } + + public ImmutableInstruction51l makeInstruction51l(@Nonnull Opcode opcode, + int registerA, + long literal) { + return new ImmutableInstruction51l(opcode, registerA, literal); + } + + public ImmutableSparseSwitchPayload makeSparseSwitchPayload(@Nullable List switchElements) { + return new ImmutableSparseSwitchPayload(switchElements); + } + + public ImmutablePackedSwitchPayload makePackedSwitchPayload(@Nullable List switchElements) { + return new ImmutablePackedSwitchPayload(switchElements); + } + + public ImmutableArrayPayload makeArrayPayload(int elementWidth, + @Nullable List arrayElements) { + return new ImmutableArrayPayload(elementWidth, arrayElements); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java index 5f2e7ef3..2cfa1b5c 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java @@ -38,7 +38,7 @@ import org.jf.dexlib2.iface.reference.MethodReference; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.List; +import java.util.Collection; public final class MethodUtil { private static int directMask = AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() | @@ -76,7 +76,8 @@ public final class MethodUtil { return getParameterRegisterCount(methodRef.getParameterTypes(), isStatic); } - public static int getParameterRegisterCount(@Nonnull List parameterTypes, boolean isStatic) { + public static int getParameterRegisterCount(@Nonnull Collection parameterTypes, + boolean isStatic) { int regCount = 0; for (CharSequence paramType: parameterTypes) { int firstChar = paramType.charAt(0); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationDirectoryPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationDirectoryPool.java deleted file mode 100644 index 44dbd200..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationDirectoryPool.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.*; -import org.jf.dexlib2.iface.ClassDef; -import org.jf.dexlib2.iface.Field; -import org.jf.dexlib2.iface.Method; -import org.jf.dexlib2.iface.MethodParameter; -import org.jf.util.CollectionUtils; -import org.jf.util.ExceptionWithContext; - -import javax.annotation.Nonnull; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class AnnotationDirectoryPool { - @Nonnull private final Map internedAnnotationDirectoryItems = Maps.newHashMap(); - @Nonnull private final Map nonInternedAnnotationDirectoryOffsetMap = Maps.newHashMap(); - @Nonnull private final List nonInternedAnnotationDirectoryItems = Lists.newArrayList(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; - - public AnnotationDirectoryPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; - } - - public void intern(@Nonnull ClassDef classDef) { - Key key = new Key(classDef); - - if (key.hasNonClassAnnotations()) { - nonInternedAnnotationDirectoryItems.add(key); - } else if (key.hasClassAnnotations()) { - Integer prev = internedAnnotationDirectoryItems.put(key, 0); - if (prev != null) { - // we don't need to re-intern the contents - return; - } - } else { - // it's empty. nothing to do. - return; - } - - dexFile.annotationSetPool.intern(classDef.getAnnotations()); - for (Field field: Iterables.filter(classDef.getFields(), FIELD_HAS_ANNOTATION)) { - dexFile.annotationSetPool.intern(field.getAnnotations()); - } - - for (Method method: classDef.getMethods()) { - if (METHOD_HAS_ANNOTATION.apply(method)) { - dexFile.annotationSetPool.intern(method.getAnnotations()); - } - if (METHOD_HAS_PARAMETER_ANNOTATION.apply(method)) { - dexFile.annotationSetRefPool.intern(method); - } - } - } - - public int getOffset(@Nonnull ClassDef classDef) { - Integer offset = nonInternedAnnotationDirectoryOffsetMap.get(classDef.getType()); - if (offset == null) { - Key key = new Key(classDef); - if (!key.hasNonClassAnnotations()) { - offset = internedAnnotationDirectoryItems.get(key); - } - if (offset == null) { - if (key.hasClassAnnotations() || key.hasNonClassAnnotations()) { - throw new ExceptionWithContext("Annotation directory not found for class %s.", classDef.getType()); - } - offset = 0; - } - } - return offset; - } - - public int getNumItems() { - return internedAnnotationDirectoryItems.size() + nonInternedAnnotationDirectoryItems.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - // we'll write out the interned items first - List directoryItems = Lists.newArrayList(internedAnnotationDirectoryItems.keySet()); - Collections.sort(directoryItems); - - writer.align(); - sectionOffset = writer.getPosition(); - for (Key key: directoryItems) { - writer.align(); - internedAnnotationDirectoryItems.put(key, writer.getPosition()); - writer.writeInt(dexFile.annotationSetPool.getOffset(key.classDef.getAnnotations())); - writer.writeInt(0); - writer.writeInt(0); - writer.writeInt(0); - } - - // now, write out the non-internable items - directoryItems = nonInternedAnnotationDirectoryItems; - Collections.sort(directoryItems); - for (Key key: directoryItems) { - writer.align(); - nonInternedAnnotationDirectoryOffsetMap.put(key.classDef.getType(), writer.getPosition()); - writer.writeInt(dexFile.annotationSetPool.getOffset(key.classDef.getAnnotations())); - writer.writeInt(key.fieldAnnotationCount); - writer.writeInt(key.methodAnnotationCount); - writer.writeInt(key.parameterAnnotationCount); - - List sortedFieldsWithAnnotations = Ordering.natural().immutableSortedCopy( - Iterables.filter(key.classDef.getFields(), FIELD_HAS_ANNOTATION)); - - for (Field field: sortedFieldsWithAnnotations) { - writer.writeInt(dexFile.fieldPool.getIndex(field)); - writer.writeInt(dexFile.annotationSetPool.getOffset(field.getAnnotations())); - } - - List sortedMethods = Ordering.natural().immutableSortedCopy( - Iterables.filter( - key.classDef.getMethods(), - Predicates.or(METHOD_HAS_ANNOTATION, METHOD_HAS_PARAMETER_ANNOTATION))); - - // It's safe to assume that we don't have any duplicate methods here. We would have already caught that and - // thrown an exception - for (Method method: sortedMethods) { - if (METHOD_HAS_ANNOTATION.apply(method)) { - writer.writeInt(dexFile.methodPool.getIndex(method)); - writer.writeInt(dexFile.annotationSetPool.getOffset(method.getAnnotations())); - } - } - - for (Method method: sortedMethods) { - if (METHOD_HAS_PARAMETER_ANNOTATION.apply(method)) { - writer.writeInt(dexFile.methodPool.getIndex(method)); - writer.writeInt(dexFile.annotationSetRefPool.getOffset(method)); - } - } - } - } - - private static final Predicate FIELD_HAS_ANNOTATION = new Predicate() { - @Override - public boolean apply(Field input) { return input.getAnnotations().size() > 0; } - }; - - private static final Predicate METHOD_HAS_ANNOTATION = new Predicate() { - @Override - public boolean apply(Method input) { return input.getAnnotations().size() > 0; } - }; - - private static final Predicate METHOD_HAS_PARAMETER_ANNOTATION = new Predicate() { - @Override - public boolean apply(Method input) { - for (MethodParameter parameter: input.getParameters()) { - if (parameter.getAnnotations().size() > 0) { - return true; - } - } - return false; - } - }; - - private static class Key implements Comparable { - @Nonnull private final ClassDef classDef; - private final int fieldAnnotationCount; - private final int methodAnnotationCount; - private final int parameterAnnotationCount; - - public Key(@Nonnull ClassDef classDef) { - this.classDef = classDef; - this.fieldAnnotationCount = Iterables.size(Iterables.filter(classDef.getFields(), FIELD_HAS_ANNOTATION)); - int methodAnnotationCount = 0; - int parameterAnnotationCount = 0; - for (Method method: classDef.getMethods()) { - if (METHOD_HAS_ANNOTATION.apply(method)) { - methodAnnotationCount++; - } - if (METHOD_HAS_PARAMETER_ANNOTATION.apply(method)) { - parameterAnnotationCount++; - } - } - this.methodAnnotationCount = methodAnnotationCount; - this.parameterAnnotationCount = parameterAnnotationCount; - } - - public boolean hasClassAnnotations() { - return classDef.getAnnotations().size() > 0; - } - - public boolean hasNonClassAnnotations() { - return fieldAnnotationCount > 0 || - methodAnnotationCount > 0 || - parameterAnnotationCount > 0; - } - - @Override - public int hashCode() { - // hashCode is only used for internable items - those that only have class annotations. - return classDef.getAnnotations().hashCode(); - } - - @Override - public boolean equals(Object o) { - // equals is only used for internable items - those that only have class annotations - if (o instanceof Key) { - Key other = (Key)o; - if (classDef.getAnnotations().size() != other.classDef.getAnnotations().size()) { - return false; - } - return Iterables.elementsEqual(classDef.getAnnotations(), other.classDef.getAnnotations()); - } - return false; - } - - @Override - public int compareTo(Key o) { - // compareTo will only be called on keys of the same internability. An internable key will not be compared - // with a non-internable one. - if (hasNonClassAnnotations()) { - return classDef.getType().compareTo(o.classDef.getType()); - } - return CollectionUtils.compareAsSet(classDef.getAnnotations(), o.classDef.getAnnotations()); - } - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationPool.java deleted file mode 100644 index 7ff1ebd0..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationPool.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.jf.dexlib2.base.BaseAnnotationElement; -import org.jf.dexlib2.iface.Annotation; -import org.jf.dexlib2.iface.AnnotationElement; -import org.jf.util.ExceptionWithContext; - -import javax.annotation.Nonnull; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.SortedSet; - -public class AnnotationPool { - @Nonnull private final Map internedAnnotations = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; - - public AnnotationPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; - } - - public void intern(@Nonnull Annotation annotation) { - Integer prev = internedAnnotations.put(annotation, 0); - if (prev == null) { - dexFile.typePool.intern(annotation.getType()); - for (AnnotationElement element: annotation.getElements()) { - dexFile.stringPool.intern(element.getName()); - dexFile.internEncodedValue(element.getValue()); - } - } - } - - public int getOffset(@Nonnull Annotation annotation) { - Integer offset = internedAnnotations.get(annotation); - if (offset == null) { - throw new ExceptionWithContext("Annotation not found."); - } - return offset; - } - - public int getNumItems() { - return internedAnnotations.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - List annotations = Lists.newArrayList(internedAnnotations.keySet()); - Collections.sort(annotations); - - sectionOffset = writer.getPosition(); - for (Annotation annotation: annotations) { - internedAnnotations.put(annotation, writer.getPosition()); - writer.writeUbyte(annotation.getVisibility()); - writer.writeUleb128(dexFile.typePool.getIndex(annotation.getType())); - writer.writeUleb128(annotation.getElements().size()); - - SortedSet sortedElements = - ImmutableSortedSet.copyOf(BaseAnnotationElement.BY_NAME, annotation.getElements()); - for (AnnotationElement element: sortedElements) { - writer.writeUleb128(dexFile.stringPool.getIndex(element.getName())); - dexFile.writeEncodedValue(writer, element.getValue()); - } - } - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSection.java new file mode 100644 index 00000000..0d8179ce --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSection.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import javax.annotation.Nonnull; +import java.util.Collection; + +public interface AnnotationSection + extends OffsetSection { + int getVisibility(@Nonnull AnnotationKey key); + @Nonnull TypeKey getType(@Nonnull AnnotationKey key); + @Nonnull Collection getElements(@Nonnull AnnotationKey key); + + @Nonnull StringKey getElementName(@Nonnull AnnotationElement element); + @Nonnull EncodedValue getElementValue(@Nonnull AnnotationElement element); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetPool.java deleted file mode 100644 index c4a5c0b5..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetPool.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Ordering; -import org.jf.dexlib2.base.BaseAnnotation; -import org.jf.dexlib2.iface.Annotation; -import org.jf.util.CollectionUtils; -import org.jf.util.ExceptionWithContext; - -import javax.annotation.Nonnull; -import java.io.IOException; -import java.util.*; - -public class AnnotationSetPool { - @Nonnull private final Map, Integer> internedAnnotationSetItems = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; - - public AnnotationSetPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; - } - - public void intern(@Nonnull Set annotationSet) { - if (annotationSet.size() > 0) { - Integer prev = internedAnnotationSetItems.put(annotationSet, 0); - if (prev == null) { - for (Annotation annotation: annotationSet) { - dexFile.annotationPool.intern(annotation); - } - } - } - } - - public int getOffset(@Nonnull Set annotationSet) { - if (annotationSet.size() == 0) { - return 0; - } - Integer offset = internedAnnotationSetItems.get(annotationSet); - if (offset == null) { - throw new ExceptionWithContext("Annotation set not found."); - } - return offset; - } - - public int getNumItems() { - return internedAnnotationSetItems.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - List> annotationSets = - Lists.newArrayList(internedAnnotationSetItems.keySet()); - Collections.sort(annotationSets, CollectionUtils.listComparator(Ordering.natural())); - - writer.align(); - sectionOffset = writer.getPosition(); - for (Set annotationSet: annotationSets) { - SortedSet sortedAnnotationSet = ImmutableSortedSet.copyOf(BaseAnnotation.BY_TYPE, - annotationSet); - writer.align(); - internedAnnotationSetItems.put(annotationSet, writer.getPosition()); - writer.writeInt(annotationSet.size()); - for (Annotation annotation: sortedAnnotationSet) { - writer.writeInt(dexFile.annotationPool.getOffset(annotation)); - } - } - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetRefSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetRefSection.java new file mode 100644 index 00000000..7f9104a9 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetRefSection.java @@ -0,0 +1,40 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import javax.annotation.Nonnull; +import java.util.Collection; + +public interface AnnotationSetRefSection + extends NullableOffsetSection { + @Nonnull Collection getAnnotationSets(@Nonnull AnnotationSetRefKey key); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetSection.java new file mode 100644 index 00000000..e42f067a --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetSection.java @@ -0,0 +1,42 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.iface.Annotation; + +import javax.annotation.Nonnull; +import java.util.Collection; + +public interface AnnotationSetSection + extends NullableOffsetSection { + @Nonnull Collection getAnnotations(@Nonnull AnnotationSetKey key); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/ClassDefPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/ClassDefPool.java deleted file mode 100644 index fbeac516..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/ClassDefPool.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import com.google.common.collect.*; -import org.jf.dexlib2.iface.ClassDef; -import org.jf.dexlib2.iface.Field; -import org.jf.dexlib2.iface.Method; -import org.jf.dexlib2.util.ReferenceUtil; -import org.jf.util.ExceptionWithContext; - -import javax.annotation.Nonnull; -import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -public class ClassDefPool { - public final static int CLASS_DEF_ITEM_SIZE = 0x20; - - @Nonnull private final Map internedClassDefItems = Maps.newHashMap(); - @Nonnull private final Map nameToClassDef = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - - private int indexSectionOffset = -1; - private int dataSectionOffset = -1; - private int classDataCount = 0; - - public ClassDefPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; - } - - public void intern(@Nonnull ClassDef classDef) { - Boolean prev = internedClassDefItems.put(classDef, false); - if (prev != null) { - // item is already interned, no need to do it again - // TODO: add additional handling for the case of interning a modified class - return; - } - - dexFile.typePool.intern(classDef.getType()); - dexFile.typePool.internNullable(classDef.getSuperclass()); - dexFile.typeListPool.intern(ImmutableSortedSet.copyOf(classDef.getInterfaces())); - dexFile.stringPool.internNullable(classDef.getSourceFile()); - dexFile.encodedArrayPool.intern(classDef); - boolean hasClassData = false; - - HashSet fields = new HashSet(); - for (Field field: classDef.getFields()) { - hasClassData = true; - String fieldDescriptor = ReferenceUtil.getShortFieldDescriptor(field); - if (!fields.add(fieldDescriptor)) { - throw new ExceptionWithContext("Multiple definitions for field %s->%s", - classDef.getType(), fieldDescriptor); - } - dexFile.fieldPool.intern(field); - } - - HashSet methods = new HashSet(); - for (Method method: classDef.getMethods()) { - hasClassData = true; - String methodDescriptor = ReferenceUtil.getShortMethodDescriptor(method); - if (!methods.add(methodDescriptor)) { - throw new ExceptionWithContext("Multiple definitions for method %s->%s", - classDef.getType(), methodDescriptor); - } - dexFile.methodPool.intern(method); - dexFile.codeItemPool.intern(method); - } - - dexFile.annotationDirectoryPool.intern(classDef); - if (hasClassData) { - classDataCount++; - } - - nameToClassDef.put(classDef.getType(), classDef); - } - - public int getIndexedSectionSize() { - return internedClassDefItems.size() * CLASS_DEF_ITEM_SIZE; - } - - public int getNumClassDefItems() { - return internedClassDefItems.size(); - } - - public int getNumClassDataItems() { - return classDataCount; - } - - public int getIndexSectionOffset() { - if (indexSectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return indexSectionOffset; - } - - public int getDataSectionOffset() { - if (dataSectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return dataSectionOffset; - } - - public void write(@Nonnull DexWriter indexWriter, @Nonnull DexWriter offsetWriter) throws IOException { - List classDefs = Lists.newArrayList(internedClassDefItems.keySet()); - - indexSectionOffset = indexWriter.getPosition(); - dataSectionOffset = offsetWriter.getPosition(); - - for (ClassDef classDef: classDefs) { - writeClass(indexWriter, offsetWriter, classDef); - } - } - - private void writeClass(DexWriter indexWriter, DexWriter offsetWriter, ClassDef classDef) throws IOException { - if (classDef == null) { - // class does not exist in this dex file, cannot write it - return; - } - - Boolean alreadyWritten = internedClassDefItems.put(classDef, true); - if (alreadyWritten != null && alreadyWritten) { - // class has already been written, no need to write it - return; - } - - // first, try to write a superclass - ClassDef superClassDef = nameToClassDef.get(classDef.getSuperclass()); - writeClass(indexWriter, offsetWriter, superClassDef); - - // then, try to write interfaces - for (String iface: classDef.getInterfaces()) { - ClassDef interfaceClassDef = nameToClassDef.get(iface); - writeClass(indexWriter, offsetWriter, interfaceClassDef); - } - - // and finally, write the class itself - writeSelf(indexWriter, offsetWriter, classDef); - } - - private void writeSelf(DexWriter indexWriter, DexWriter offsetWriter, ClassDef classDef) throws IOException { - indexWriter.writeInt(dexFile.typePool.getIndex(classDef)); - indexWriter.writeInt(classDef.getAccessFlags()); - indexWriter.writeInt(dexFile.typePool.getIndexNullable(classDef.getSuperclass())); - - indexWriter.writeInt(dexFile.typeListPool.getOffset(ImmutableSortedSet.copyOf(classDef.getInterfaces()))); - - if (classDef.getSourceFile() != null) { - indexWriter.writeInt(dexFile.stringPool.getIndexNullable(classDef.getSourceFile())); - } else { - indexWriter.writeInt(-1); // TODO: this should be replaced by NO_INDEX - } - - indexWriter.writeInt(dexFile.annotationDirectoryPool.getOffset(classDef)); - - ClassDataItem classDataItem = new ClassDataItem(classDef); - - if (classDataItem.hasData()) { - indexWriter.writeInt(offsetWriter.getPosition()); - classDataItem.write(offsetWriter); - } else { - indexWriter.writeInt(0); - } - - if (classDataItem.hasStaticFields()) { - indexWriter.writeInt(dexFile.encodedArrayPool.getOffset(classDef)); - } else { - indexWriter.writeInt(0); - } - } - - private class ClassDataItem { - ClassDef classDef; - - int numStaticFields = 0; - int numInstanceFields = 0; - int numDirectMethods = 0; - int numVirtualMethods = 0; - - private ClassDataItem(ClassDef classDef) { - this.classDef = classDef; - - numStaticFields = Iterables.size(classDef.getStaticFields()); - numInstanceFields = Iterables.size(classDef.getInstanceFields()); - numDirectMethods = Iterables.size(classDef.getDirectMethods()); - numVirtualMethods = Iterables.size(classDef.getVirtualMethods()); - } - - private boolean hasData() { - return (numStaticFields > 0 || numInstanceFields > 0 || numDirectMethods > 0 || numVirtualMethods > 0); - } - - private boolean hasStaticFields() { - return numStaticFields > 0; - } - - private void writeStaticFields(DexWriter writer) throws IOException { - int lastIdx = 0; - - Iterable sortedStaticFields = - Ordering.natural().immutableSortedCopy(classDef.getStaticFields()); - - for (Field field: sortedStaticFields) { - int idx = dexFile.fieldPool.getIndex(field); - writer.writeUleb128(idx - lastIdx); - lastIdx = idx; - - writer.writeUleb128(field.getAccessFlags()); - } - } - - private void writeInstanceFields(DexWriter writer) throws IOException { - int lastIdx = 0; - - Iterable sortedInstanceFields = - Ordering.natural().immutableSortedCopy(classDef.getInstanceFields()); - - for (Field field: sortedInstanceFields) { - int idx = dexFile.fieldPool.getIndex(field); - writer.writeUleb128(idx - lastIdx); - lastIdx = idx; - - writer.writeUleb128(field.getAccessFlags()); - } - } - - private void writeDirectMethods(DexWriter writer) throws IOException { - int lastIdx = 0; - - Iterable sortedDirectMethods = - Ordering.natural().immutableSortedCopy(classDef.getDirectMethods()); - - for (Method method: sortedDirectMethods) { - int idx = dexFile.methodPool.getIndex(method); - writer.writeUleb128(idx - lastIdx); - lastIdx = idx; - - writer.writeUleb128(method.getAccessFlags()); - writer.writeUleb128(dexFile.codeItemPool.getOffset(method)); - } - } - - private void writeVirtualMethods(DexWriter writer) throws IOException { - int lastIdx = 0; - - Iterable sortedVirtualMethods = - Ordering.natural().immutableSortedCopy(classDef.getVirtualMethods()); - - for (Method method: sortedVirtualMethods) { - int idx = dexFile.methodPool.getIndex(method); - writer.writeUleb128(idx - lastIdx); - lastIdx = idx; - - writer.writeUleb128(method.getAccessFlags()); - writer.writeUleb128(dexFile.codeItemPool.getOffset(method)); - } - } - - private void write(DexWriter writer) throws IOException { - writer.writeUleb128(numStaticFields); - writer.writeUleb128(numInstanceFields); - writer.writeUleb128(numDirectMethods); - writer.writeUleb128(numVirtualMethods); - - writeStaticFields(writer); - writeInstanceFields(writer); - writeDirectMethods(writer); - writeVirtualMethods(writer); - } - } - -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/ClassSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/ClassSection.java new file mode 100644 index 00000000..15eca7db --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/ClassSection.java @@ -0,0 +1,89 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.iface.TryBlock; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public interface ClassSection extends IndexSection { + @Nonnull Collection getSortedClasses(); + + @Nullable Map.Entry getClassEntryByType(@Nullable TypeKey key); + + @Nonnull TypeKey getType(@Nonnull ClassKey key); + int getAccessFlags(@Nonnull ClassKey key); + @Nullable TypeKey getSuperclass(@Nonnull ClassKey key); + @Nullable TypeListKey getSortedInterfaces(@Nonnull ClassKey key); + @Nullable StringKey getSourceFile(@Nonnull ClassKey key); + @Nullable EncodedArrayKey getStaticInitializers(@Nonnull ClassKey key); + + @Nonnull Collection getSortedStaticFields(@Nonnull ClassKey key); + @Nonnull Collection getSortedInstanceFields(@Nonnull ClassKey key); + @Nonnull Collection getSortedDirectMethods(@Nonnull ClassKey key); + @Nonnull Collection getSortedVirtualMethods(@Nonnull ClassKey key); + + int getFieldAccessFlags(@Nonnull FieldKey key); + int getMethodAccessFlags(@Nonnull MethodKey key); + + @Nullable AnnotationSetKey getClassAnnotations(@Nonnull ClassKey key); + @Nullable AnnotationSetKey getFieldAnnotations(@Nonnull FieldKey key); + @Nullable AnnotationSetKey getMethodAnnotations(@Nonnull MethodKey key); + @Nullable AnnotationSetRefKey getParameterAnnotations(@Nonnull MethodKey key); + + @Nullable Iterable getDebugItems(@Nonnull MethodKey key); + @Nullable Iterable getParameterNames(@Nonnull MethodKey key); + + int getRegisterCount(@Nonnull MethodKey key); + @Nullable Iterable getInstructions(@Nonnull MethodKey key); + @Nonnull List> getTryBlocks(@Nonnull MethodKey key); + @Nullable TypeKey getExceptionType(@Nonnull ExceptionHandler handler); + + void setAnnotationDirectoryOffset(@Nonnull ClassKey key, int offset); + int getAnnotationDirectoryOffset(@Nonnull ClassKey key); + + void setCodeItemOffset(@Nonnull MethodKey key, int offset); + int getCodeItemOffset(@Nonnull MethodKey key); + + void setDebugItemOffset(@Nonnull MethodKey key, int offset); + int getDebugItemOffset(@Nonnull MethodKey key); + + void writeDebugItem(@Nonnull DebugWriter writer, DebugItem debugItem) throws IOException; +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/CodeItemPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/CodeItemPool.java deleted file mode 100644 index 75219048..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/CodeItemPool.java +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.jf.dexlib2.ReferenceType; -import org.jf.dexlib2.iface.ExceptionHandler; -import org.jf.dexlib2.iface.Method; -import org.jf.dexlib2.iface.MethodImplementation; -import org.jf.dexlib2.iface.TryBlock; -import org.jf.dexlib2.iface.instruction.Instruction; -import org.jf.dexlib2.iface.instruction.ReferenceInstruction; -import org.jf.dexlib2.iface.instruction.SwitchElement; -import org.jf.dexlib2.iface.instruction.formats.*; -import org.jf.dexlib2.iface.reference.*; -import org.jf.dexlib2.util.MethodUtil; -import org.jf.dexlib2.util.ReferenceUtil; -import org.jf.dexlib2.writer.util.InstructionWriteUtil; -import org.jf.dexlib2.writer.util.TryListBuilder; -import org.jf.util.ExceptionWithContext; - -import javax.annotation.Nonnull; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class CodeItemPool { - @Nonnull private final Map codeItemOffsetMap = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; - - public CodeItemPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; - } - - public void intern(@Nonnull Method method) { - // TODO: can we have parameter names (in the debug_info_item), without having any other sort of method implementation - // this also handles parameter names, which aren't directly tied to the MethodImplementation, even though the debug items are - boolean hasDebugInfo = dexFile.debugInfoPool.intern(method); - boolean hasInstruction = false; - - MethodImplementation methodImpl = method.getImplementation(); - if (methodImpl != null) { - for (Instruction instruction: methodImpl.getInstructions()) { - hasInstruction = true; - if (instruction instanceof ReferenceInstruction) { - Reference reference = ((ReferenceInstruction)instruction).getReference(); - switch (instruction.getOpcode().referenceType) { - case ReferenceType.STRING: - dexFile.stringPool.intern((StringReference) reference); - break; - case ReferenceType.TYPE: - dexFile.typePool.intern((TypeReference)reference); - break; - case ReferenceType.FIELD: - dexFile.fieldPool.intern((FieldReference) reference); - break; - case ReferenceType.METHOD: - dexFile.methodPool.intern((MethodReference)reference); - break; - default: - throw new ExceptionWithContext("Unrecognized reference type: %d", - instruction.getOpcode().referenceType); - } - } - } - - List tryBlocks = methodImpl.getTryBlocks(); - if (!hasInstruction && tryBlocks.size() > 0) { - throw new ExceptionWithContext("Method %s has no instructions, but has try blocks.", - ReferenceUtil.getMethodDescriptor(method)); - } - - for (TryBlock tryBlock: methodImpl.getTryBlocks()) { - for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) { - dexFile.typePool.internNullable(handler.getExceptionType()); - } - } - } - - if (hasDebugInfo || hasInstruction) { - codeItemOffsetMap.put(method, 0); - } - } - - public int getOffset(@Nonnull Method method) { - Integer offset = codeItemOffsetMap.get(method); - if (offset == null) { - return 0; - } - return offset; - } - - public int getNumItems() { - return codeItemOffsetMap.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - ByteArrayOutputStream ehBuf = new ByteArrayOutputStream(); - - writer.align(); - sectionOffset = writer.getPosition(); - List methods = Lists.newArrayList(codeItemOffsetMap.keySet()); - Collections.sort(methods); - for (Method method: methods) { - writer.align(); - codeItemOffsetMap.put(method, writer.getPosition()); - - MethodImplementation methodImpl = method.getImplementation(); - if (methodImpl != null) { - writer.writeUshort(methodImpl.getRegisterCount()); - writer.writeUshort(MethodUtil.getParameterRegisterCount(method, MethodUtil.isStatic(method))); - - InstructionWriteUtil instrWriteUtil = new InstructionWriteUtil(methodImpl, dexFile.stringPool); - writer.writeUshort(instrWriteUtil.getOutParamCount()); - - List tryBlocks = TryListBuilder.massageTryBlocks(methodImpl.getTryBlocks()); - writer.writeUshort(tryBlocks.size()); - writer.writeInt(dexFile.debugInfoPool.getOffset(method)); - writer.writeInt(instrWriteUtil.getCodeUnitCount()); - - for (Instruction instruction: instrWriteUtil.getInstructions()) { - switch (instruction.getOpcode().format) { - case Format10t: - writeFormat10t(writer, (Instruction10t)instruction); - break; - case Format10x: - writeFormat10x(writer, (Instruction10x)instruction); - break; - case Format11n: - writeFormat11n(writer, (Instruction11n)instruction); - break; - case Format11x: - writeFormat11x(writer, (Instruction11x)instruction); - break; - case Format12x: - writeFormat12x(writer, (Instruction12x)instruction); - break; - case Format20t: - writeFormat20t(writer, (Instruction20t)instruction); - break; - case Format21c: - writeFormat21c(writer, (Instruction21c)instruction); - break; - case Format21ih: - writeFormat21ih(writer, (Instruction21ih)instruction); - break; - case Format21lh: - writeFormat21lh(writer, (Instruction21lh)instruction); - break; - case Format21s: - writeFormat21s(writer, (Instruction21s)instruction); - break; - case Format21t: - writeFormat21t(writer, (Instruction21t)instruction); - break; - case Format22b: - writeFormat22b(writer, (Instruction22b)instruction); - break; - case Format22c: - writeFormat22c(writer, (Instruction22c)instruction); - break; - case Format22s: - writeFormat22s(writer, (Instruction22s)instruction); - break; - case Format22t: - writeFormat22t(writer, (Instruction22t)instruction); - break; - case Format22x: - writeFormat22x(writer, (Instruction22x)instruction); - break; - case Format23x: - writeFormat23x(writer, (Instruction23x)instruction); - break; - case Format30t: - writeFormat30t(writer, (Instruction30t)instruction); - break; - case Format31c: - writeFormat31c(writer, (Instruction31c)instruction); - break; - case Format31i: - writeFormat31i(writer, (Instruction31i)instruction); - break; - case Format31t: - writeFormat31t(writer, (Instruction31t)instruction); - break; - case Format32x: - writeFormat32x(writer, (Instruction32x)instruction); - break; - case Format35c: - writeFormat35c(writer, (Instruction35c)instruction); - break; - case Format3rc: - writeFormat3rc(writer, (Instruction3rc)instruction); - break; - case Format51l: - writeFormat51l(writer, (Instruction51l)instruction); - break; - case ArrayPayload: - writeArrayPayload(writer, (ArrayPayload)instruction); - break; - case SparseSwitchPayload: - writeSparseSwitchPayload(writer, (SparseSwitchPayload)instruction); - break; - case PackedSwitchPayload: - writePackedSwitchPayload(writer, (PackedSwitchPayload)instruction); - break; - default: - throw new ExceptionWithContext("Unexpected format: %s", instruction.getOpcode().format); - } - } - - if (tryBlocks.size() > 0) { - writer.align(); - - // filter out unique lists of exception handlers - Map, Integer> exceptionHandlerOffsetMap = Maps.newHashMap(); - for (TryBlock tryBlock: tryBlocks) { - exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0); - } - DexWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size()); - - for (TryBlock tryBlock: tryBlocks) { - int startAddress = tryBlock.getStartCodeAddress(); - int endAddress = startAddress + tryBlock.getCodeUnitCount(); - - startAddress += instrWriteUtil.codeOffsetShift(startAddress); - endAddress += instrWriteUtil.codeOffsetShift(endAddress); - int tbCodeUnitCount = endAddress - startAddress; - - writer.writeInt(startAddress); - writer.writeUshort(tbCodeUnitCount); - - if (tryBlock.getExceptionHandlers().size() == 0) { - throw new ExceptionWithContext("No exception handlers for the try block!"); - } - - Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers()); - if (offset != 0) { - // exception handler has already been written out, just use it - writer.writeUshort(offset); - } else { - // if offset has not been set yet, we are about to write out a new exception handler - offset = ehBuf.size(); - writer.writeUshort(offset); - exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset); - - // check if the last exception handler is a catch-all and adjust the size accordingly - int ehSize = tryBlock.getExceptionHandlers().size(); - ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1); - if (ehLast.getExceptionType() == null) { - ehSize = ehSize * (-1) + 1; - } - - // now let's layout the exception handlers, assuming that catch-all is always last - DexWriter.writeSleb128(ehBuf, ehSize); - for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) { - String exceptionType = eh.getExceptionType(); - int codeAddress = eh.getHandlerCodeAddress(); - codeAddress += instrWriteUtil.codeOffsetShift(codeAddress); - - if (exceptionType != null) { - //regular exception handling - DexWriter.writeUleb128(ehBuf, dexFile.typePool.getIndex(exceptionType)); - DexWriter.writeUleb128(ehBuf, codeAddress); - } else { - //catch-all - DexWriter.writeUleb128(ehBuf, codeAddress); - } - } - } - } - - if (ehBuf.size() > 0) { - ehBuf.writeTo(writer); - ehBuf.reset(); - } - } - } - } - } - - private static int packNibbles(int a, int b) { - return (b << 4) | a; - } - - private int getReferenceIndex(ReferenceInstruction referenceInstruction) { - switch (referenceInstruction.getOpcode().referenceType) { - case ReferenceType.FIELD: - return dexFile.fieldPool.getIndex((FieldReference)referenceInstruction.getReference()); - case ReferenceType.METHOD: - return dexFile.methodPool.getIndex((MethodReference)referenceInstruction.getReference()); - case ReferenceType.STRING: - return dexFile.stringPool.getIndex((StringReference)referenceInstruction.getReference()); - case ReferenceType.TYPE: - return dexFile.typePool.getIndex((TypeReference)referenceInstruction.getReference()); - default: - throw new ExceptionWithContext("Unknown reference type: %d", - referenceInstruction.getOpcode().referenceType); - } - } - - public void writeFormat10t(@Nonnull DexWriter writer, @Nonnull Instruction10t instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getCodeOffset()); - } - - public void writeFormat10x(@Nonnull DexWriter writer, @Nonnull Instruction10x instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(0); - } - - public void writeFormat11n(@Nonnull DexWriter writer, @Nonnull Instruction11n instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(packNibbles(instruction.getRegisterA(), instruction.getNarrowLiteral())); - } - - public void writeFormat11x(@Nonnull DexWriter writer, @Nonnull Instruction11x instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - } - - public void writeFormat12x(@Nonnull DexWriter writer, @Nonnull Instruction12x instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); - } - - public void writeFormat20t(@Nonnull DexWriter writer, @Nonnull Instruction20t instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(0); - writer.writeShort(instruction.getCodeOffset()); - } - - public void writeFormat21c(@Nonnull DexWriter writer, @Nonnull Instruction21c instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.writeUshort(getReferenceIndex(instruction)); - } - - public void writeFormat21ih(@Nonnull DexWriter writer, @Nonnull Instruction21ih instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.writeShort(instruction.getHatLiteral()); - } - - public void writeFormat21lh(@Nonnull DexWriter writer, @Nonnull Instruction21lh instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.writeShort(instruction.getHatLiteral()); - } - - public void writeFormat21s(@Nonnull DexWriter writer, @Nonnull Instruction21s instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.writeShort(instruction.getNarrowLiteral()); - } - - public void writeFormat21t(@Nonnull DexWriter writer, @Nonnull Instruction21t instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.writeShort(instruction.getCodeOffset()); - } - - public void writeFormat22b(@Nonnull DexWriter writer, @Nonnull Instruction22b instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.write(instruction.getRegisterB()); - writer.write(instruction.getNarrowLiteral()); - } - - public void writeFormat22c(@Nonnull DexWriter writer, @Nonnull Instruction22c instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); - writer.writeUshort(getReferenceIndex(instruction)); - } - - public void writeFormat22s(@Nonnull DexWriter writer, @Nonnull Instruction22s instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); - writer.writeShort(instruction.getNarrowLiteral()); - } - - public void writeFormat22t(@Nonnull DexWriter writer, @Nonnull Instruction22t instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); - writer.writeShort(instruction.getCodeOffset()); - } - - public void writeFormat22x(@Nonnull DexWriter writer, @Nonnull Instruction22x instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.writeUshort(instruction.getRegisterB()); - } - - public void writeFormat23x(@Nonnull DexWriter writer, @Nonnull Instruction23x instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.write(instruction.getRegisterB()); - writer.write(instruction.getRegisterC()); - } - - public void writeFormat30t(@Nonnull DexWriter writer, @Nonnull Instruction30t instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(0); - writer.writeInt(instruction.getCodeOffset()); - } - - public void writeFormat31c(@Nonnull DexWriter writer, @Nonnull Instruction31c instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.writeInt(getReferenceIndex(instruction)); - } - - public void writeFormat31i(@Nonnull DexWriter writer, @Nonnull Instruction31i instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.writeInt(instruction.getNarrowLiteral()); - } - - public void writeFormat31t(@Nonnull DexWriter writer, @Nonnull Instruction31t instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.writeInt(instruction.getCodeOffset()); - } - - public void writeFormat32x(@Nonnull DexWriter writer, @Nonnull Instruction32x instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(0); - writer.writeUshort(instruction.getRegisterA()); - writer.writeUshort(instruction.getRegisterB()); - } - - public void writeFormat35c(@Nonnull DexWriter writer, @Nonnull Instruction35c instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount())); - writer.writeUshort(getReferenceIndex(instruction)); - writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD())); - writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF())); - } - - public void writeFormat3rc(@Nonnull DexWriter writer, @Nonnull Instruction3rc instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterCount()); - writer.writeUshort(getReferenceIndex(instruction)); - writer.writeUshort(instruction.getStartRegister()); - } - - public void writeFormat51l(@Nonnull DexWriter writer, @Nonnull Instruction51l instruction) throws IOException { - writer.write(instruction.getOpcode().value); - writer.write(instruction.getRegisterA()); - writer.writeLong(instruction.getWideLiteral()); - } - - public void writeArrayPayload(@Nonnull DexWriter writer, @Nonnull ArrayPayload instruction) throws IOException { - writer.writeUshort(instruction.getOpcode().value); - writer.writeUshort(instruction.getElementWidth()); - List elements = instruction.getArrayElements(); - writer.writeInt(elements.size()); - // TODO: validate that dalvik only allows these element widths - switch (instruction.getElementWidth()) { - case 1: - for (Number element: elements) { - writer.write(element.byteValue()); - } - break; - case 2: - for (Number element: elements) { - writer.writeShort(element.shortValue()); - } - break; - case 4: - for (Number element: elements) { - writer.writeInt(element.intValue()); - } - break; - case 8: - for (Number element: elements) { - writer.writeLong(element.longValue()); - } - break; - } - if ((writer.getPosition() & 1) != 0) { - writer.write(0); - } - } - - public void writeSparseSwitchPayload(@Nonnull DexWriter writer, @Nonnull SparseSwitchPayload instruction) - throws IOException { - writer.writeUbyte(0); - writer.writeUbyte(instruction.getOpcode().value >> 8); - List elements = instruction.getSwitchElements(); - writer.writeUshort(elements.size()); - for (SwitchElement element: elements) { - writer.writeInt(element.getKey()); - } - for (SwitchElement element: elements) { - writer.writeInt(element.getOffset()); - } - } - - public void writePackedSwitchPayload(@Nonnull DexWriter writer, @Nonnull PackedSwitchPayload instruction) - throws IOException { - writer.writeUbyte(0); - writer.writeUbyte(instruction.getOpcode().value >> 8); - List elements = instruction.getSwitchElements(); - writer.writeUshort(elements.size()); - if (elements.size() == 0) { - writer.writeInt(0); - } else { - writer.writeInt(elements.get(0).getKey()); - for (SwitchElement element: elements) { - writer.writeInt(element.getOffset()); - } - } - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/DebugInfoPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/DebugInfoPool.java deleted file mode 100644 index 6fd44979..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/DebugInfoPool.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.jf.dexlib2.DebugItemType; -import org.jf.dexlib2.iface.Method; -import org.jf.dexlib2.iface.MethodImplementation; -import org.jf.dexlib2.iface.MethodParameter; -import org.jf.dexlib2.iface.debug.*; -import org.jf.util.ExceptionWithContext; - -import javax.annotation.Nonnull; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class DebugInfoPool { - @Nonnull private final Map debugInfoOffsetMap = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; - - public DebugInfoPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; - } - - public boolean intern(@Nonnull Method method) { - boolean hasDebugInfo = false; - for (MethodParameter param: method.getParameters()) { - String paramName = param.getName(); - if (paramName != null) { - hasDebugInfo = true; - dexFile.stringPool.intern(paramName); - } - } - - MethodImplementation methodImpl = method.getImplementation(); - if (methodImpl != null) { - for (DebugItem debugItem: methodImpl.getDebugItems()) { - hasDebugInfo = true; - switch (debugItem.getDebugItemType()) { - case DebugItemType.START_LOCAL: - StartLocal startLocal = (StartLocal)debugItem; - dexFile.stringPool.internNullable(startLocal.getName()); - dexFile.typePool.internNullable(startLocal.getType()); - dexFile.stringPool.internNullable(startLocal.getSignature()); - break; - case DebugItemType.SET_SOURCE_FILE: - dexFile.stringPool.internNullable(((SetSourceFile) debugItem).getSourceFile()); - break; - } - } - } - - if (hasDebugInfo) { - debugInfoOffsetMap.put(method, 0); - } - return hasDebugInfo; - } - - public int getOffset(@Nonnull Method method) { - Integer offset = debugInfoOffsetMap.get(method); - if (offset == null) { - return 0; - } - return offset; - } - - public int getNumItems() { - return debugInfoOffsetMap.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - List methods = Lists.newArrayList(debugInfoOffsetMap.keySet()); - Collections.sort(methods); - - sectionOffset = writer.getPosition(); - for (Method method: methods) { - debugInfoOffsetMap.put(method, writer.getPosition()); - - int startingLineNumber = 0; - - MethodImplementation methodImpl = method.getImplementation(); - List debugItems = null; - - if (methodImpl != null) { - debugItems = Lists.newArrayList(methodImpl.getDebugItems()); - for (DebugItem item: debugItems) { - if (item.getDebugItemType() == DebugItemType.LINE_NUMBER) { - startingLineNumber = ((LineNumber)item).getLineNumber(); - break; - } - } - } - writer.writeUleb128(startingLineNumber); - - // TODO: do we need to write out all param names, even if the last n are null? - List parameters = method.getParameters(); - writer.writeUleb128(parameters.size()); - for (MethodParameter parameter: parameters) { - writer.writeUleb128(dexFile.stringPool.getIndexNullable(parameter.getName())+1); - } - - if (debugItems != null && debugItems.size() > 0) { - DebugWriter debugWriter = new DebugWriter(dexFile, writer, startingLineNumber); - for (DebugItem debugItem: debugItems) { - switch (debugItem.getDebugItemType()) { - case DebugItemType.START_LOCAL: - debugWriter.emitStartLocal((StartLocal)debugItem); - break; - case DebugItemType.END_LOCAL: - debugWriter.emitEndLocal((EndLocal)debugItem); - break; - case DebugItemType.RESTART_LOCAL: - debugWriter.emitRestartLocal((RestartLocal)debugItem); - break; - case DebugItemType.PROLOGUE_END: - debugWriter.emitPrologueEnd((PrologueEnd)debugItem); - break; - case DebugItemType.EPILOGUE_BEGIN: - debugWriter.emitEpilogueBegin((EpilogueBegin)debugItem); - break; - case DebugItemType.SET_SOURCE_FILE: - debugWriter.emitSetSourceFile((SetSourceFile)debugItem); - break; - case DebugItemType.LINE_NUMBER: - debugWriter.emitLineNumber((LineNumber)debugItem); - break; - default: - throw new ExceptionWithContext("Unexpected debug item type: %d", - debugItem.getDebugItemType()); - } - } - } - // write an END_SEQUENCE opcode, to end the debug item - writer.write(0); - } - } - - // TODO: add some validation here. - private static class DebugWriter { - - @Nonnull private final DexFile dexFile; - @Nonnull private final DexWriter writer; - private int currentAddress = 0; - private int currentLine; - - public DebugWriter(@Nonnull DexFile dexFile, @Nonnull DexWriter writer, int startLine) { - this.dexFile = dexFile; - this.writer = writer; - this.currentLine = startLine; - } - - private void emitAdvancePC(int address) throws IOException { - int addressDelta = address-currentAddress; - - if (addressDelta > 0) { - writer.write(1); - writer.writeUleb128(addressDelta); - currentAddress = address; - } - } - - private void emitAdvanceLine(int line) throws IOException { - int lineDelta = line-currentLine; - if (lineDelta != 0) { - writer.write(2); - writer.writeSleb128(lineDelta); - currentLine = line; - } - } - - public void emitLineNumber(@Nonnull LineNumber lineNumber) throws IOException { - int lineDelta = lineNumber.getLineNumber() - currentLine; - int addressDelta = lineNumber.getCodeAddress() - currentAddress; - - if (lineDelta < -4 || lineDelta > 10) { - emitAdvanceLine(lineNumber.getLineNumber()); - lineDelta = 0; - } - if ((lineDelta < 2 && addressDelta > 16) || (lineDelta > 1 && addressDelta > 15)) { - emitAdvancePC(lineNumber.getCodeAddress()); - addressDelta = 0; - } - - //TODO: need to handle the case when the line delta is larger than a signed int - emitSpecialOpcode(lineDelta, addressDelta); - } - - public void emitStartLocal(@Nonnull StartLocal startLocal) throws IOException { - int nameIndex = dexFile.stringPool.getIndexNullable(startLocal.getName()); - int typeIndex = dexFile.typePool.getIndexNullable(startLocal.getType()); - int signatureIndex = dexFile.stringPool.getIndexNullable(startLocal.getSignature()); - emitAdvancePC(startLocal.getCodeAddress()); - if (signatureIndex == -1) { - writer.write(3); - writer.writeUleb128(startLocal.getRegister()); - writer.writeUleb128(nameIndex+1); - writer.writeUleb128(typeIndex+1); - } else { - writer.write(4); - writer.writeUleb128(startLocal.getRegister()); - writer.writeUleb128(nameIndex+1); - writer.writeUleb128(typeIndex+1); - writer.writeUleb128(signatureIndex+1); - } - } - - public void emitEndLocal(@Nonnull EndLocal endLocal) throws IOException { - emitAdvancePC(endLocal.getCodeAddress()); - writer.write(5); - writer.writeUleb128(endLocal.getRegister()); - } - - public void emitRestartLocal(@Nonnull RestartLocal restartLocal) throws IOException { - emitAdvancePC(restartLocal.getCodeAddress()); - writer.write(6); - writer.writeUleb128(restartLocal.getRegister()); - } - - public void emitPrologueEnd(@Nonnull PrologueEnd prologueEnd) throws IOException { - emitAdvancePC(prologueEnd.getCodeAddress()); - writer.write(7); - } - - public void emitEpilogueBegin(@Nonnull EpilogueBegin epilogueBegin) throws IOException { - emitAdvancePC(epilogueBegin.getCodeAddress()); - writer.write(8); - } - - public void emitSetSourceFile(@Nonnull SetSourceFile setSourceFile) throws IOException { - emitAdvancePC(setSourceFile.getCodeAddress()); - writer.write(9); - writer.writeUleb128(dexFile.stringPool.getIndexNullable(setSourceFile.getSourceFile()) + 1); - } - - private static final int LINE_BASE = -4; - private static final int LINE_RANGE = 15; - private static final int FIRST_SPECIAL = 0x0a; - private void emitSpecialOpcode(int lineDelta, int addressDelta) throws IOException { - writer.write((byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE))); - currentLine += lineDelta; - currentAddress += addressDelta; - } - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/DebugWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/DebugWriter.java new file mode 100644 index 00000000..683cc152 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/DebugWriter.java @@ -0,0 +1,163 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.DebugItemType; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.IOException; + +public class DebugWriter { + @Nonnull private final StringSection stringSection; + @Nonnull private final TypeSection typeSection; + @Nonnull private final DexDataWriter writer; + private int currentAddress; + private int currentLine; + + DebugWriter(@Nonnull StringSection stringSection, + @Nonnull TypeSection typeSection, + @Nonnull DexDataWriter writer) { + this.stringSection = stringSection; + this.typeSection = typeSection; + this.writer = writer; + } + + void reset(int startLine) { + this.currentAddress = 0; + this.currentLine = startLine; + } + + public void writeStartLocal(int codeAddress, int register, + @Nullable StringKey name, + @Nullable TypeKey type, + @Nullable StringKey signature) throws IOException { + int nameIndex = stringSection.getNullableItemIndex(name); + int typeIndex = typeSection.getNullableItemIndex(type); + int signatureIndex = stringSection.getNullableItemIndex(signature); + + writeAdvancePC(codeAddress); + if (signatureIndex == DexWriter.NO_INDEX) { + writer.write(DebugItemType.START_LOCAL); + writer.writeUleb128(register); + writer.writeUleb128(nameIndex + 1); + writer.writeUleb128(typeIndex + 1); + } else { + writer.write(DebugItemType.START_LOCAL_EXTENDED); + writer.writeUleb128(register); + writer.writeUleb128(nameIndex + 1); + writer.writeUleb128(typeIndex + 1); + writer.writeUleb128(signatureIndex + 1); + } + } + + public void writeEndLocal(int codeAddress, int register) throws IOException { + writeAdvancePC(codeAddress); + writer.write(DebugItemType.END_LOCAL); + writer.writeUleb128(register); + } + + public void writeRestartLocal(int codeAddress, int register) throws IOException { + writeAdvancePC(codeAddress); + writer.write(DebugItemType.RESTART_LOCAL); + writer.writeUleb128(register); + } + + public void writePrologueEnd(int codeAddress) throws IOException { + writeAdvancePC(codeAddress); + writer.write(DebugItemType.PROLOGUE_END); + } + + public void writeEpilogueBegin(int codeAddress) throws IOException { + writeAdvancePC(codeAddress); + writer.write(DebugItemType.EPILOGUE_BEGIN); + } + + public void writeLineNumber(int codeAddress, int lineNumber) throws IOException { + int lineDelta = lineNumber - currentLine; + int addressDelta = codeAddress - currentAddress; + + if (addressDelta < 0) { + throw new ExceptionWithContext("debug info items must have non-decreasing code addresses"); + } + if (lineDelta < -4 || lineDelta > 10) { + writeAdvanceLine(lineNumber); + lineDelta = 0; + } // no else is intentional here. we might need to advance the PC as well as the line + if ((lineDelta < 2 && addressDelta > 16) || (lineDelta > 1 && addressDelta > 15)) { + writeAdvancePC(codeAddress); + addressDelta = 0; + } + + // we need to emit the special opcode even if both lineDelta and addressDelta are 0, otherwise a positions + // entry isn't generated + writeSpecialOpcode(lineDelta, addressDelta); + } + + public void writeSetSourceFile(int codeAddress, @Nullable StringKey sourceFile) throws IOException { + writeAdvancePC(codeAddress); + writer.write(DebugItemType.SET_SOURCE_FILE); + writer.writeUleb128(stringSection.getNullableItemIndex(sourceFile) + 1); + } + + private void writeAdvancePC(int address) throws IOException { + int addressDelta = address - currentAddress; + + if (addressDelta > 0) { + writer.write(1); + writer.writeUleb128(addressDelta); + currentAddress = address; + } /*else if (addressDelta < 0) { + throw new ExceptionWithContext("debug info items must have non-decreasing code addresses"); + }*/ + } + + private void writeAdvanceLine(int line) throws IOException { + int lineDelta = line - currentLine; + if (lineDelta != 0) { + writer.write(2); + writer.writeSleb128(lineDelta); + currentLine = line; + } + } + + private static final int LINE_BASE = -4; + private static final int LINE_RANGE = 15; + private static final int FIRST_SPECIAL = 0x0a; + + private void writeSpecialOpcode(int lineDelta, int addressDelta) throws IOException { + writer.write((byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE))); + currentLine += lineDelta; + currentAddress += addressDelta; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexDataWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexDataWriter.java new file mode 100644 index 00000000..0102b755 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexDataWriter.java @@ -0,0 +1,282 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class DexDataWriter extends BufferedOutputStream { + /** + * The position within the file that we will write to next. This is only updated when the buffer is flushed to the + * outputStream. + */ + private int filePosition; + + /** + * A temporary buffer that can be used for larger writes. Can be replaced with a larger buffer if needed. + * Must be at least 8 bytes + */ + private byte[] tempBuf = new byte[8]; + + /** A buffer of 0s to use for writing alignment values */ + private byte[] zeroBuf = new byte[3]; + + /** + * Construct a new DexWriter instance that writes to output. + * + * @param output An OutputStream to write the data to. + * @param filePosition The position within the file that OutputStream will write to. + */ + public DexDataWriter(@Nonnull OutputStream output, int filePosition) { + this(output, filePosition, 256 * 1024); + } + + public DexDataWriter(@Nonnull OutputStream output, int filePosition, int bufferSize) { + super(output, bufferSize); + + this.filePosition = filePosition; + } + + @Override + public void write(int b) throws IOException { + filePosition++; + super.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + filePosition += len; + super.write(b, off, len); + } + + public void writeLong(long value) throws IOException { + writeInt((int)value); + writeInt((int)(value >> 32)); + } + + public static void writeInt(OutputStream out, int value) throws IOException { + out.write(value); + out.write(value >> 8); + out.write(value >> 16); + out.write(value >> 24); + } + + public void writeInt(int value) throws IOException { + writeInt(this, value); + } + + public void writeShort(int value) throws IOException { + if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { + throw new ExceptionWithContext("Short value out of range: %d", value); + } + write(value); + write(value >> 8); + } + + public void writeUshort(int value) throws IOException { + if (value < 0 || value > 0xFFFF) { + throw new ExceptionWithContext("Unsigned short value out of range: %d", value); + } + write(value); + write(value >> 8); + } + + public void writeUbyte(int value) throws IOException { + if (value < 0 || value > 0xFF) { + throw new ExceptionWithContext("Unsigned byte value out of range: %d", value); + } + write(value); + } + + public static void writeUleb128(OutputStream out, int value) throws IOException { + while (value > 0x7f) { + out.write((value & 0x7f) | 0x80); + value >>>= 7; + } + out.write(value); + } + + public void writeUleb128(int value) throws IOException { + writeUleb128(this, value); + } + + public static void writeSleb128(OutputStream out, int value) throws IOException { + if (value >= 0) { + while (value > 0x3f) { + out.write((value & 0x7f) | 0x80); + value >>>= 7; + } + out.write(value & 0x7f); + } else { + while (value < -0x40) { + out.write((value & 0x7f) | 0x80); + value >>= 7; + } + out.write(value & 0x7f); + } + } + + public void writeSleb128(int value) throws IOException { + writeSleb128(this, value); + } + + public void writeEncodedValueHeader(int valueType, int valueArg) throws IOException { + write(valueType | (valueArg << 5)); + } + + public void writeEncodedInt(int valueType, int value) throws IOException { + int index = 0; + if (value >= 0) { + while (value > 0x7f) { + tempBuf[index++] = (byte)value; + value >>= 8; + } + } else { + while (value < -0x80) { + tempBuf[index++] = (byte)value; + value >>= 8; + } + } + tempBuf[index++] = (byte)value; + writeEncodedValueHeader(valueType, index-1); + write(tempBuf, 0, index); + } + + public void writeEncodedLong(int valueType, long value) throws IOException { + int index = 0; + if (value >= 0) { + while (value > 0x7f) { + tempBuf[index++] = (byte)value; + value >>= 8; + } + } else { + while (value < -0x80) { + tempBuf[index++] = (byte)value; + value >>= 8; + } + } + tempBuf[index++] = (byte)value; + writeEncodedValueHeader(valueType, index-1); + write(tempBuf, 0, index); + } + + public void writeEncodedUint(int valueType, int value) throws IOException { + int index = 0; + do { + tempBuf[index++] = (byte)value; + value >>>= 8; + } while (value != 0); + writeEncodedValueHeader(valueType, index-1); + write(tempBuf, 0, index); + } + + public void writeEncodedFloat(int valueType, float value) throws IOException { + writeRightZeroExtendedInt(valueType, Float.floatToRawIntBits(value)); + } + + protected void writeRightZeroExtendedInt(int valueType, int value) throws IOException { + int index = 3; + do { + tempBuf[index--] = (byte)((value & 0xFF000000) >>> 24); + value <<= 8; + } while (value != 0); + + int firstElement = index+1; + int encodedLength = 4-firstElement; + writeEncodedValueHeader(valueType, encodedLength - 1); + write(tempBuf, firstElement, encodedLength); + } + + public void writeEncodedDouble(int valueType, double value) throws IOException { + writeRightZeroExtendedLong(valueType, Double.doubleToRawLongBits(value)); + } + + protected void writeRightZeroExtendedLong(int valueType, long value) throws IOException { + int index = 7; + do { + tempBuf[index--] = (byte)((value & 0xFF00000000000000L) >>> 56); + value <<= 8; + } while (value != 0); + + int firstElement = index+1; + int encodedLength = 8-firstElement; + writeEncodedValueHeader(valueType, encodedLength - 1); + write(tempBuf, firstElement, encodedLength); + } + + public void writeString(String string) throws IOException { + int len = string.length(); + + // make sure we have enough room in the temporary buffer + if (tempBuf.length <= string.length()*3) { + tempBuf = new byte[string.length()*3]; + } + + final byte[] buf = tempBuf; + + int bufPos = 0; + for (int i = 0; i < len; i++) { + char c = string.charAt(i); + if ((c != 0) && (c < 0x80)) { + buf[bufPos++] = (byte)c; + } else if (c < 0x800) { + buf[bufPos++] = (byte)(((c >> 6) & 0x1f) | 0xc0); + buf[bufPos++] = (byte)((c & 0x3f) | 0x80); + } else { + buf[bufPos++] = (byte)(((c >> 12) & 0x0f) | 0xe0); + buf[bufPos++] = (byte)(((c >> 6) & 0x3f) | 0x80); + buf[bufPos++] = (byte)((c & 0x3f) | 0x80); + } + } + write(buf, 0, bufPos); + } + + public void align() throws IOException { + int zeros = (-getPosition()) & 3; + if (zeros > 0) { + write(zeroBuf, 0, zeros); + } + } + + public int getPosition() { + return filePosition; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexFile.java deleted file mode 100644 index 7b39fd1b..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexFile.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import org.jf.dexlib2.ValueType; -import org.jf.dexlib2.iface.AnnotationElement; -import org.jf.dexlib2.iface.ClassDef; -import org.jf.dexlib2.iface.value.*; -import org.jf.util.ExceptionWithContext; -import org.jf.util.RandomAccessFileOutputStream; - -import javax.annotation.Nonnull; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; -import java.util.Collection; -import java.util.Set; - -public class DexFile { - // package-private access for these - @Nonnull final StringPool stringPool = new StringPool(); - @Nonnull final TypePool typePool = new TypePool(this); - @Nonnull final FieldPool fieldPool = new FieldPool(this); - @Nonnull final ProtoPool protoPool = new ProtoPool(this); - @Nonnull final MethodPool methodPool = new MethodPool(this); - - @Nonnull final TypeListPool typeListPool = new TypeListPool(this); - @Nonnull final EncodedArrayPool encodedArrayPool = new EncodedArrayPool(this); - @Nonnull final AnnotationPool annotationPool = new AnnotationPool(this); - @Nonnull final AnnotationSetPool annotationSetPool = new AnnotationSetPool(this); - @Nonnull final AnnotationSetRefPool annotationSetRefPool = new AnnotationSetRefPool(this); - @Nonnull final AnnotationDirectoryPool annotationDirectoryPool = new AnnotationDirectoryPool(this); - @Nonnull final DebugInfoPool debugInfoPool = new DebugInfoPool(this); - @Nonnull final CodeItemPool codeItemPool = new CodeItemPool(this); - @Nonnull final ClassDefPool classDefPool = new ClassDefPool(this); - @Nonnull final MapItem mapItem = new MapItem(this); - @Nonnull final HeaderItem headerItem = new HeaderItem(this); - - private DexFile(Set classes) { - for (ClassDef classDef: classes) { - classDefPool.intern(classDef); - } - } - - public void writeEncodedValue(@Nonnull DexWriter writer, @Nonnull EncodedValue encodedValue) throws IOException { - int valueType = encodedValue.getValueType(); - switch (valueType) { - case ValueType.ANNOTATION: - AnnotationEncodedValue annotationEncodedValue = (AnnotationEncodedValue)encodedValue; - Collection annotationElements = annotationEncodedValue.getElements(); - writer.writeEncodedValueHeader(valueType, 0); - writer.writeUleb128(typePool.getIndex(annotationEncodedValue.getType())); - writer.writeUleb128(annotationElements.size()); - for (AnnotationElement element: annotationElements) { - writer.writeUleb128(stringPool.getIndex(element.getName())); - writeEncodedValue(writer, element.getValue()); - } - break; - case ValueType.ARRAY: - ArrayEncodedValue arrayEncodedValue = (ArrayEncodedValue)encodedValue; - Collection elements = arrayEncodedValue.getValue(); - writer.writeEncodedValueHeader(valueType, 0); - writer.writeUleb128(elements.size()); - for (EncodedValue element: elements) { - writeEncodedValue(writer, element); - } - break; - case ValueType.BOOLEAN: - writer.writeEncodedValueHeader(valueType, (((BooleanEncodedValue)encodedValue).getValue()?1:0)); - break; - case ValueType.BYTE: - writer.writeEncodedInt(valueType, ((ByteEncodedValue)encodedValue).getValue()); - break; - case ValueType.CHAR: - writer.writeEncodedUint(valueType, ((CharEncodedValue)encodedValue).getValue()); - break; - case ValueType.DOUBLE: - writer.writeEncodedDouble(valueType, ((DoubleEncodedValue)encodedValue).getValue()); - break; - case ValueType.ENUM: - writer.writeEncodedUint(valueType, fieldPool.getIndex(((EnumEncodedValue)encodedValue).getValue())); - break; - case ValueType.FIELD: - writer.writeEncodedUint(valueType, fieldPool.getIndex(((FieldEncodedValue)encodedValue).getValue())); - break; - case ValueType.FLOAT: - writer.writeEncodedFloat(valueType, ((FloatEncodedValue)encodedValue).getValue()); - break; - case ValueType.INT: - writer.writeEncodedInt(valueType, ((IntEncodedValue)encodedValue).getValue()); - break; - case ValueType.LONG: - writer.writeEncodedLong(valueType, ((LongEncodedValue)encodedValue).getValue()); - break; - case ValueType.METHOD: - writer.writeEncodedUint(valueType, methodPool.getIndex(((MethodEncodedValue)encodedValue).getValue())); - break; - case ValueType.NULL: - writer.write(valueType); - break; - case ValueType.SHORT: - writer.writeEncodedInt(valueType, ((ShortEncodedValue)encodedValue).getValue()); - break; - case ValueType.STRING: - writer.writeEncodedUint(valueType, stringPool.getIndex(((StringEncodedValue)encodedValue).getValue())); - break; - case ValueType.TYPE: - writer.writeEncodedUint(valueType, typePool.getIndex(((TypeEncodedValue)encodedValue).getValue())); - break; - default: - throw new ExceptionWithContext("Unrecognized value type: %d", encodedValue.getValueType()); - } - } - - public void internEncodedValue(@Nonnull EncodedValue encodedValue) { - switch (encodedValue.getValueType()) { - case ValueType.ARRAY: - ArrayEncodedValue arrayEncodedValue = (ArrayEncodedValue)encodedValue; - for (EncodedValue value: arrayEncodedValue.getValue()) { - internEncodedValue(value); - } - return; - case ValueType.ANNOTATION: - AnnotationEncodedValue annotationEncodedValue = (AnnotationEncodedValue)encodedValue; - typePool.intern(annotationEncodedValue.getType()); - for(AnnotationElement annotationElement: annotationEncodedValue.getElements()) { - stringPool.intern(annotationElement.getName()); - internEncodedValue(annotationElement.getValue()); - } - return; - case ValueType.STRING: - StringEncodedValue stringEncodedValue = (StringEncodedValue)encodedValue; - stringPool.intern(stringEncodedValue.getValue()); - return; - case ValueType.TYPE: - TypeEncodedValue typeEncodedValue = (TypeEncodedValue)encodedValue; - typePool.intern(typeEncodedValue.getValue()); - return; - case ValueType.ENUM: - EnumEncodedValue enumEncodedValue = (EnumEncodedValue)encodedValue; - fieldPool.intern(enumEncodedValue.getValue()); - return; - case ValueType.FIELD: - FieldEncodedValue fieldEncodedValue = (FieldEncodedValue)encodedValue; - fieldPool.intern(fieldEncodedValue.getValue()); - return; - case ValueType.METHOD: - MethodEncodedValue methodEncodedValue = (MethodEncodedValue)encodedValue; - methodPool.intern(methodEncodedValue.getValue()); - return; - default: - // nothing to do - break; - } - } - - private int getDataSectionOffset() { - return HeaderItem.HEADER_ITEM_SIZE + - stringPool.getIndexedSectionSize() + - typePool.getIndexedSectionSize() + - protoPool.getIndexedSectionSize() + - fieldPool.getIndexedSectionSize() + - methodPool.getIndexedSectionSize() + - classDefPool.getIndexedSectionSize(); - } - - private void writeTo(@Nonnull String path) throws IOException { - RandomAccessFile raf = new RandomAccessFile(path, "rw"); - raf.setLength(0); - try { - int dataSectionOffset = getDataSectionOffset(); - DexWriter headerWriter = outputAt(raf, 0); - DexWriter indexWriter = outputAt(raf, HeaderItem.HEADER_ITEM_SIZE); - DexWriter offsetWriter = outputAt(raf, dataSectionOffset); - try { - stringPool.write(indexWriter, offsetWriter); - typePool.write(indexWriter); - typeListPool.write(offsetWriter); - protoPool.write(indexWriter); - fieldPool.write(indexWriter); - methodPool.write(indexWriter); - encodedArrayPool.write(offsetWriter); - annotationPool.write(offsetWriter); - annotationSetPool.write(offsetWriter); - annotationSetRefPool.write(offsetWriter); - annotationDirectoryPool.write(offsetWriter); - debugInfoPool.write(offsetWriter); - codeItemPool.write(offsetWriter); - classDefPool.write(indexWriter, offsetWriter); - mapItem.write(offsetWriter); - headerItem.write(headerWriter, dataSectionOffset, offsetWriter.getPosition()); - } finally { - headerWriter.close(); - indexWriter.close(); - offsetWriter.close(); - } - FileChannel fileChannel = raf.getChannel(); - headerItem.updateSignature(fileChannel); - headerItem.updateChecksum(fileChannel); - } finally { - raf.close(); - } - } - - private static DexWriter outputAt(RandomAccessFile raf, int filePosition) throws IOException { - return new DexWriter(new RandomAccessFileOutputStream(raf, filePosition), filePosition); - } - - public static void writeTo(@Nonnull String path, @Nonnull org.jf.dexlib2.iface.DexFile input) throws IOException { - DexFile dexFile = new DexFile(input.getClasses()); - dexFile.writeTo(path); - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java index 5e0593d1..36fe3be6 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012, Google Inc. + * Copyright 2013, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,252 +31,1304 @@ package org.jf.dexlib2.writer; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Ordering; +import org.jf.dexlib2.AccessFlags; +import org.jf.dexlib2.base.BaseAnnotation; +import org.jf.dexlib2.dexbacked.raw.*; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.TryBlock; +import org.jf.dexlib2.iface.debug.LineNumber; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.formats.*; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.iface.reference.StringReference; +import org.jf.dexlib2.iface.reference.TypeReference; +import org.jf.dexlib2.util.MethodUtil; +import org.jf.dexlib2.writer.util.InstructionWriteUtil; +import org.jf.dexlib2.writer.util.TryListBuilder; import org.jf.util.ExceptionWithContext; +import org.jf.util.RandomAccessFileOutputStream; import javax.annotation.Nonnull; -import java.io.BufferedOutputStream; +import javax.annotation.Nullable; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.Map.Entry; +import java.util.zip.Adler32; -public class DexWriter extends BufferedOutputStream { - /** - * The position within the file that we will write to next. This is only updated when the buffer is flushed to the - * outputStream. - */ - private int filePosition; +public abstract class DexWriter< + StringKey extends CharSequence, StringRef extends StringReference, TypeKey extends CharSequence, + TypeRef extends TypeReference, ProtoKey extends Comparable, + FieldRefKey extends FieldReference, MethodRefKey extends MethodReference, + ClassKey extends Comparable, + AnnotationKey extends Annotation, AnnotationSetKey, AnnotationSetRefKey, + TypeListKey, EncodedArrayKey, + FieldKey extends FieldRefKey, MethodKey extends MethodRefKey, EncodedValue, AnnotationElement, + DebugItem extends org.jf.dexlib2.iface.debug.DebugItem, + Insn extends Instruction, ExceptionHandler extends org.jf.dexlib2.iface.ExceptionHandler> { + public static final int NO_INDEX = -1; + public static final int NO_OFFSET = 0; + + protected int stringIndexSectionOffset = NO_OFFSET; + protected int typeSectionOffset = NO_OFFSET; + protected int protoSectionOffset = NO_OFFSET; + protected int fieldSectionOffset = NO_OFFSET; + protected int methodSectionOffset = NO_OFFSET; + protected int classIndexSectionOffset = NO_OFFSET; + + protected int stringDataSectionOffset = NO_OFFSET; + protected int classDataSectionOffset = NO_OFFSET; + protected int typeListSectionOffset = NO_OFFSET; + protected int encodedArraySectionOffset = NO_OFFSET; + protected int annotationSectionOffset = NO_OFFSET; + protected int annotationSetSectionOffset = NO_OFFSET; + protected int annotationSetRefSectionOffset = NO_OFFSET; + protected int annotationDirectorySectionOffset = NO_OFFSET; + protected int debugSectionOffset = NO_OFFSET; + protected int codeSectionOffset = NO_OFFSET; + protected int mapSectionOffset = NO_OFFSET; + + protected int numAnnotationDirectoryPoolItems = 0; + protected int numDebugInfoPoolItems = 0; + protected int numCodeItemPoolItems = 0; + protected int numClassDataItems = 0; + + protected InstructionFactory instructionFactory; + + protected StringSection stringSection; + protected TypeSection typeSection; + protected ProtoSection protoSection; + protected FieldSection fieldSection; + protected MethodSection methodSection; + protected ClassSection classSection; + + protected TypeListSection typeListSection; + protected AnnotationSection annotationSection; + protected AnnotationSetSection annotationSetSection; + protected AnnotationSetRefSection annotationSetRefSection; + + protected EncodedArraySection encodedArraySection; + + protected DexWriter(InstructionFactory instructionFactory, + StringSection stringSection, + TypeSection typeSection, + ProtoSection protoSection, + FieldSection fieldSection, + MethodSection methodSection, + ClassSection classSection, + TypeListSection typeListSection, + AnnotationSection annotationSection, + AnnotationSetSection annotationSetSection, + AnnotationSetRefSection annotationSetRefSection, + EncodedArraySection encodedArraySection) { + this.instructionFactory = instructionFactory; + this.stringSection = stringSection; + this.typeSection = typeSection; + this.protoSection = protoSection; + this.fieldSection = fieldSection; + this.methodSection = methodSection; + this.classSection = classSection; + this.typeListSection = typeListSection; + this.annotationSection = annotationSection; + this.annotationSetSection = annotationSetSection; + this.annotationSetRefSection = annotationSetRefSection; + this.encodedArraySection = encodedArraySection; + } + + protected abstract void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer, + @Nonnull EncodedValue encodedValue) throws IOException; + + private static Comparator toStringKeyComparator = + new Comparator() { + @Override public int compare(Entry o1, Entry o2) { + return o1.getKey().toString().compareTo(o2.getKey().toString()); + } + }; + + private static > Comparator> comparableKeyComparator() { + return new Comparator>() { + @Override public int compare(Entry o1, Entry o2) { + return o1.getKey().compareTo(o2.getKey()); + } + }; + } + + protected class InternalEncodedValueWriter extends EncodedValueWriter { + private InternalEncodedValueWriter(@Nonnull DexDataWriter writer) { + super(writer, stringSection, typeSection, fieldSection, methodSection, annotationSection); + } + + @Override protected void writeEncodedValue(@Nonnull EncodedValue encodedValue) throws IOException { + DexWriter.this.writeEncodedValue(this, encodedValue); + } + } + + private int getDataSectionOffset() { + return HeaderItem.ITEM_SIZE + + stringSection.getItems().size() * StringIdItem.ITEM_SIZE + + typeSection.getItems().size() * TypeIdItem.ITEM_SIZE + + protoSection.getItems().size() * ProtoIdItem.ITEM_SIZE + + fieldSection.getItems().size() * FieldIdItem.ITEM_SIZE + + methodSection.getItems().size() * MethodIdItem.ITEM_SIZE + + classSection.getItems().size() * ClassDefItem.ITEM_SIZE; + } + + public void writeTo(String path) throws IOException { + RandomAccessFile raf = new RandomAccessFile(path, "rw"); + raf.setLength(0); + try { + int dataSectionOffset = getDataSectionOffset(); + DexDataWriter headerWriter = outputAt(raf, 0); + DexDataWriter indexWriter = outputAt(raf, HeaderItem.ITEM_SIZE); + DexDataWriter offsetWriter = outputAt(raf, dataSectionOffset); + try { + writeStrings(indexWriter, offsetWriter); + writeTypes(indexWriter); + writeTypeLists(offsetWriter); + writeProtos(indexWriter); + writeFields(indexWriter); + writeMethods(indexWriter); + writeEncodedArrays(offsetWriter); + writeAnnotations(offsetWriter); + writeAnnotationSets(offsetWriter); + writeAnnotationSetRefs(offsetWriter); + writeAnnotationDirectories(offsetWriter); + writeDebugItems(offsetWriter); + writeCodeItems(offsetWriter); + writeClasses(indexWriter, offsetWriter); + writeMapItem(offsetWriter); + writeHeader(headerWriter, dataSectionOffset, offsetWriter.getPosition()); + } finally { + headerWriter.close(); + indexWriter.close(); + offsetWriter.close(); + } + FileChannel fileChannel = raf.getChannel(); + updateSignature(fileChannel); + updateChecksum(fileChannel); + } finally { + raf.close(); + } + } + + private void updateSignature(FileChannel fileChannel) throws IOException { + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + + ByteBuffer buffer = ByteBuffer.allocate(128 * 1024); + fileChannel.position(HeaderItem.HEADER_SIZE_OFFSET); + int bytesRead = fileChannel.read(buffer); + while (bytesRead >= 0) { + buffer.rewind(); + md.update(buffer); + buffer.clear(); + bytesRead = fileChannel.read(buffer); + } + + byte[] signature = md.digest(); + if (signature.length != HeaderItem.SIGNATURE_SIZE) { + throw new RuntimeException("unexpected digest write: " + signature.length + " bytes"); + } + + // write signature + fileChannel.position(HeaderItem.SIGNATURE_OFFSET); + fileChannel.write(ByteBuffer.wrap(signature)); + + // flush + fileChannel.force(false); + } + + private void updateChecksum(FileChannel fileChannel) throws IOException { + Adler32 a32 = new Adler32(); + + ByteBuffer buffer = ByteBuffer.allocate(128 * 1024); + fileChannel.position(HeaderItem.SIGNATURE_OFFSET); + int bytesRead = fileChannel.read(buffer); + while (bytesRead >= 0) { + a32.update(buffer.array(), 0, bytesRead); + buffer.clear(); + bytesRead = fileChannel.read(buffer); + } + + // write checksum, utilizing logic in DexWriter to write the integer value properly + fileChannel.position(HeaderItem.CHECKSUM_OFFSET); + int checksum = (int) a32.getValue(); + ByteArrayOutputStream checksumBuf = new ByteArrayOutputStream(); + DexDataWriter.writeInt(checksumBuf, checksum); + fileChannel.write(ByteBuffer.wrap(checksumBuf.toByteArray())); + + // flush + fileChannel.force(false); + } + + private static DexDataWriter outputAt(RandomAccessFile raf, int filePosition) throws IOException { + return new DexDataWriter(new RandomAccessFileOutputStream(raf, filePosition), filePosition); + } + + private void writeStrings(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter) throws IOException { + stringIndexSectionOffset = indexWriter.getPosition(); + stringDataSectionOffset = offsetWriter.getPosition(); + int index = 0; + List> stringEntries = Lists.newArrayList(stringSection.getItems()); + Collections.sort(stringEntries, toStringKeyComparator); + + for (Map.Entry entry: stringEntries) { + entry.setValue(index++); + indexWriter.writeInt(offsetWriter.getPosition()); + String stringValue = entry.getKey().toString(); + offsetWriter.writeUleb128(stringValue.length()); + offsetWriter.writeString(stringValue); + offsetWriter.write(0); + } + } + + private void writeTypes(@Nonnull DexDataWriter writer) throws IOException { + typeSectionOffset = writer.getPosition(); + int index = 0; + + List> typeEntries = Lists.newArrayList(typeSection.getItems()); + Collections.sort(typeEntries, toStringKeyComparator); + + for (Map.Entry entry : typeEntries) { + entry.setValue(index++); + writer.writeInt(stringSection.getItemIndex(typeSection.getString(entry.getKey()))); + } + } + + private void writeProtos(@Nonnull DexDataWriter writer) throws IOException { + protoSectionOffset = writer.getPosition(); + int index = 0; + + List> protoEntries = Lists.newArrayList(protoSection.getItems()); + Collections.sort(protoEntries, DexWriter.comparableKeyComparator()); + + for (Map.Entry entry: protoEntries) { + entry.setValue(index++); + ProtoKey key = entry.getKey(); + writer.writeInt(stringSection.getItemIndex(protoSection.getShorty(key))); + writer.writeInt(typeSection.getItemIndex(protoSection.getReturnType(key))); + writer.writeInt(typeListSection.getNullableItemOffset(protoSection.getParameters(key))); + } + } + + private void writeFields(@Nonnull DexDataWriter writer) throws IOException { + fieldSectionOffset = writer.getPosition(); + int index = 0; + + List> fieldEntries = Lists.newArrayList(fieldSection.getItems()); + Collections.sort(fieldEntries, DexWriter.comparableKeyComparator()); + + for (Map.Entry entry: fieldEntries) { + entry.setValue(index++); + FieldRefKey key = entry.getKey(); + writer.writeUshort(typeSection.getItemIndex(fieldSection.getDefiningClass(key))); + writer.writeUshort(typeSection.getItemIndex(fieldSection.getFieldType(key))); + writer.writeInt(stringSection.getItemIndex(fieldSection.getName(key))); + } + } + + private void writeMethods(@Nonnull DexDataWriter writer) throws IOException { + methodSectionOffset = writer.getPosition(); + int index = 0; + + List> methodEntries = Lists.newArrayList(methodSection.getItems()); + Collections.sort(methodEntries, DexWriter.comparableKeyComparator()); + + for (Map.Entry entry: methodEntries) { + entry.setValue(index++); + MethodRefKey key = entry.getKey(); + writer.writeUshort(typeSection.getItemIndex(methodSection.getDefiningClass(key))); + writer.writeUshort(protoSection.getItemIndex(methodSection.getPrototype(key))); + writer.writeInt(stringSection.getItemIndex(methodSection.getName(key))); + } + } + + private void writeClasses(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter) throws IOException { + classIndexSectionOffset = indexWriter.getPosition(); + classDataSectionOffset = offsetWriter.getPosition(); + + List> classEntries = Lists.newArrayList(classSection.getItems()); + Collections.sort(classEntries, DexWriter.comparableKeyComparator()); + + int index = 0; + for (Map.Entry key: classEntries) { + index = writeClass(indexWriter, offsetWriter, index, key); + } + } /** - * A temporary buffer that can be used for larger writes. Can be replaced with a larger buffer if needed. - * Must be at least 8 bytes - */ - private byte[] tempBuf = new byte[8]; - - /** A buffer of 0s to use for writing alignment values */ - private byte[] zeroBuf = new byte[3]; - - /** - * Construct a new DexWriter instance that writes to output. + * Writes out the class_def_item and class_data_item for the given class. * - * @param output An OutputStream to write the data to. - * @param filePosition The position within the file that OutputStream will write to. + * This will recursively write out any unwritten superclass/interface before writing the class itself, as per the + * dex specification. + * + * @return the index for the next class to be written */ - public DexWriter(@Nonnull OutputStream output, int filePosition) { - this(output, filePosition, 256 * 1024); - } - - public DexWriter(@Nonnull OutputStream output, int filePosition, int bufferSize) { - super(output, bufferSize); - - this.filePosition = filePosition; - } - - @Override - public void write(int b) throws IOException { - filePosition++; - super.write(b); - } - - @Override - public void write(byte[] b) throws IOException { - write(b, 0, b.length); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - filePosition += len; - super.write(b, off, len); - } - - public void writeLong(long value) throws IOException { - writeInt((int)value); - writeInt((int)(value >> 32)); - } - - public static void writeInt(OutputStream out, int value) throws IOException { - out.write(value); - out.write(value >> 8); - out.write(value >> 16); - out.write(value >> 24); - } - - public void writeInt(int value) throws IOException { - writeInt(this, value); - } - - public void writeShort(int value) throws IOException { - if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { - throw new ExceptionWithContext("Short value out of range: %d", value); + private int writeClass(@Nonnull DexDataWriter indexWriter, @Nonnull DexDataWriter offsetWriter, + int nextIndex, @Nullable Map.Entry entry) throws IOException { + if (entry == null) { + // class does not exist in this dex file, cannot write it + return nextIndex; } - write(value); - write(value >> 8); - } - public void writeUshort(int value) throws IOException { - if (value < 0 || value > 0xFFFF) { - throw new ExceptionWithContext("Unsigned short value out of range: %d", value); + if (entry.getValue() != NO_INDEX) { + // class has already been written, no need to write it + return nextIndex; } - write(value); - write(value >> 8); - } - public void writeUbyte(int value) throws IOException { - if (value < 0 || value > 0xFF) { - throw new ExceptionWithContext("Unsigned byte value out of range: %d", value); + ClassKey key = entry.getKey(); + + // set a bogus index, to make sure we don't recuse and double-write it + entry.setValue(0); + + // first, try to write the superclass + Map.Entry superEntry = + classSection.getClassEntryByType(classSection.getSuperclass(key)); + nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, superEntry); + + // then, try to write interfaces + for (TypeKey interfaceTypeKey: typeListSection.getTypes(classSection.getSortedInterfaces(key))) { + Map.Entry interfaceEntry = classSection.getClassEntryByType(interfaceTypeKey); + nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, interfaceEntry); } - write(value); - } - public static void writeUleb128(OutputStream out, int value) throws IOException { - while (value > 0x7f) { - out.write((value & 0x7f) | 0x80); - value >>>= 7; - } - out.write(value); - } + // now set the index for real + entry.setValue(nextIndex++); - public void writeUleb128(int value) throws IOException { - writeUleb128(this, value); - } + // and finally, write the class itself + // first, the class_def_item + indexWriter.writeInt(typeSection.getItemIndex(classSection.getType(key))); + indexWriter.writeInt(classSection.getAccessFlags(key)); + indexWriter.writeInt(typeSection.getNullableItemIndex(classSection.getSuperclass(key))); + indexWriter.writeInt(typeListSection.getNullableItemOffset(classSection.getSortedInterfaces(key))); + indexWriter.writeInt(stringSection.getNullableItemIndex(classSection.getSourceFile(key))); + indexWriter.writeInt(classSection.getAnnotationDirectoryOffset(key)); - public static void writeSleb128(OutputStream out, int value) throws IOException { - if (value >= 0) { - while (value > 0x3f) { - out.write((value & 0x7f) | 0x80); - value >>>= 7; - } - out.write(value & 0x7f); + Collection staticFields = classSection.getSortedStaticFields(key); + Collection instanceFields = classSection.getSortedInstanceFields(key); + Collection directMethods = classSection.getSortedDirectMethods(key); + Collection virtualMethods = classSection.getSortedVirtualMethods(key); + boolean classHasData = staticFields.size() > 0 || + instanceFields.size() > 0 || + directMethods.size() > 0 || + virtualMethods.size() > 0; + + if (classHasData) { + indexWriter.writeInt(offsetWriter.getPosition()); } else { - while (value < -0x40) { - out.write((value & 0x7f) | 0x80); - value >>= 7; - } - out.write(value & 0x7f); + indexWriter.writeInt(0); + } + + indexWriter.writeInt(encodedArraySection.getNullableItemOffset(classSection.getStaticInitializers(key))); + + // now write the class_data_item + if (classHasData) { + numClassDataItems++; + + offsetWriter.writeUleb128(staticFields.size()); + offsetWriter.writeUleb128(instanceFields.size()); + offsetWriter.writeUleb128(directMethods.size()); + offsetWriter.writeUleb128(virtualMethods.size()); + + writeEncodedFields(offsetWriter, staticFields); + writeEncodedFields(offsetWriter, instanceFields); + writeEncodedMethods(offsetWriter, directMethods); + writeEncodedMethods(offsetWriter, virtualMethods); + } + + return nextIndex; + } + + private void writeEncodedFields(@Nonnull DexDataWriter writer, @Nonnull Collection fields) + throws IOException { + int prevIndex = 0; + for (FieldKey key: fields) { + int index = fieldSection.getItemIndex(key); + writer.writeUleb128(index-prevIndex); + writer.writeUleb128(classSection.getFieldAccessFlags(key)); + prevIndex = index; } } - public void writeSleb128(int value) throws IOException { - writeSleb128(this, value); + private void writeEncodedMethods(@Nonnull DexDataWriter writer, @Nonnull Collection methods) + throws IOException { + int prevIndex = 0; + for (MethodKey key: methods) { + int index = methodSection.getItemIndex(key); + writer.writeUleb128(index-prevIndex); + writer.writeUleb128(classSection.getMethodAccessFlags(key)); + writer.writeUleb128(classSection.getCodeItemOffset(key)); + prevIndex = index; + } } - public void writeEncodedValueHeader(int valueType, int valueArg) throws IOException { - write(valueType | (valueArg << 5)); - } + private void writeTypeLists(@Nonnull DexDataWriter writer) throws IOException { + writer.align(); + typeListSectionOffset = writer.getPosition(); + for (Map.Entry entry: typeListSection.getItems()) { + writer.align(); + entry.setValue(writer.getPosition()); - public void writeEncodedInt(int valueType, int value) throws IOException { - int index = 0; - if (value >= 0) { - while (value > 0x7f) { - tempBuf[index++] = (byte)value; - value >>= 8; + Collection types = typeListSection.getTypes(entry.getKey()); + writer.writeInt(types.size()); + for (TypeKey typeKey: types) { + writer.writeUshort(typeSection.getItemIndex(typeKey)); } + } + } + + private void writeEncodedArrays(@Nonnull DexDataWriter writer) throws IOException { + InternalEncodedValueWriter encodedValueWriter = new InternalEncodedValueWriter(writer); + encodedArraySectionOffset = writer.getPosition(); + for (Map.Entry entry: encodedArraySection.getItems()) { + entry.setValue(writer.getPosition()); + + Collection elements = encodedArraySection.getElements(entry.getKey()); + + writer.writeUleb128(elements.size()); + for (EncodedValue value: elements) { + writeEncodedValue(encodedValueWriter, value); + } + } + } + + private void writeAnnotations(@Nonnull DexDataWriter writer) throws IOException { + InternalEncodedValueWriter encodedValueWriter = new InternalEncodedValueWriter(writer); + + annotationSectionOffset = writer.getPosition(); + for (Map.Entry entry: annotationSection.getItems()) { + entry.setValue(writer.getPosition()); + + AnnotationKey key = entry.getKey(); + + writer.writeUbyte(annotationSection.getVisibility(key)); + writer.writeUleb128(typeSection.getItemIndex(annotationSection.getType(key))); + + Collection elements = annotationSection.getElements(key); + writer.writeUleb128(elements.size()); + + for (AnnotationElement element: elements) { + writer.writeUleb128(stringSection.getItemIndex(annotationSection.getElementName(element))); + writeEncodedValue(encodedValueWriter, annotationSection.getElementValue(element)); + } + } + } + + + private void writeAnnotationSets(@Nonnull DexDataWriter writer) throws IOException { + writer.align(); + annotationSetSectionOffset = writer.getPosition(); + for (Map.Entry entry: annotationSetSection.getItems()) { + Collection annotations = Ordering.from(BaseAnnotation.BY_TYPE) + .immutableSortedCopy(annotationSetSection.getAnnotations(entry.getKey())); + + writer.align(); + entry.setValue(writer.getPosition()); + writer.writeInt(annotations.size()); + for (AnnotationKey annotationKey: annotations) { + writer.writeInt(annotationSection.getItemOffset(annotationKey)); + } + } + } + + private void writeAnnotationSetRefs(@Nonnull DexDataWriter writer) throws IOException { + writer.align(); + annotationSetRefSectionOffset = writer.getPosition(); + for (Map.Entry entry: annotationSetRefSection.getItems()) { + writer.align(); + entry.setValue(writer.getPosition()); + + Collection annotationSets = annotationSetRefSection.getAnnotationSets(entry.getKey()); + + writer.writeInt(annotationSets.size()); + for (AnnotationSetKey annotationSetKey: annotationSets) { + writer.writeInt(annotationSetSection.getItemOffset(annotationSetKey)); + } + } + } + + private void writeAnnotationDirectories(@Nonnull DexDataWriter writer) throws IOException { + writer.align(); + annotationDirectorySectionOffset = writer.getPosition(); + HashMap internedItems = Maps.newHashMap(); + + ByteBuffer tempBuffer = ByteBuffer.allocate(65536); + tempBuffer.order(ByteOrder.LITTLE_ENDIAN); + + for (ClassKey key: classSection.getSortedClasses()) { + // first, we write the field/method/parameter items to a temporary buffer, so that we can get a count + // of each type, and determine if we even need to write an annotation directory for this class + + Collection staticFields = classSection.getSortedStaticFields(key); + Collection instanceFields = classSection.getSortedInstanceFields(key); + Collection directMethods = classSection.getSortedDirectMethods(key); + Collection virtualMethods = classSection.getSortedVirtualMethods(key); + + // this is how much space we'll need if every field and method has annotations. + int maxSize = (staticFields.size() + instanceFields.size()) * 8 + + (directMethods.size() + virtualMethods.size()) * 16; + if (maxSize > tempBuffer.capacity()) { + tempBuffer = ByteBuffer.allocate(maxSize); + tempBuffer.order(ByteOrder.LITTLE_ENDIAN); + } + + tempBuffer.clear(); + + int fieldAnnotations = 0; + int methodAnnotations = 0; + int parameterAnnotations = 0; + + // the following logic for merging fields/methods is rather verbose and repetitive, but it's intentionally + // so, for efficiency (rather than adding yet another layer of indirection, to simplify it) + { + Iterator staticIterator = staticFields.iterator(); + Iterator instanceIterator = instanceFields.iterator(); + + FieldKey nextStaticField; + int nextStaticFieldIndex = 0; + AnnotationSetKey nextStaticFieldAnnotationSetKey = null; + FieldKey nextInstanceField; + int nextInstanceFieldIndex = 0; + AnnotationSetKey nextInstanceFieldAnnotationSetKey = null; + + try { + do { + nextStaticField = staticIterator.next(); + nextStaticFieldIndex = fieldSection.getItemIndex(nextStaticField); + nextStaticFieldAnnotationSetKey = classSection.getFieldAnnotations(nextStaticField); + } while (nextStaticFieldAnnotationSetKey == null); + } catch (NoSuchElementException ex) { + nextStaticField = null; + } + try { + do { + nextInstanceField = instanceIterator.next(); + nextInstanceFieldIndex = fieldSection.getItemIndex(nextInstanceField); + nextInstanceFieldAnnotationSetKey = + classSection.getFieldAnnotations(nextInstanceField); + } while (nextInstanceFieldAnnotationSetKey == null); + } catch (NoSuchElementException ex) { + nextInstanceField = null; + } + + while (true) { + int nextFieldIndex; + AnnotationSetKey nextFieldAnnotationSetKey; + + if (nextStaticField == null) { + if (nextInstanceField == null) { + break; + } + + nextFieldIndex = nextInstanceFieldIndex; + nextFieldAnnotationSetKey = nextInstanceFieldAnnotationSetKey; + try { + do { + nextInstanceField = instanceIterator.next(); + nextInstanceFieldIndex = fieldSection.getItemIndex(nextInstanceField); + nextInstanceFieldAnnotationSetKey = + classSection.getFieldAnnotations(nextInstanceField); + } while (nextInstanceFieldAnnotationSetKey == null); + } catch (NoSuchElementException ex) { + nextInstanceField = null; + } + } else { + if (nextInstanceField == null || nextStaticFieldIndex < nextInstanceFieldIndex) { + nextFieldIndex = nextStaticFieldIndex; + nextFieldAnnotationSetKey = nextStaticFieldAnnotationSetKey; + try { + do { + nextStaticField = staticIterator.next(); + nextStaticFieldIndex = fieldSection.getItemIndex(nextStaticField); + nextStaticFieldAnnotationSetKey = + classSection.getFieldAnnotations(nextStaticField); + } while (nextStaticFieldAnnotationSetKey == null); + } catch (NoSuchElementException ex) { + nextStaticField = null; + } + } else { + nextFieldIndex = nextInstanceFieldIndex; + nextFieldAnnotationSetKey = nextInstanceFieldAnnotationSetKey; + try { + do { + nextInstanceField = instanceIterator.next(); + nextInstanceFieldIndex = fieldSection.getItemIndex(nextInstanceField); + nextInstanceFieldAnnotationSetKey = + classSection.getFieldAnnotations(nextInstanceField); + } while (nextInstanceFieldAnnotationSetKey == null); + } catch (NoSuchElementException ex) { + nextInstanceField = null; + } + } + } + + fieldAnnotations++; + tempBuffer.putInt(nextFieldIndex); + tempBuffer.putInt(annotationSetSection.getItemOffset(nextFieldAnnotationSetKey)); + } + } + + { + // weeee! now do it all again, except for methods this time + Iterator directIterator = classSection.getSortedDirectMethods(key).iterator(); + Iterator virtualIterator = classSection.getSortedVirtualMethods(key).iterator(); + + MethodKey nextDirectMethod; + int nextDirectMethodIndex = 0; + AnnotationSetKey nextDirectMethodAnnotationKey = null; + MethodKey nextVirtualMethod; + int nextVirtualMethodIndex = 0; + AnnotationSetKey nextVirtualMethodAnnotationKey = null; + + try { + do { + nextDirectMethod = directIterator.next(); + nextDirectMethodIndex = methodSection.getItemIndex(nextDirectMethod); + nextDirectMethodAnnotationKey = classSection.getMethodAnnotations(nextDirectMethod); + } while (nextDirectMethodAnnotationKey == null); + } catch (NoSuchElementException ex) { + nextDirectMethod = null; + } + try { + do { + nextVirtualMethod = virtualIterator.next(); + nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod); + nextVirtualMethodAnnotationKey = + classSection.getMethodAnnotations(nextVirtualMethod); + } while (nextVirtualMethodAnnotationKey == null); + } catch (NoSuchElementException ex) { + nextVirtualMethod = null; + } + + while (true) { + int nextMethodIndex; + AnnotationSetKey nextAnnotationKey; + + if (nextDirectMethod == null) { + if (nextVirtualMethod == null) { + break; + } + + nextMethodIndex = nextVirtualMethodIndex; + nextAnnotationKey = nextVirtualMethodAnnotationKey; + try { + do { + nextVirtualMethod = virtualIterator.next(); + nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod); + nextVirtualMethodAnnotationKey = + classSection.getMethodAnnotations(nextVirtualMethod); + } while (nextVirtualMethodAnnotationKey == null); + } catch (NoSuchElementException ex) { + nextVirtualMethod = null; + } + } else { + if (nextVirtualMethod == null || nextDirectMethodIndex < nextVirtualMethodIndex) { + nextMethodIndex = nextDirectMethodIndex; + nextAnnotationKey = nextDirectMethodAnnotationKey; + try { + do { + nextDirectMethod = directIterator.next(); + nextDirectMethodIndex = methodSection.getItemIndex(nextDirectMethod); + nextDirectMethodAnnotationKey = + classSection.getMethodAnnotations(nextDirectMethod); + } while (nextDirectMethodAnnotationKey == null); + } catch (NoSuchElementException ex) { + nextDirectMethod = null; + } + } else { + nextMethodIndex = nextVirtualMethodIndex; + nextAnnotationKey = nextVirtualMethodAnnotationKey; + try { + do { + nextVirtualMethod = virtualIterator.next(); + nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod); + nextVirtualMethodAnnotationKey = + classSection.getMethodAnnotations(nextVirtualMethod); + } while (nextVirtualMethodAnnotationKey == null); + } catch (NoSuchElementException ex) { + nextVirtualMethod = null; + } + } + } + + methodAnnotations++; + tempBuffer.putInt(nextMethodIndex); + tempBuffer.putInt(annotationSetSection.getItemOffset(nextAnnotationKey)); + } + } + + { + // AAAAAAAAAAAnd one final time, for parameter annotations + Iterator directIterator = classSection.getSortedDirectMethods(key).iterator(); + Iterator virtualIterator = classSection.getSortedVirtualMethods(key).iterator(); + + MethodKey nextDirectMethod; + int nextDirectMethodIndex = 0; + AnnotationSetRefKey nextDirectMethodAnnotationKey = null; + MethodKey nextVirtualMethod; + int nextVirtualMethodIndex = 0; + AnnotationSetRefKey nextVirtualMethodAnnotationKey = null; + + try { + do { + nextDirectMethod = directIterator.next(); + nextDirectMethodIndex = methodSection.getItemIndex(nextDirectMethod); + nextDirectMethodAnnotationKey = + classSection.getParameterAnnotations(nextDirectMethod); + } while (nextDirectMethodAnnotationKey == null); + } catch (NoSuchElementException ex) { + nextDirectMethod = null; + } + try { + do { + nextVirtualMethod = virtualIterator.next(); + nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod); + nextVirtualMethodAnnotationKey = + classSection.getParameterAnnotations(nextVirtualMethod); + } while (nextVirtualMethodAnnotationKey == null); + } catch (NoSuchElementException ex) { + nextVirtualMethod = null; + } + + while (true) { + int nextMethodIndex; + AnnotationSetRefKey nextAnnotationKey; + + if (nextDirectMethod == null) { + if (nextVirtualMethod == null) { + break; + } + + nextMethodIndex = nextVirtualMethodIndex; + nextAnnotationKey = nextVirtualMethodAnnotationKey; + try { + do { + nextVirtualMethod = virtualIterator.next(); + nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod); + nextVirtualMethodAnnotationKey = + classSection.getParameterAnnotations(nextVirtualMethod); + } while (nextVirtualMethodAnnotationKey == null); + } catch (NoSuchElementException ex) { + nextVirtualMethod = null; + } + } else { + if (nextVirtualMethod == null || nextDirectMethodIndex < nextVirtualMethodIndex) { + nextMethodIndex = nextDirectMethodIndex; + nextAnnotationKey = nextDirectMethodAnnotationKey; + try { + do { + nextDirectMethod = directIterator.next(); + nextDirectMethodIndex = methodSection.getItemIndex(nextDirectMethod); + nextDirectMethodAnnotationKey = + classSection.getParameterAnnotations(nextDirectMethod); + } while (nextDirectMethodAnnotationKey == null); + } catch (NoSuchElementException ex) { + nextDirectMethod = null; + } + } else { + nextMethodIndex = nextVirtualMethodIndex; + nextAnnotationKey = nextVirtualMethodAnnotationKey; + try { + do { + nextVirtualMethod = virtualIterator.next(); + nextVirtualMethodIndex = methodSection.getItemIndex(nextVirtualMethod); + nextVirtualMethodAnnotationKey = + classSection.getParameterAnnotations(nextVirtualMethod); + } while (nextVirtualMethodAnnotationKey == null); + } catch (NoSuchElementException ex) { + nextVirtualMethod = null; + } + } + } + + parameterAnnotations++; + tempBuffer.putInt(nextMethodIndex); + tempBuffer.putInt(annotationSetRefSection.getItemOffset(nextAnnotationKey)); + } + } + + // now, we finally know how many field/method/parameter annotations were written to the temp buffer + + AnnotationSetKey classAnnotationKey = classSection.getClassAnnotations(key); + if (fieldAnnotations == 0 && methodAnnotations == 0 && parameterAnnotations == 0) { + if (classAnnotationKey != null) { + // This is an internable directory. Let's see if we've already written one like it + Integer directoryOffset = internedItems.get(classAnnotationKey); + if (directoryOffset != null) { + classSection.setAnnotationDirectoryOffset(key, directoryOffset); + continue; + } else { + internedItems.put(classAnnotationKey, writer.getPosition()); + } + } else { + continue; + } + } + + // yep, we need to write it out + numAnnotationDirectoryPoolItems++; + classSection.setAnnotationDirectoryOffset(key, writer.getPosition()); + + writer.writeInt(annotationSetSection.getNullableItemOffset(classAnnotationKey)); + writer.writeInt(fieldAnnotations); + writer.writeInt(methodAnnotations); + writer.writeInt(parameterAnnotations); + writer.write(tempBuffer.array(), 0, tempBuffer.position()); + } + } + + private void writeDebugItems(@Nonnull DexDataWriter writer) throws IOException { + debugSectionOffset = writer.getPosition(); + DebugWriter debugWriter = + new DebugWriter(stringSection, typeSection, writer); + + for (ClassKey classKey: classSection.getSortedClasses()) { + Collection directMethods = classSection.getSortedDirectMethods(classKey); + Collection virtualMethods = classSection.getSortedVirtualMethods(classKey); + + Iterable methods = Iterables.concat(directMethods, virtualMethods); + + for (MethodKey methodKey: methods) { + Iterable debugItems = classSection.getDebugItems(methodKey); + Iterable parameterNames = classSection.getParameterNames(methodKey); + + int parameterCount = 0; + if (parameterNames != null) { + int index = 0; + for (StringKey parameterName: parameterNames) { + index++; + if (parameterName != null) { + parameterCount = index; + } + } + } + + if (debugItems == null && parameterCount == 0) { + continue; + } + + numDebugInfoPoolItems++; + + classSection.setDebugItemOffset(methodKey, writer.getPosition()); + int startingLineNumber = 0; + + if (debugItems != null) { + for (org.jf.dexlib2.iface.debug.DebugItem debugItem: debugItems) { + if (debugItem instanceof LineNumber) { + startingLineNumber = ((LineNumber)debugItem).getLineNumber(); + break; + } + } + } + writer.writeUleb128(startingLineNumber); + + writer.writeUleb128(parameterCount); + if (parameterNames != null) { + int index = 0; + for (StringKey parameterName: parameterNames) { + if (index == parameterCount) { + break; + } + index++; + writer.writeUleb128(stringSection.getNullableItemIndex(parameterName) + 1); + } + } + + if (debugItems != null) { + debugWriter.reset(startingLineNumber); + + for (DebugItem debugItem: debugItems) { + classSection.writeDebugItem(debugWriter, debugItem); + } + } + // write an END_SEQUENCE opcode, to end the debug item + writer.write(0); + } + } + } + + private void writeCodeItems(@Nonnull DexDataWriter writer) throws IOException { + ByteArrayOutputStream ehBuf = new ByteArrayOutputStream(); + + writer.align(); + codeSectionOffset = writer.getPosition(); + for (ClassKey classKey: classSection.getSortedClasses()) { + Collection directMethods = classSection.getSortedDirectMethods(classKey); + Collection virtualMethods = classSection.getSortedVirtualMethods(classKey); + + Iterable methods = Iterables.concat(directMethods, virtualMethods); + + for (MethodKey methodKey: methods) { + Iterable instructions = classSection.getInstructions(methodKey); + int debugItemOffset = classSection.getDebugItemOffset(methodKey); + + if (instructions == null && debugItemOffset == NO_OFFSET) { + continue; + } + + numCodeItemPoolItems++; + + writer.align(); + classSection.setCodeItemOffset(methodKey, writer.getPosition()); + + writer.writeUshort(classSection.getRegisterCount(methodKey)); + + boolean isStatic = AccessFlags.STATIC.isSet(classSection.getMethodAccessFlags(methodKey)); + Collection parameters = typeListSection.getTypes( + protoSection.getParameters(methodSection.getPrototype(methodKey))); + + List> tryBlocks = classSection.getTryBlocks(methodKey); + writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic)); + + if (instructions != null) { + tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks); + + InstructionWriteUtil instrWriteUtil = + new InstructionWriteUtil(instructions, stringSection, instructionFactory); + writer.writeUshort(instrWriteUtil.getOutParamCount()); + writer.writeUshort(tryBlocks.size()); + writer.writeInt(debugItemOffset); + + InstructionWriter instructionWriter = + InstructionWriter.makeInstructionWriter(writer, stringSection, typeSection, fieldSection, + methodSection); + + writer.writeInt(instrWriteUtil.getCodeUnitCount()); + for (Insn instruction: instrWriteUtil.getInstructions()) { + switch (instruction.getOpcode().format) { + case Format10t: + instructionWriter.write((Instruction10t)instruction); + break; + case Format10x: + instructionWriter.write((Instruction10x)instruction); + break; + case Format11n: + instructionWriter.write((Instruction11n)instruction); + break; + case Format11x: + instructionWriter.write((Instruction11x)instruction); + break; + case Format12x: + instructionWriter.write((Instruction12x)instruction); + break; + case Format20bc: + instructionWriter.write((Instruction20bc)instruction); + break; + case Format20t: + instructionWriter.write((Instruction20t)instruction); + break; + case Format21c: + instructionWriter.write((Instruction21c)instruction); + break; + case Format21ih: + instructionWriter.write((Instruction21ih)instruction); + break; + case Format21lh: + instructionWriter.write((Instruction21lh)instruction); + break; + case Format21s: + instructionWriter.write((Instruction21s)instruction); + break; + case Format21t: + instructionWriter.write((Instruction21t)instruction); + break; + case Format22b: + instructionWriter.write((Instruction22b)instruction); + break; + case Format22c: + instructionWriter.write((Instruction22c)instruction); + break; + case Format22s: + instructionWriter.write((Instruction22s)instruction); + break; + case Format22t: + instructionWriter.write((Instruction22t)instruction); + break; + case Format22x: + instructionWriter.write((Instruction22x)instruction); + break; + case Format23x: + instructionWriter.write((Instruction23x)instruction); + break; + case Format30t: + instructionWriter.write((Instruction30t)instruction); + break; + case Format31c: + instructionWriter.write((Instruction31c)instruction); + break; + case Format31i: + instructionWriter.write((Instruction31i)instruction); + break; + case Format31t: + instructionWriter.write((Instruction31t)instruction); + break; + case Format32x: + instructionWriter.write((Instruction32x)instruction); + break; + case Format35c: + instructionWriter.write((Instruction35c)instruction); + break; + case Format3rc: + instructionWriter.write((Instruction3rc)instruction); + break; + case Format51l: + instructionWriter.write((Instruction51l)instruction); + break; + case ArrayPayload: + instructionWriter.write((ArrayPayload)instruction); + break; + case PackedSwitchPayload: + instructionWriter.write((PackedSwitchPayload)instruction); + break; + case SparseSwitchPayload: + instructionWriter.write((SparseSwitchPayload)instruction); + break; + default: + throw new ExceptionWithContext("Unsupported instruction format: %s", + instruction.getOpcode().format); + } + } + + if (tryBlocks.size() > 0) { + writer.align(); + + + + // filter out unique lists of exception handlers + Map, Integer> exceptionHandlerOffsetMap = Maps.newHashMap(); + for (TryBlock tryBlock: tryBlocks) { + exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), 0); + } + DexDataWriter.writeUleb128(ehBuf, exceptionHandlerOffsetMap.size()); + + for (TryBlock tryBlock: tryBlocks) { + int startAddress = tryBlock.getStartCodeAddress(); + int endAddress = startAddress + tryBlock.getCodeUnitCount(); + + startAddress += instrWriteUtil.codeOffsetShift(startAddress); + endAddress += instrWriteUtil.codeOffsetShift(endAddress); + int tbCodeUnitCount = endAddress - startAddress; + + writer.writeInt(startAddress); + writer.writeUshort(tbCodeUnitCount); + + if (tryBlock.getExceptionHandlers().size() == 0) { + throw new ExceptionWithContext("No exception handlers for the try block!"); + } + + Integer offset = exceptionHandlerOffsetMap.get(tryBlock.getExceptionHandlers()); + if (offset != 0) { + // exception handler has already been written out, just use it + writer.writeUshort(offset); + } else { + // if offset has not been set yet, we are about to write out a new exception handler + offset = ehBuf.size(); + writer.writeUshort(offset); + exceptionHandlerOffsetMap.put(tryBlock.getExceptionHandlers(), offset); + + // check if the last exception handler is a catch-all and adjust the size accordingly + int ehSize = tryBlock.getExceptionHandlers().size(); + ExceptionHandler ehLast = tryBlock.getExceptionHandlers().get(ehSize-1); + if (ehLast.getExceptionType() == null) { + ehSize = ehSize * (-1) + 1; + } + + // now let's layout the exception handlers, assuming that catch-all is always last + DexDataWriter.writeSleb128(ehBuf, ehSize); + for (ExceptionHandler eh : tryBlock.getExceptionHandlers()) { + TypeKey exceptionTypeKey = classSection.getExceptionType(eh); + + int codeAddress = eh.getHandlerCodeAddress(); + codeAddress += instrWriteUtil.codeOffsetShift(codeAddress); + + if (exceptionTypeKey != null) { + //regular exception handling + DexDataWriter.writeUleb128(ehBuf, typeSection.getItemIndex(exceptionTypeKey)); + DexDataWriter.writeUleb128(ehBuf, codeAddress); + } else { + //catch-all + DexDataWriter.writeUleb128(ehBuf, codeAddress); + } + } + } + } + + if (ehBuf.size() > 0) { + ehBuf.writeTo(writer); + ehBuf.reset(); + } + } + } else { + // no instructions, all we have is the debug item offset + writer.writeUshort(0); + writer.writeUshort(0); + writer.writeInt(debugItemOffset); + writer.writeInt(0); + } + } + } + } + + private int calcNumItems() { + int numItems = 0; + + // header item + numItems++; + + if (stringSection.getItems().size() > 0) { + numItems += 2; // index and data + } + if (typeSection.getItems().size() > 0) { + numItems++; + } + if (protoSection.getItems().size() > 0) { + numItems++; + } + if (fieldSection.getItems().size() > 0) { + numItems++; + } + if (methodSection.getItems().size() > 0) { + numItems++; + } + if (typeListSection.getItems().size() > 0) { + numItems++; + } + if (encodedArraySection.getItems().size() > 0) { + numItems++; + } + if (annotationSection.getItems().size() > 0) { + numItems++; + } + if (annotationSetSection.getItems().size() > 0) { + numItems++; + } + if (annotationSetRefSection.getItems().size() > 0) { + numItems++; + } + if (numAnnotationDirectoryPoolItems > 0) { + numItems++; + } + if (numDebugInfoPoolItems > 0) { + numItems++; + } + if (numCodeItemPoolItems > 0) { + numItems++; + } + if (classSection.getItems().size() > 0) { + numItems++; + } + if (numClassDataItems > 0) { + numItems++; + } + // map item itself + numItems++; + + return numItems; + } + + private void writeMapItem(@Nonnull DexDataWriter writer) throws IOException{ + writer.align(); + mapSectionOffset = writer.getPosition(); + int numItems = calcNumItems(); + + writer.writeInt(numItems); + + // index section + writeMapItem(writer, ItemType.HEADER_ITEM, 1, 0); + writeMapItem(writer, ItemType.STRING_ID_ITEM, stringSection.getItems().size(), stringIndexSectionOffset); + writeMapItem(writer, ItemType.TYPE_ID_ITEM, typeSection.getItems().size(), typeSectionOffset); + writeMapItem(writer, ItemType.PROTO_ID_ITEM, protoSection.getItems().size(), protoSectionOffset); + writeMapItem(writer, ItemType.FIELD_ID_ITEM, fieldSection.getItems().size(), fieldSectionOffset); + writeMapItem(writer, ItemType.METHOD_ID_ITEM, methodSection.getItems().size(), methodSectionOffset); + writeMapItem(writer, ItemType.CLASS_DEF_ITEM, classSection.getItems().size(), classIndexSectionOffset); + + // data section + writeMapItem(writer, ItemType.STRING_DATA_ITEM, stringSection.getItems().size(), stringDataSectionOffset); + writeMapItem(writer, ItemType.TYPE_LIST, typeListSection.getItems().size(), typeListSectionOffset); + writeMapItem(writer, ItemType.ENCODED_ARRAY_ITEM, encodedArraySection.getItems().size(), + encodedArraySectionOffset); + writeMapItem(writer, ItemType.ANNOTATION_ITEM, annotationSection.getItems().size(), annotationSectionOffset); + writeMapItem(writer, ItemType.ANNOTATION_SET_ITEM, annotationSetSection.getItems().size(), + annotationSetSectionOffset); + writeMapItem(writer, ItemType.ANNOTATION_SET_REF_LIST, annotationSetRefSection.getItems().size(), + annotationSetRefSectionOffset); + writeMapItem(writer, ItemType.ANNOTATION_DIRECTORY_ITEM, numAnnotationDirectoryPoolItems, + annotationDirectorySectionOffset); + writeMapItem(writer, ItemType.DEBUG_INFO_ITEM, numDebugInfoPoolItems, debugSectionOffset); + writeMapItem(writer, ItemType.CODE_ITEM, numCodeItemPoolItems, codeSectionOffset); + writeMapItem(writer, ItemType.CLASS_DATA_ITEM, numClassDataItems, classDataSectionOffset); + writeMapItem(writer, ItemType.MAP_LIST, 1, mapSectionOffset); + } + + private void writeMapItem(@Nonnull DexDataWriter writer, int type, int size, int offset) throws IOException { + if (size > 0) { + writer.writeUshort(type); + writer.writeUshort(0); + writer.writeInt(size); + writer.writeInt(offset); + } + } + + private void writeHeader(@Nonnull DexDataWriter writer, int dataOffset, int fileSize) throws IOException { + // TODO: need to determine which magic value to write + writer.write(HeaderItem.MAGIC_VALUES[0]); + + // checksum placeholder + writer.writeInt(0); + + // signature placeholder + writer.write(new byte[20]); + + writer.writeInt(fileSize); + writer.writeInt(HeaderItem.ITEM_SIZE); + writer.writeInt(HeaderItem.LITTLE_ENDIAN_TAG); + + // link + writer.writeInt(0); + writer.writeInt(0); + + // map + writer.writeInt(mapSectionOffset); + + // index sections + + writeSectionInfo(writer, stringSection.getItems().size(), stringIndexSectionOffset); + writeSectionInfo(writer, typeSection.getItems().size(), typeSectionOffset); + writeSectionInfo(writer, protoSection.getItems().size(), protoSectionOffset); + writeSectionInfo(writer, fieldSection.getItems().size(), fieldSectionOffset); + writeSectionInfo(writer, methodSection.getItems().size(), methodSectionOffset); + writeSectionInfo(writer, classSection.getItems().size(), classIndexSectionOffset); + + // data section + writer.writeInt(fileSize - dataOffset); + writer.writeInt(dataOffset); + } + + private void writeSectionInfo(DexDataWriter writer, int numItems, int offset) throws IOException { + writer.writeInt(numItems); + if (numItems > 0) { + writer.writeInt(offset); } else { - while (value < -0x80) { - tempBuf[index++] = (byte)value; - value >>= 8; - } + writer.writeInt(0); } - tempBuf[index++] = (byte)value; - writeEncodedValueHeader(valueType, index-1); - write(tempBuf, 0, index); - } - - public void writeEncodedLong(int valueType, long value) throws IOException { - int index = 0; - if (value >= 0) { - while (value > 0x7f) { - tempBuf[index++] = (byte)value; - value >>= 8; - } - } else { - while (value < -0x80) { - tempBuf[index++] = (byte)value; - value >>= 8; - } - } - tempBuf[index++] = (byte)value; - writeEncodedValueHeader(valueType, index-1); - write(tempBuf, 0, index); - } - - public void writeEncodedUint(int valueType, int value) throws IOException { - int index = 0; - do { - tempBuf[index++] = (byte)value; - value >>>= 8; - } while (value != 0); - writeEncodedValueHeader(valueType, index-1); - write(tempBuf, 0, index); - } - - public void writeEncodedFloat(int valueType, float value) throws IOException { - writeRightZeroExtendedInt(valueType, Float.floatToRawIntBits(value)); - } - - protected void writeRightZeroExtendedInt(int valueType, int value) throws IOException { - int index = 3; - do { - tempBuf[index--] = (byte)((value & 0xFF000000) >>> 24); - value <<= 8; - } while (value != 0); - - int firstElement = index+1; - int encodedLength = 4-firstElement; - writeEncodedValueHeader(valueType, encodedLength - 1); - write(tempBuf, firstElement, encodedLength); - } - - public void writeEncodedDouble(int valueType, double value) throws IOException { - writeRightZeroExtendedLong(valueType, Double.doubleToRawLongBits(value)); - } - - protected void writeRightZeroExtendedLong(int valueType, long value) throws IOException { - int index = 7; - do { - tempBuf[index--] = (byte)((value & 0xFF00000000000000L) >>> 56); - value <<= 8; - } while (value != 0); - - int firstElement = index+1; - int encodedLength = 8-firstElement; - writeEncodedValueHeader(valueType, encodedLength - 1); - write(tempBuf, firstElement, encodedLength); - } - - public void writeString(String string) throws IOException { - int len = string.length(); - - // make sure we have enough room in the temporary buffer - if (tempBuf.length <= string.length()*3) { - tempBuf = new byte[string.length()*3]; - } - - final byte[] buf = tempBuf; - - int bufPos = 0; - for (int i = 0; i < len; i++) { - char c = string.charAt(i); - if ((c != 0) && (c < 0x80)) { - buf[bufPos++] = (byte)c; - } else if (c < 0x800) { - buf[bufPos++] = (byte)(((c >> 6) & 0x1f) | 0xc0); - buf[bufPos++] = (byte)((c & 0x3f) | 0x80); - } else { - buf[bufPos++] = (byte)(((c >> 12) & 0x0f) | 0xe0); - buf[bufPos++] = (byte)(((c >> 6) & 0x3f) | 0x80); - buf[bufPos++] = (byte)((c & 0x3f) | 0x80); - } - } - write(buf, 0, bufPos); - } - - public void align() throws IOException { - int zeros = (-getPosition()) & 3; - if (zeros > 0) { - write(zeroBuf, 0, zeros); - } - } - - public int getPosition() { - return filePosition; } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedArraySection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedArraySection.java new file mode 100644 index 00000000..f40d4412 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedArraySection.java @@ -0,0 +1,41 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +package org.jf.dexlib2.writer; + +import javax.annotation.Nonnull; +import java.util.Collection; + +public interface EncodedArraySection extends NullableOffsetSection { + @Nonnull Collection getElements(@Nonnull EncodedArrayKey key); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedValueWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedValueWriter.java new file mode 100644 index 00000000..09b7ccf9 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedValueWriter.java @@ -0,0 +1,142 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.Collection; + +public abstract class EncodedValueWriter { + @Nonnull private final DexDataWriter writer; + @Nonnull private final StringSection stringSection; + @Nonnull private final TypeSection typeSection; + @Nonnull private final FieldSection fieldSection; + @Nonnull private final MethodSection methodSection; + @Nonnull private final AnnotationSection annotationSection; + + public EncodedValueWriter( + @Nonnull DexDataWriter writer, + @Nonnull StringSection stringSection, + @Nonnull TypeSection typeSection, + @Nonnull FieldSection fieldSection, + @Nonnull MethodSection methodSection, + @Nonnull AnnotationSection annotationSection) { + this.writer = writer; + this.stringSection = stringSection; + this.typeSection = typeSection; + this.fieldSection = fieldSection; + this.methodSection = methodSection; + this.annotationSection = annotationSection; + } + + protected abstract void writeEncodedValue(@Nonnull EncodedValue encodedValue) throws IOException; + + public void writeAnnotation(TypeKey annotationType, + Collection elements) throws IOException { + writer.writeEncodedValueHeader(ValueType.ANNOTATION, 0); + writer.writeUleb128(typeSection.getItemIndex(annotationType)); + writer.writeUleb128(elements.size()); + for (AnnotationElement element: elements) { + writer.writeUleb128(stringSection.getItemIndex(annotationSection.getElementName(element))); + writeEncodedValue(annotationSection.getElementValue(element)); + } + } + + public void writeArray(Collection elements) throws IOException { + writer.writeEncodedValueHeader(ValueType.ARRAY, 0); + writer.writeUleb128(elements.size()); + for (EncodedValue element: elements) { + writeEncodedValue(element); + } + } + + public void writeBoolean(boolean value) throws IOException { + writer.writeEncodedValueHeader(ValueType.BOOLEAN, value ? 1 : 0); + } + + public void writeByte(byte value) throws IOException { + writer.writeEncodedInt(ValueType.BYTE, value); + } + + public void writeChar(char value) throws IOException { + writer.writeEncodedUint(ValueType.CHAR, value); + } + + public void writeDouble(double value) throws IOException { + writer.writeEncodedDouble(ValueType.DOUBLE, value); + } + + public void writeEnum(@Nonnull FieldRefKey value) throws IOException { + writer.writeEncodedUint(ValueType.ENUM, fieldSection.getItemIndex(value)); + } + + public void writeField(@Nonnull FieldRefKey value) throws IOException { + writer.writeEncodedUint(ValueType.FIELD, fieldSection.getItemIndex(value)); + } + + public void writeFloat(float value) throws IOException { + writer.writeEncodedFloat(ValueType.FLOAT, value); + } + + public void writeInt(int value) throws IOException { + writer.writeEncodedInt(ValueType.INT, value); + } + + public void writeLong(long value) throws IOException { + writer.writeEncodedLong(ValueType.LONG, value); + } + + public void writeMethod(@Nonnull MethodRefKey value) throws IOException { + writer.writeEncodedUint(ValueType.METHOD, methodSection.getItemIndex(value)); + } + + public void writeNull() throws IOException { + writer.write(ValueType.NULL); + } + + public void writeShort(int value) throws IOException { + writer.writeEncodedInt(ValueType.SHORT, value); + } + + public void writeString(@Nonnull StringKey value) throws IOException { + writer.writeEncodedUint(ValueType.STRING, stringSection.getItemIndex(value)); + } + + public void writeType(@Nonnull TypeKey value) throws IOException { + writer.writeEncodedUint(ValueType.TYPE, typeSection.getItemIndex(value)); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/FieldPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/FieldPool.java deleted file mode 100644 index 116fa69e..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/FieldPool.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.jf.dexlib2.iface.reference.FieldReference; -import org.jf.dexlib2.util.ReferenceUtil; -import org.jf.util.ExceptionWithContext; - -import javax.annotation.Nonnull; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class FieldPool { - public final static int FIELD_ID_ITEM_SIZE = 0x08; - - @Nonnull private final Map internedFieldIdItems = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; - - public FieldPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; - } - - public void intern(@Nonnull FieldReference field) { - Integer prev = internedFieldIdItems.put(field, 0); - if (prev == null) { - dexFile.typePool.intern(field.getDefiningClass()); - dexFile.stringPool.intern(field.getName()); - dexFile.typePool.intern(field.getType()); - } - } - - public int getIndex(@Nonnull FieldReference fieldReference) { - Integer index = internedFieldIdItems.get(fieldReference); - if (index == null) { - throw new ExceptionWithContext("Field not found.: %s", ReferenceUtil.getFieldDescriptor(fieldReference)); - } - return index; - } - - public int getIndexedSectionSize() { - return internedFieldIdItems.size() * FIELD_ID_ITEM_SIZE; - } - - public int getNumItems() { - return internedFieldIdItems.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - List fields = Lists.newArrayList(internedFieldIdItems.keySet()); - Collections.sort(fields); - - sectionOffset = writer.getPosition(); - int index = 0; - for (FieldReference field: fields) { - internedFieldIdItems.put(field, index++); - writer.writeUshort(dexFile.typePool.getIndex(field.getDefiningClass())); - writer.writeUshort(dexFile.typePool.getIndex(field.getType())); - writer.writeInt(dexFile.stringPool.getIndex(field.getName())); - } - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/FieldSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/FieldSection.java new file mode 100644 index 00000000..626b8180 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/FieldSection.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.iface.reference.FieldReference; + +import javax.annotation.Nonnull; + +public interface FieldSection + extends IndexSection { + @Nonnull TypeKey getDefiningClass(@Nonnull FieldRefKey key); + @Nonnull TypeKey getFieldType(@Nonnull FieldRefKey key); + @Nonnull StringKey getName(@Nonnull FieldRefKey key); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/HeaderItem.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/HeaderItem.java deleted file mode 100644 index 2bff28b0..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/HeaderItem.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.zip.Adler32; - -public class HeaderItem { - public final static int HEADER_ITEM_SIZE = 0x70; - private static byte[] DEX_FILE_MAGIC = new byte[] {0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 }; - private static int LITTLE_ENDIAN = 0x12345678; - private static int CHECKSUM_SIZE = 4; - private static int SIGNATURE_SIZE = 20; - - DexFile dexFile; - - public HeaderItem(DexFile dexFile) { - this.dexFile = dexFile; - } - - public int getSectionOffset() { - return 0; - } - - public int getNumItems() { - return 1; - } - - public int getSize() { - return HEADER_ITEM_SIZE; - } - - public void write(DexWriter writer, int dataOffset, int fileSize) throws IOException { - writer.write(DEX_FILE_MAGIC); - - // checksum placeholder - writer.writeInt(0); - - // signature placeholder - writer.write(new byte[20]); - - writer.writeInt(fileSize); - writer.writeInt(HEADER_ITEM_SIZE); - writer.writeInt(LITTLE_ENDIAN); - - // link - writer.writeInt(0); - writer.writeInt(0); - - // map - writer.writeInt(dexFile.mapItem.getSectionOffset()); - - // index sections - // TODO: double-check whether section offset for an empty section must be 0 - writeSectionInfo(writer, dexFile.stringPool.getNumItems(), dexFile.stringPool.getIndexSectionOffset()); - writeSectionInfo(writer, dexFile.typePool.getNumItems(), dexFile.typePool.getSectionOffset()); - writeSectionInfo(writer, dexFile.protoPool.getNumItems(), dexFile.protoPool.getSectionOffset()); - writeSectionInfo(writer, dexFile.fieldPool.getNumItems(), dexFile.fieldPool.getSectionOffset()); - writeSectionInfo(writer, dexFile.methodPool.getNumItems(), dexFile.methodPool.getSectionOffset()); - writeSectionInfo(writer, dexFile.classDefPool.getNumClassDefItems(), dexFile.classDefPool.getIndexSectionOffset()); - - // data section - writer.writeInt(fileSize - dataOffset); - writer.writeInt(dataOffset); - } - - public void updateSignature(FileChannel fileChannel) throws IOException { - MessageDigest md; - try { - md = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException ex) { - throw new RuntimeException(ex); - } - - ByteBuffer buffer = ByteBuffer.allocate(128 * 1024); - fileChannel.position(DEX_FILE_MAGIC.length + CHECKSUM_SIZE + SIGNATURE_SIZE); - int bytesRead = fileChannel.read(buffer); - while (bytesRead >= 0) { - buffer.rewind(); - md.update(buffer); - buffer.clear(); - bytesRead = fileChannel.read(buffer); - } - - byte[] signature = md.digest(); - if (signature.length != SIGNATURE_SIZE) { - throw new RuntimeException("unexpected digest write: " + signature.length + " bytes"); - } - - // write signature - fileChannel.position(DEX_FILE_MAGIC.length + CHECKSUM_SIZE); - fileChannel.write(ByteBuffer.wrap(signature)); - - // flush - fileChannel.force(false); - } - - public void updateChecksum(FileChannel fileChannel) throws IOException { - Adler32 a32 = new Adler32(); - - ByteBuffer buffer = ByteBuffer.allocate(128 * 1024); - fileChannel.position(DEX_FILE_MAGIC.length + CHECKSUM_SIZE); - int bytesRead = fileChannel.read(buffer); - while (bytesRead >= 0) { - a32.update(buffer.array(), 0, bytesRead); - buffer.clear(); - bytesRead = fileChannel.read(buffer); - } - - // write checksum, utilizing logic in DexWriter to write the integer value properly - fileChannel.position(DEX_FILE_MAGIC.length); - int checksum = (int) a32.getValue(); - ByteArrayOutputStream checksumBuf = new ByteArrayOutputStream(); - DexWriter.writeInt(checksumBuf, checksum); - fileChannel.write(ByteBuffer.wrap(checksumBuf.toByteArray())); - - // flush - fileChannel.force(false); - } - - private void writeSectionInfo(DexWriter writer, int numItems, int offset) throws IOException { - writer.writeInt(numItems); - if (numItems > 0) { - writer.writeInt(offset); - } else { - writer.writeInt(0); - } - } -} diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/MockStringPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/IndexSection.java similarity index 86% rename from dexlib2/src/test/java/org/jf/dexlib2/writer/MockStringPool.java rename to dexlib2/src/main/java/org/jf/dexlib2/writer/IndexSection.java index 01d47789..53d14474 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/writer/MockStringPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/IndexSection.java @@ -1,5 +1,5 @@ /* - * Copyright 2012, Google Inc. + * Copyright 2013, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,9 +32,10 @@ package org.jf.dexlib2.writer; import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Map; -public class MockStringPool extends StringPool { - public void intern(@Nonnull CharSequence string, int index) { - internedStringIdItems.put(string.toString(), index); - } +public interface IndexSection { + int getItemIndex(@Nonnull Key key); + @Nonnull Collection> getItems(); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionFactory.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionFactory.java new file mode 100644 index 00000000..519f0a54 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionFactory.java @@ -0,0 +1,76 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.SwitchElement; +import org.jf.dexlib2.iface.reference.Reference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public interface InstructionFactory { + Insn makeInstruction10t(@Nonnull Opcode opcode, int codeOffset); + Insn makeInstruction10x(@Nonnull Opcode opcode); + Insn makeInstruction11n(@Nonnull Opcode opcode, int registerA, int literal); + Insn makeInstruction11x(@Nonnull Opcode opcode, int registerA); + Insn makeInstruction12x(@Nonnull Opcode opcode, int registerA, int registerB); + Insn makeInstruction20bc(@Nonnull Opcode opcode, int verificationError, @Nonnull Reference reference); + Insn makeInstruction20t(@Nonnull Opcode opcode, int codeOffset); + Insn makeInstruction21c(@Nonnull Opcode opcode, int registerA, @Nonnull Reference reference); + Insn makeInstruction21ih(@Nonnull Opcode opcode, int registerA, int literal); + Insn makeInstruction21lh(@Nonnull Opcode opcode, int registerA, long literal); + Insn makeInstruction21s(@Nonnull Opcode opcode, int registerA, int literal); + Insn makeInstruction21t(@Nonnull Opcode opcode, int registerA, int codeOffset); + Insn makeInstruction22b(@Nonnull Opcode opcode, int registerA, int registerB, int literal); + Insn makeInstruction22c(@Nonnull Opcode opcode, int registerA, int registerB, @Nonnull Reference reference); + Insn makeInstruction22s(@Nonnull Opcode opcode, int registerA, int registerB, int literal); + Insn makeInstruction22t(@Nonnull Opcode opcode, int registerA, int registerB, int codeOffset); + Insn makeInstruction22x(@Nonnull Opcode opcode, int registerA, int registerB); + Insn makeInstruction23x(@Nonnull Opcode opcode, int registerA, int registerB, int registerC); + Insn makeInstruction30t(@Nonnull Opcode opcode, int codeOffset); + Insn makeInstruction31c(@Nonnull Opcode opcode, int registerA, @Nonnull Reference reference); + Insn makeInstruction31i(@Nonnull Opcode opcode, int registerA, int literal); + Insn makeInstruction31t(@Nonnull Opcode opcode, int registerA, int codeOffset); + Insn makeInstruction32x(@Nonnull Opcode opcode, int registerA, int registerB); + Insn makeInstruction35c(@Nonnull Opcode opcode, int registerCount, int registerC, int registerD, int registerE, + int registerF, int registerG, @Nonnull Reference reference); + Insn makeInstruction3rc(@Nonnull Opcode opcode, int startRegister, int registerCount, + @Nonnull Reference reference); + Insn makeInstruction51l(@Nonnull Opcode opcode, int registerA, long literal); + Insn makeSparseSwitchPayload(@Nullable List switchElements); + Insn makePackedSwitchPayload(@Nullable List switchElements); + Insn makeArrayPayload(int elementWidth, @Nullable List arrayElements); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java new file mode 100644 index 00000000..f2e31132 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java @@ -0,0 +1,432 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.ReferenceType; +import org.jf.dexlib2.iface.instruction.ReferenceInstruction; +import org.jf.dexlib2.iface.instruction.SwitchElement; +import org.jf.dexlib2.iface.instruction.formats.*; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.iface.reference.StringReference; +import org.jf.dexlib2.iface.reference.TypeReference; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.List; + +public class InstructionWriter { + @Nonnull private final DexDataWriter writer; + @Nonnull private final StringSection stringSection; + @Nonnull private final TypeSection typeSection; + @Nonnull private final FieldSection fieldSection; + @Nonnull private final MethodSection methodSection; + + @Nonnull static + InstructionWriter + makeInstructionWriter( + @Nonnull DexDataWriter writer, + @Nonnull StringSection stringSection, + @Nonnull TypeSection typeSection, + @Nonnull FieldSection fieldSection, + @Nonnull MethodSection methodSection) { + return new InstructionWriter( + writer, stringSection, typeSection, fieldSection, methodSection); + } + + InstructionWriter(@Nonnull DexDataWriter writer, + @Nonnull StringSection stringSection, + @Nonnull TypeSection typeSection, + @Nonnull FieldSection fieldSection, + @Nonnull MethodSection methodSection) { + this.writer = writer; + this.stringSection = stringSection; + this.typeSection = typeSection; + this.fieldSection = fieldSection; + this.methodSection = methodSection; + } + + public void write(@Nonnull Instruction10t instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getCodeOffset()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction10x instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(0); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction11n instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(packNibbles(instruction.getRegisterA(), instruction.getNarrowLiteral())); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction11x instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction12x instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction20bc instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getVerificationError()); + writer.writeUshort(getReferenceIndex(instruction)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction20t instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(0); + writer.writeShort(instruction.getCodeOffset()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction21c instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.writeUshort(getReferenceIndex(instruction)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction21ih instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.writeShort(instruction.getHatLiteral()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction21lh instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.writeShort(instruction.getHatLiteral()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction21s instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.writeShort(instruction.getNarrowLiteral()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction21t instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.writeShort(instruction.getCodeOffset()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction22b instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.write(instruction.getRegisterB()); + writer.write(instruction.getNarrowLiteral()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction22c instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); + writer.writeUshort(getReferenceIndex(instruction)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction22s instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); + writer.writeShort(instruction.getNarrowLiteral()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction22t instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB())); + writer.writeShort(instruction.getCodeOffset()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction22x instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.writeUshort(instruction.getRegisterB()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction23x instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.write(instruction.getRegisterB()); + writer.write(instruction.getRegisterC()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction30t instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(0); + writer.writeInt(instruction.getCodeOffset()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction31c instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.writeInt(getReferenceIndex(instruction)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction31i instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.writeInt(instruction.getNarrowLiteral()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction31t instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.writeInt(instruction.getCodeOffset()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction32x instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(0); + writer.writeUshort(instruction.getRegisterA()); + writer.writeUshort(instruction.getRegisterB()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction35c instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount())); + writer.writeUshort(getReferenceIndex(instruction)); + writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD())); + writer.write(packNibbles(instruction.getRegisterE(), instruction.getRegisterF())); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction3rc instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterCount()); + writer.writeUshort(getReferenceIndex(instruction)); + writer.writeUshort(instruction.getStartRegister()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull Instruction51l instruction) { + try { + writer.write(instruction.getOpcode().value); + writer.write(instruction.getRegisterA()); + writer.writeLong(instruction.getWideLiteral()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull ArrayPayload instruction) { + try { + writer.writeUshort(instruction.getOpcode().value); + writer.writeUshort(instruction.getElementWidth()); + List elements = instruction.getArrayElements(); + writer.writeInt(elements.size()); + switch (instruction.getElementWidth()) { + case 1: + for (Number element: elements) { + writer.write(element.byteValue()); + } + break; + case 2: + for (Number element: elements) { + writer.writeShort(element.shortValue()); + } + break; + case 4: + for (Number element: elements) { + writer.writeInt(element.intValue()); + } + break; + case 8: + for (Number element: elements) { + writer.writeLong(element.longValue()); + } + break; + } + if ((writer.getPosition() & 1) != 0) { + writer.write(0); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull SparseSwitchPayload instruction) { + try { + writer.writeUbyte(0); + writer.writeUbyte(instruction.getOpcode().value >> 8); + List elements = instruction.getSwitchElements(); + writer.writeUshort(elements.size()); + for (SwitchElement element: elements) { + writer.writeInt(element.getKey()); + } + for (SwitchElement element: elements) { + writer.writeInt(element.getOffset()); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + public void write(@Nonnull PackedSwitchPayload instruction) { + try { + writer.writeUbyte(0); + writer.writeUbyte(instruction.getOpcode().value >> 8); + List elements = instruction.getSwitchElements(); + writer.writeUshort(elements.size()); + if (elements.size() == 0) { + writer.writeInt(0); + } else { + writer.writeInt(elements.get(0).getKey()); + for (SwitchElement element: elements) { + writer.writeInt(element.getOffset()); + } + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private static int packNibbles(int a, int b) { + return (b << 4) | a; + } + + private int getReferenceIndex(ReferenceInstruction referenceInstruction) { + switch (referenceInstruction.getOpcode().referenceType) { + case ReferenceType.FIELD: + return fieldSection.getItemIndex((FieldRefKey)referenceInstruction.getReference()); + case ReferenceType.METHOD: + return methodSection.getItemIndex((MethodRefKey)referenceInstruction.getReference()); + case ReferenceType.STRING: + return stringSection.getItemIndex((StringRef)referenceInstruction.getReference()); + case ReferenceType.TYPE: + return typeSection.getItemIndex((TypeRef)referenceInstruction.getReference()); + default: + throw new ExceptionWithContext("Unknown reference type: %d", + referenceInstruction.getOpcode().referenceType); + } + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/MapItem.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/MapItem.java deleted file mode 100644 index 8611623a..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/MapItem.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import org.jf.dexlib2.dexbacked.raw.ItemType; -import org.jf.util.ExceptionWithContext; - -import javax.annotation.Nonnull; -import java.io.IOException; - -public class MapItem { - DexFile dexFile; - private int sectionOffset = -1; - - public MapItem(DexFile dexFile) { - this.dexFile = dexFile; - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - writer.align(); - sectionOffset = writer.getPosition(); - int numItems = calcNumItems(); - - writer.writeInt(numItems); - - // index section - writeItem(writer, ItemType.HEADER_ITEM, 1, 0); - writeItem(writer, ItemType.STRING_ID_ITEM, dexFile.stringPool.getNumItems(), dexFile.stringPool.getIndexSectionOffset()); - writeItem(writer, ItemType.TYPE_ID_ITEM, dexFile.typePool.getNumItems(), dexFile.typePool.getSectionOffset()); - writeItem(writer, ItemType.PROTO_ID_ITEM, dexFile.protoPool.getNumItems(), dexFile.protoPool.getSectionOffset()); - writeItem(writer, ItemType.FIELD_ID_ITEM, dexFile.fieldPool.getNumItems(), dexFile.fieldPool.getSectionOffset()); - writeItem(writer, ItemType.METHOD_ID_ITEM, dexFile.methodPool.getNumItems(), dexFile.methodPool.getSectionOffset()); - writeItem(writer, ItemType.CLASS_DEF_ITEM, dexFile.classDefPool.getNumClassDefItems(), dexFile.classDefPool.getIndexSectionOffset()); - - // data section - writeItem(writer, ItemType.STRING_DATA_ITEM, dexFile.stringPool.getNumItems(), dexFile.stringPool.getDataSectionOffset()); - writeItem(writer, ItemType.TYPE_LIST, dexFile.typeListPool.getNumItems(), dexFile.typeListPool.getSectionOffset()); - writeItem(writer, ItemType.ENCODED_ARRAY_ITEM, dexFile.encodedArrayPool.getNumItems(), dexFile.encodedArrayPool.getSectionOffset()); - writeItem(writer, ItemType.ANNOTATION_ITEM, dexFile.annotationPool.getNumItems(), dexFile.annotationPool.getSectionOffset()); - writeItem(writer, ItemType.ANNOTATION_SET_ITEM, dexFile.annotationSetPool.getNumItems(), dexFile.annotationSetPool.getSectionOffset()); - writeItem(writer, ItemType.ANNOTATION_SET_REF_LIST, dexFile.annotationSetRefPool.getNumItems(), dexFile.annotationSetRefPool.getSectionOffset()); - writeItem(writer, ItemType.ANNOTATION_DIRECTORY_ITEM, dexFile.annotationDirectoryPool.getNumItems(), dexFile.annotationDirectoryPool.getSectionOffset()); - writeItem(writer, ItemType.DEBUG_INFO_ITEM, dexFile.debugInfoPool.getNumItems(), dexFile.debugInfoPool.getSectionOffset()); - writeItem(writer, ItemType.CODE_ITEM, dexFile.codeItemPool.getNumItems(), dexFile.codeItemPool.getSectionOffset()); - writeItem(writer, ItemType.CLASS_DATA_ITEM, dexFile.classDefPool.getNumClassDataItems(), dexFile.classDefPool.getDataSectionOffset()); - writeItem(writer, ItemType.MAP_LIST, 1, sectionOffset); - } - - private int calcNumItems() { - int numItems = 0; - - // header item - numItems++; - - if (dexFile.stringPool.getNumItems() > 0) { - numItems += 2; // index and data - } - if (dexFile.typePool.getNumItems() > 0) { - numItems++; - } - if (dexFile.protoPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.fieldPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.methodPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.typeListPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.encodedArrayPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.annotationPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.annotationSetPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.annotationSetRefPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.annotationDirectoryPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.debugInfoPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.codeItemPool.getNumItems() > 0) { - numItems++; - } - if (dexFile.classDefPool.getNumClassDefItems() > 0) { - numItems++; - } - if (dexFile.classDefPool.getNumClassDataItems() > 0) { - numItems++; - } - // map item itself - numItems++; - - return numItems; - } - - private void writeItem(DexWriter writer, int type, int size, int offset) throws IOException { - if (size > 0) { - writer.writeUshort(type); - writer.writeUshort(0); - writer.writeInt(size); - writer.writeInt(offset); - } - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/MethodPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/MethodPool.java deleted file mode 100644 index 40ab94e2..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/MethodPool.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.jf.dexlib2.iface.reference.MethodReference; -import org.jf.dexlib2.util.ReferenceUtil; -import org.jf.util.ExceptionWithContext; - -import javax.annotation.Nonnull; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class MethodPool { - public final static int METHOD_ID_ITEM_SIZE = 0x08; - - @Nonnull private final Map internedMethodIdItems = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; - - public MethodPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; - } - - public void intern(@Nonnull MethodReference method) { - Integer prev = internedMethodIdItems.put(method, 0); - if (prev == null) { - dexFile.typePool.intern(method.getDefiningClass()); - dexFile.protoPool.intern(method); - dexFile.stringPool.intern(method.getName()); - } - } - - public int getIndex(@Nonnull MethodReference methodReference) { - Integer index = internedMethodIdItems.get(methodReference); - if (index == null) { - throw new ExceptionWithContext("Method not found.: %s", ReferenceUtil.getMethodDescriptor(methodReference)); - } - return index; - } - - public int getIndexedSectionSize() { - return internedMethodIdItems.size() * METHOD_ID_ITEM_SIZE; - } - - public int getNumItems() { - return internedMethodIdItems.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - List methods = Lists.newArrayList(internedMethodIdItems.keySet()); - Collections.sort(methods); - - sectionOffset = writer.getPosition(); - int index = 0; - for (MethodReference method: methods) { - internedMethodIdItems.put(method, index++); - writer.writeUshort(dexFile.typePool.getIndex(method.getDefiningClass())); - writer.writeUshort(dexFile.protoPool.getIndex(method)); - writer.writeInt(dexFile.stringPool.getIndex(method.getName())); - } - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/MethodSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/MethodSection.java new file mode 100644 index 00000000..0ce47a6c --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/MethodSection.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.iface.reference.MethodReference; + +import javax.annotation.Nonnull; + +public interface MethodSection + extends IndexSection { + @Nonnull TypeKey getDefiningClass(@Nonnull MethodRefKey key); + @Nonnull ProtoKey getPrototype(@Nonnull MethodRefKey key); + @Nonnull StringKey getName(@Nonnull MethodRefKey key); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/NullableIndexSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/NullableIndexSection.java new file mode 100644 index 00000000..f3b6510d --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/NullableIndexSection.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import javax.annotation.Nullable; + +public interface NullableIndexSection extends IndexSection { + int getNullableItemIndex(@Nullable Key key); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/NullableOffsetSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/NullableOffsetSection.java new file mode 100644 index 00000000..9762ee5a --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/NullableOffsetSection.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import javax.annotation.Nullable; + +public interface NullableOffsetSection extends OffsetSection { + int getNullableItemOffset(@Nullable Key key); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/OffsetSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/OffsetSection.java new file mode 100644 index 00000000..6d8d8f0c --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/OffsetSection.java @@ -0,0 +1,41 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Map; + +public interface OffsetSection { + int getItemOffset(@Nonnull Key key); + @Nonnull Collection> getItems(); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/ProtoSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/ProtoSection.java new file mode 100644 index 00000000..e9ca595a --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/ProtoSection.java @@ -0,0 +1,41 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public interface ProtoSection extends IndexSection { + @Nonnull StringKey getShorty(@Nonnull ProtoKey key); + @Nonnull TypeKey getReturnType(@Nonnull ProtoKey key); + @Nullable TypeListKey getParameters(@Nonnull ProtoKey key); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/StringPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/StringPool.java deleted file mode 100644 index 7a31f2df..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/StringPool.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.jf.util.ExceptionWithContext; -import org.jf.util.StringUtils; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class StringPool { - public final static int STRING_ID_ITEM_SIZE = 0x04; - - @Nonnull protected final Map internedStringIdItems = Maps.newHashMap(); - private int indexSectionOffset = -1; - private int dataSectionOffset = -1; - - public void intern(@Nonnull CharSequence string) { - internedStringIdItems.put(string.toString(), 0); - } - - public void internNullable(@Nullable CharSequence string) { - if (string != null) { - intern(string); - } - } - - public int getIndex(@Nonnull CharSequence string) { - Integer index = internedStringIdItems.get(string.toString()); - if (index == null) { - throw new ExceptionWithContext("String not found.: %s", - StringUtils.escapeString(string.toString())); - } - return index; - } - - public int getIndexNullable(@Nullable CharSequence string) { - if (string == null) { - return -1; - } - return getIndex(string); - } - - public int getIndexedSectionSize() { - return internedStringIdItems.size() * STRING_ID_ITEM_SIZE; - } - - public int getNumItems() { - return internedStringIdItems.size(); - } - - public int getIndexSectionOffset() { - return indexSectionOffset; - } - - public int getDataSectionOffset() { - return dataSectionOffset; - } - - public void write(@Nonnull DexWriter indexWriter, @Nonnull DexWriter offsetWriter) throws IOException { - List strings = Lists.newArrayList(internedStringIdItems.keySet()); - Collections.sort(strings); - - indexSectionOffset = indexWriter.getPosition(); - dataSectionOffset = offsetWriter.getPosition(); - - int index = 0; - for (String string: strings) { - internedStringIdItems.put(string, index++); - indexWriter.writeInt(offsetWriter.getPosition()); - offsetWriter.writeUleb128(string.length()); - offsetWriter.writeString(string); - offsetWriter.write(0); - } - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/StringSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/StringSection.java new file mode 100644 index 00000000..9dc5886e --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/StringSection.java @@ -0,0 +1,39 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.iface.reference.StringReference; +import org.jf.dexlib2.writer.util.InstructionWriteUtil; + +public interface StringSection extends NullableIndexSection, + InstructionWriteUtil.StringIndexProvider { +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeListSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeListSection.java new file mode 100644 index 00000000..18afa99d --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeListSection.java @@ -0,0 +1,41 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; + +public interface TypeListSection extends NullableOffsetSection { + int getNullableItemOffset(@Nullable TypeListKey index); + @Nonnull Collection getTypes(@Nullable TypeListKey key); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/TypePool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/TypePool.java deleted file mode 100644 index 88d5045d..00000000 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/TypePool.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2012, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib2.writer; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.jf.util.ExceptionWithContext; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class TypePool { - public final static int TYPE_ID_ITEM_SIZE = 0x04; - - @Nonnull private final Map internedTypeIdItems = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; - - public TypePool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; - } - - public void intern(@Nonnull CharSequence type) { - Integer prev = internedTypeIdItems.put(type.toString(), 0); - if (prev == null) { - dexFile.stringPool.intern(type); - } - } - - public void internNullable(@Nullable CharSequence type) { - if (type != null) { - intern(type); - } - } - - public int getIndex(@Nonnull CharSequence type) { - Integer index = internedTypeIdItems.get(type.toString()); - if (index == null) { - throw new ExceptionWithContext("Type not found.: %s", type); - } - return index; - } - - public int getIndexNullable(@Nullable CharSequence type) { - if (type == null) { - return -1; - } - return getIndex(type); - } - - public int getIndexedSectionSize() { - return internedTypeIdItems.size() * TYPE_ID_ITEM_SIZE; - } - - public int getNumItems() { - return internedTypeIdItems.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - List types = Lists.newArrayList(internedTypeIdItems.keySet()); - Collections.sort(types); - - sectionOffset = writer.getPosition(); - int index = 0; - for (String type: types) { - internedTypeIdItems.put(type, index++); - int stringIndex = dexFile.stringPool.getIndex(type); - writer.writeInt(stringIndex); - } - } -} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeSection.java new file mode 100644 index 00000000..62f32809 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeSection.java @@ -0,0 +1,41 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.iface.reference.TypeReference; + +import javax.annotation.Nonnull; + +public interface TypeSection extends NullableIndexSection { + @Nonnull StringKey getString(@Nonnull TypeKey key); + int getItemIndex(@Nonnull TypeReference key); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/AnnotationPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/AnnotationPool.java new file mode 100644 index 00000000..c4cfa390 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/AnnotationPool.java @@ -0,0 +1,87 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.AnnotationElement; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.writer.AnnotationSection; + +import javax.annotation.Nonnull; +import java.util.Collection; + +public class AnnotationPool extends BaseOffsetPool + implements AnnotationSection { + @Nonnull StringPool stringPool; + @Nonnull TypePool typePool; + @Nonnull FieldPool fieldPool; + @Nonnull MethodPool methodPool; + + public AnnotationPool(@Nonnull StringPool stringPool, @Nonnull TypePool typePool, + @Nonnull FieldPool fieldPool, @Nonnull MethodPool methodPool) { + this.stringPool = stringPool; + this.typePool = typePool; + this.fieldPool = fieldPool; + this.methodPool = methodPool; + } + + public void intern(@Nonnull Annotation annotation) { + Integer prev = internedItems.put(annotation, 0); + if (prev == null) { + typePool.intern(annotation.getType()); + for (AnnotationElement element: annotation.getElements()) { + stringPool.intern(element.getName()); + DexPool.internEncodedValue(element.getValue(), stringPool, typePool, fieldPool, methodPool); + } + } + } + + @Override public int getVisibility(@Nonnull Annotation annotation) { + return annotation.getVisibility(); + } + + @Nonnull @Override public CharSequence getType(@Nonnull Annotation annotation) { + return annotation.getType(); + } + + @Nonnull @Override public Collection getElements(@Nonnull Annotation annotation) { + return annotation.getElements(); + } + + @Nonnull @Override public CharSequence getElementName(@Nonnull AnnotationElement annotationElement) { + return annotationElement.getName(); + } + + @Nonnull @Override public EncodedValue getElementValue(@Nonnull AnnotationElement annotationElement) { + return annotationElement.getValue(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/AnnotationSetPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/AnnotationSetPool.java new file mode 100644 index 00000000..6512dcb4 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/AnnotationSetPool.java @@ -0,0 +1,64 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.writer.AnnotationSetSection; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Set; + +public class AnnotationSetPool extends BaseNullableOffsetPool> + implements AnnotationSetSection> { + @Nonnull private final AnnotationPool annotationPool; + + public AnnotationSetPool(@Nonnull AnnotationPool annotationPool) { + this.annotationPool = annotationPool; + } + + public void intern(@Nonnull Set annotationSet) { + if (annotationSet.size() > 0) { + Integer prev = internedItems.put(annotationSet, 0); + if (prev == null) { + for (Annotation annotation: annotationSet) { + annotationPool.intern(annotation); + } + } + } + } + + @Nonnull @Override public Collection getAnnotations( + @Nonnull Set annotations) { + return annotations; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetRefPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/AnnotationSetRefPool.java similarity index 65% rename from dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetRefPool.java rename to dexlib2/src/main/java/org/jf/dexlib2/writer/pool/AnnotationSetRefPool.java index ab02fe02..8f49403a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/AnnotationSetRefPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/AnnotationSetRefPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2012, Google Inc. + * Copyright 2013, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,82 +29,49 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.jf.dexlib2.writer; +package org.jf.dexlib2.writer.pool; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.common.collect.Ordering; import com.google.common.primitives.Ints; import org.jf.dexlib2.iface.Annotation; import org.jf.dexlib2.iface.Method; import org.jf.dexlib2.iface.MethodParameter; +import org.jf.dexlib2.writer.pool.AnnotationSetRefPool.Key; +import org.jf.dexlib2.writer.AnnotationSetRefSection; import org.jf.util.CollectionUtils; -import org.jf.util.ExceptionWithContext; import javax.annotation.Nonnull; -import java.io.IOException; -import java.util.*; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; -public class AnnotationSetRefPool { - @Nonnull private final Map internedAnnotationSetRefItems = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; +public class AnnotationSetRefPool extends BaseNullableOffsetPool + implements AnnotationSetRefSection, Key> { + @Nonnull private final AnnotationSetPool annotationSetPool; - public AnnotationSetRefPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; + public AnnotationSetRefPool(@Nonnull AnnotationSetPool annotationSetPool) { + this.annotationSetPool = annotationSetPool; } public void intern(@Nonnull Method method) { Key annotationSetRefKey = new Key(method); - Integer prev = internedAnnotationSetRefItems.put(annotationSetRefKey, 0); + Integer prev = internedItems.put(annotationSetRefKey, 0); if (prev == null) { for (Set annotationSet: annotationSetRefKey.getAnnotationSets()) { - dexFile.annotationSetPool.intern(annotationSet); + annotationSetPool.intern(annotationSet); } } } - public int getOffset(@Nonnull Method method) { - Key annotationSetRefKey = new Key(method); - Integer offset = internedAnnotationSetRefItems.get(annotationSetRefKey); - if (offset == null) { - throw new ExceptionWithContext("Annotation set ref not found."); - } - return offset; + @Nonnull @Override public Collection> getAnnotationSets(@Nonnull Key key) { + return key.getAnnotationSets(); } - public int getNumItems() { - return internedAnnotationSetRefItems.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - List annotationSetRefs = - Lists.newArrayList(internedAnnotationSetRefItems.keySet()); - Collections.sort(annotationSetRefs); - - writer.align(); - sectionOffset = writer.getPosition(); - for (Key key: annotationSetRefs) { - writer.align(); - internedAnnotationSetRefItems.put(key, writer.getPosition()); - writer.writeInt(key.getAnnotationSetCount()); - for (Set annotationSet: key.getAnnotationSets()) { - writer.writeInt(dexFile.annotationSetPool.getOffset(annotationSet)); - } - } - } - - private static class Key implements Comparable { + public static class Key implements Comparable { @Nonnull private final Method method; private final int size; @@ -113,14 +80,18 @@ public class AnnotationSetRefPool { this.size = CollectionUtils.lastIndexOf(method.getParameters(), HAS_ANNOTATIONS) + 1; } - public int getAnnotationSetCount() { - return size; - } + public Collection> getAnnotationSets() { + return new AbstractCollection>() { + @Nonnull @Override public Iterator> iterator() { + return FluentIterable.from(method.getParameters()) + .limit(size) + .transform(PARAMETER_ANNOTATIONS).iterator(); + } - public Iterable> getAnnotationSets() { - return FluentIterable.from(method.getParameters()) - .limit(size) - .transform(PARAMETER_ANNOTATIONS); + @Override public int size() { + return size; + } + }; } @Override diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/BaseIndexPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/BaseIndexPool.java new file mode 100644 index 00000000..01109ad4 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/BaseIndexPool.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import com.google.common.collect.Maps; +import org.jf.dexlib2.writer.IndexSection; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Map; + +public abstract class BaseIndexPool implements IndexSection { + @Nonnull protected final Map internedItems = Maps.newHashMap(); + + @Nonnull @Override public Collection> getItems() { + return internedItems.entrySet(); + } + + @Override public int getItemIndex(@Nonnull Key key) { + Integer index = internedItems.get(key); + if (index == null) { + throw new ExceptionWithContext("Item not found.: %s", getItemString(key)); + } + return index; + } + + @Nonnull protected String getItemString(@Nonnull Key key) { + return key.toString(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/BaseNullableOffsetPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/BaseNullableOffsetPool.java new file mode 100644 index 00000000..ed9dbb6a --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/BaseNullableOffsetPool.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import org.jf.dexlib2.writer.DexWriter; +import org.jf.dexlib2.writer.NullableOffsetSection; + +import javax.annotation.Nullable; + +public abstract class BaseNullableOffsetPool extends BaseOffsetPool + implements NullableOffsetSection { + @Override public int getNullableItemOffset(@Nullable Key key) { + if (key == null) { + return DexWriter.NO_OFFSET; + } + return getItemOffset(key); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/BaseOffsetPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/BaseOffsetPool.java new file mode 100644 index 00000000..e66a50ad --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/BaseOffsetPool.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import com.google.common.collect.Maps; +import org.jf.dexlib2.writer.OffsetSection; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Map; + +public abstract class BaseOffsetPool implements OffsetSection { + @Nonnull protected final Map internedItems = Maps.newHashMap(); + + @Nonnull @Override public Collection> getItems() { + return internedItems.entrySet(); + } + + @Override public int getItemOffset(@Nonnull Key key) { + Integer offset = internedItems.get(key); + if (offset == null) { + throw new ExceptionWithContext("Item not found.: %s", getItemString(key)); + } + return offset; + } + + @Nonnull protected String getItemString(@Nonnull Key key) { + return key.toString(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java new file mode 100644 index 00000000..71713aba --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java @@ -0,0 +1,480 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.common.collect.Ordering; +import org.jf.dexlib2.DebugItemType; +import org.jf.dexlib2.ReferenceType; +import org.jf.dexlib2.iface.*; +import org.jf.dexlib2.iface.debug.*; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.ReferenceInstruction; +import org.jf.dexlib2.iface.reference.*; +import org.jf.dexlib2.util.ReferenceUtil; +import org.jf.dexlib2.writer.ClassSection; +import org.jf.dexlib2.writer.DebugWriter; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.*; +import java.util.Map.Entry; + +public class ClassPool implements ClassSection>, PoolClassDef, Field, PoolMethod, + Set, AnnotationSetRefPool.Key, EncodedArrayPool.Key, + DebugItem, Instruction, ExceptionHandler> { + @Nonnull private HashMap internedItems = Maps.newHashMap(); + + @Nonnull private final StringPool stringPool; + @Nonnull private final TypePool typePool; + @Nonnull private final FieldPool fieldPool; + @Nonnull private final MethodPool methodPool; + @Nonnull private final AnnotationSetPool annotationSetPool; + @Nonnull private final AnnotationSetRefPool annotationSetRefPool; + @Nonnull private final TypeListPool typeListPool; + @Nonnull private final EncodedArrayPool encodedArrayPool; + + public ClassPool(@Nonnull StringPool stringPool, + @Nonnull TypePool typePool, + @Nonnull FieldPool fieldPool, + @Nonnull MethodPool methodPool, + @Nonnull AnnotationSetPool annotationSetPool, + @Nonnull AnnotationSetRefPool annotationSetRefPool, + @Nonnull TypeListPool typeListPool, + @Nonnull EncodedArrayPool encodedArrayPool) { + this.stringPool = stringPool; + this.typePool = typePool; + this.fieldPool = fieldPool; + this.methodPool = methodPool; + this.annotationSetPool = annotationSetPool; + this.annotationSetRefPool = annotationSetRefPool; + this.typeListPool = typeListPool; + this.encodedArrayPool = encodedArrayPool; + } + + public void intern(@Nonnull ClassDef classDef) { + PoolClassDef poolClassDef = new PoolClassDef(classDef); + + PoolClassDef prev = internedItems.put(poolClassDef.getType(), poolClassDef); + if (prev != null) { + throw new ExceptionWithContext("Class %s has already been interned", poolClassDef.getType()); + } + + typePool.intern(poolClassDef.getType()); + typePool.internNullable(poolClassDef.getSuperclass()); + typeListPool.intern(poolClassDef.getInterfaces()); + stringPool.internNullable(poolClassDef.getSourceFile()); + encodedArrayPool.intern(poolClassDef); + + HashSet fields = new HashSet(); + for (Field field: poolClassDef.getFields()) { + String fieldDescriptor = ReferenceUtil.getShortFieldDescriptor(field); + if (!fields.add(fieldDescriptor)) { + throw new ExceptionWithContext("Multiple definitions for field %s->%s", + poolClassDef.getType(), fieldDescriptor); + } + fieldPool.intern(field); + + annotationSetPool.intern(field.getAnnotations()); + } + + HashSet methods = new HashSet(); + for (PoolMethod method: poolClassDef.getMethods()) { + String methodDescriptor = ReferenceUtil.getShortMethodDescriptor(method); + if (!methods.add(methodDescriptor)) { + throw new ExceptionWithContext("Multiple definitions for method %s->%s", + poolClassDef.getType(), methodDescriptor); + } + methodPool.intern(method); + internCode(method); + internDebug(method); + annotationSetPool.intern(method.getAnnotations()); + annotationSetRefPool.intern(method); + } + + annotationSetPool.intern(poolClassDef.getAnnotations()); + } + + private void internCode(@Nonnull Method method) { + // this also handles parameter names, which aren't directly tied to the MethodImplementation, even though the debug items are + boolean hasInstruction = false; + + MethodImplementation methodImpl = method.getImplementation(); + if (methodImpl != null) { + for (Instruction instruction: methodImpl.getInstructions()) { + hasInstruction = true; + if (instruction instanceof ReferenceInstruction) { + Reference reference = ((ReferenceInstruction)instruction).getReference(); + switch (instruction.getOpcode().referenceType) { + case ReferenceType.STRING: + stringPool.intern((StringReference)reference); + break; + case ReferenceType.TYPE: + typePool.intern((TypeReference)reference); + break; + case ReferenceType.FIELD: + fieldPool.intern((FieldReference) reference); + break; + case ReferenceType.METHOD: + methodPool.intern((MethodReference)reference); + break; + default: + throw new ExceptionWithContext("Unrecognized reference type: %d", + instruction.getOpcode().referenceType); + } + } + } + + List tryBlocks = methodImpl.getTryBlocks(); + if (!hasInstruction && tryBlocks.size() > 0) { + throw new ExceptionWithContext("Method %s has no instructions, but has try blocks.", + ReferenceUtil.getMethodDescriptor(method)); + } + + for (TryBlock tryBlock: methodImpl.getTryBlocks()) { + for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) { + typePool.internNullable(handler.getExceptionType()); + } + } + } + } + + private void internDebug(@Nonnull Method method) { + for (MethodParameter param: method.getParameters()) { + String paramName = param.getName(); + if (paramName != null) { + stringPool.intern(paramName); + } + } + + MethodImplementation methodImpl = method.getImplementation(); + if (methodImpl != null) { + for (DebugItem debugItem: methodImpl.getDebugItems()) { + switch (debugItem.getDebugItemType()) { + case DebugItemType.START_LOCAL: + StartLocal startLocal = (StartLocal)debugItem; + stringPool.internNullable(startLocal.getName()); + typePool.internNullable(startLocal.getType()); + stringPool.internNullable(startLocal.getSignature()); + break; + case DebugItemType.SET_SOURCE_FILE: + stringPool.internNullable(((SetSourceFile) debugItem).getSourceFile()); + break; + } + } + } + } + + private ImmutableList sortedClasses = null; + @Nonnull @Override public Collection getSortedClasses() { + if (sortedClasses == null) { + sortedClasses = Ordering.natural().immutableSortedCopy(internedItems.values()); + } + return sortedClasses; + } + + @Nullable @Override + public Map.Entry getClassEntryByType(@Nullable CharSequence name) { + if (name == null) { + return null; + } + + final PoolClassDef classDef = internedItems.get(name.toString()); + if (classDef == null) { + return null; + } + + return new Map.Entry() { + @Override public PoolClassDef getKey() { + return classDef; + } + + @Override public Integer getValue() { + return classDef.classDefOffset; + } + + @Override public Integer setValue(Integer value) { + return classDef.classDefOffset = value; + } + }; + } + + @Nonnull @Override public CharSequence getType(@Nonnull PoolClassDef classDef) { + return classDef.getType(); + } + + @Override public int getAccessFlags(@Nonnull PoolClassDef classDef) { + return classDef.getAccessFlags(); + } + + @Nullable @Override public CharSequence getSuperclass(@Nonnull PoolClassDef classDef) { + return classDef.getSuperclass(); + } + + @Nullable @Override public TypeListPool.Key> getSortedInterfaces(@Nonnull PoolClassDef classDef) { + return classDef.interfaces; + } + + @Nullable @Override public CharSequence getSourceFile(@Nonnull PoolClassDef classDef) { + return classDef.getSourceFile(); + } + + @Nullable @Override public EncodedArrayPool.Key getStaticInitializers(@Nonnull PoolClassDef classDef) { + return EncodedArrayPool.Key.of(classDef); + } + + @Nonnull @Override public Collection getSortedStaticFields(@Nonnull PoolClassDef classDef) { + return classDef.getStaticFields(); + } + + @Nonnull @Override public Collection getSortedInstanceFields(@Nonnull PoolClassDef classDef) { + return classDef.getInstanceFields(); + } + + @Nonnull @Override public Collection getSortedDirectMethods(@Nonnull PoolClassDef classDef) { + return classDef.getDirectMethods(); + } + + @Nonnull @Override public Collection getSortedVirtualMethods(@Nonnull PoolClassDef classDef) { + return classDef.getVirtualMethods(); + } + + @Override public int getFieldAccessFlags(@Nonnull Field field) { + return field.getAccessFlags(); + } + + @Override public int getMethodAccessFlags(@Nonnull PoolMethod method) { + return method.getAccessFlags(); + } + + @Nullable @Override public Set getClassAnnotations(@Nonnull PoolClassDef classDef) { + Set annotations = classDef.getAnnotations(); + if (annotations.size() == 0) { + return null; + } + return annotations; + } + + @Nullable @Override public Set getFieldAnnotations(@Nonnull Field field) { + Set annotations = field.getAnnotations(); + if (annotations.size() == 0) { + return null; + } + return annotations; + } + + @Nullable @Override public Set getMethodAnnotations(@Nonnull PoolMethod method) { + Set annotations = method.getAnnotations(); + if (annotations.size() == 0) { + return null; + } + return annotations; + } + + @Nullable @Override public AnnotationSetRefPool.Key getParameterAnnotations(@Nonnull PoolMethod method) { + AnnotationSetRefPool.Key key = new AnnotationSetRefPool.Key(method); + Collection> annotations = key.getAnnotationSets(); + if (annotations.size() == 0) { + return null; + } + return key; + } + + @Nullable @Override public Iterable getDebugItems(@Nonnull PoolMethod method) { + MethodImplementation impl = method.getImplementation(); + if (impl != null) { + return impl.getDebugItems(); + } + return null; + } + + @Nullable @Override public Iterable getParameterNames(@Nonnull PoolMethod method) { + return Iterables.transform(method.getParameters(), new Function() { + @Nullable @Override public CharSequence apply(MethodParameter input) { + return input.getName(); + } + }); + } + + @Override public int getRegisterCount(@Nonnull PoolMethod method) { + MethodImplementation impl = method.getImplementation(); + if (impl != null) { + return impl.getRegisterCount(); + } + return 0; + } + + @Nullable @Override public Iterable getInstructions(@Nonnull PoolMethod method) { + MethodImplementation impl = method.getImplementation(); + if (impl != null) { + return impl.getInstructions(); + } + return null; + } + + @Nonnull @Override public List> getTryBlocks( + @Nonnull PoolMethod method) { + MethodImplementation impl = method.getImplementation(); + if (impl != null) { + return impl.getTryBlocks(); + } + return ImmutableList.of(); + } + + @Nullable @Override public CharSequence getExceptionType(@Nonnull ExceptionHandler handler) { + return handler.getExceptionType(); + } + + @Override public void setAnnotationDirectoryOffset(@Nonnull PoolClassDef classDef, int offset) { + classDef.annotationDirectoryOffset = offset; + } + + @Override public int getAnnotationDirectoryOffset(@Nonnull PoolClassDef classDef) { + return classDef.annotationDirectoryOffset; + } + + @Override public void setCodeItemOffset(@Nonnull PoolMethod method, int offset) { + method.codeItemOffset = offset; + } + + @Override public int getCodeItemOffset(@Nonnull PoolMethod method) { + return method.codeItemOffset; + } + + @Override public void setDebugItemOffset(@Nonnull PoolMethod method, int offset) { + method.debugInfoOffset = offset; + } + + @Override public int getDebugItemOffset(@Nonnull PoolMethod method) { + return method.debugInfoOffset; + } + + @Override public void writeDebugItem(@Nonnull DebugWriter writer, + DebugItem debugItem) throws IOException { + switch (debugItem.getDebugItemType()) { + case DebugItemType.START_LOCAL: { + StartLocal startLocal = (StartLocal)debugItem; + writer.writeStartLocal(startLocal.getCodeAddress(), + startLocal.getRegister(), + startLocal.getName(), + startLocal.getType(), + startLocal.getSignature()); + break; + } + case DebugItemType.END_LOCAL: { + EndLocal endLocal = (EndLocal)debugItem; + writer.writeEndLocal(endLocal.getCodeAddress(), endLocal.getRegister()); + break; + } + case DebugItemType.RESTART_LOCAL: { + RestartLocal restartLocal = (RestartLocal)debugItem; + writer.writeRestartLocal(restartLocal.getCodeAddress(), restartLocal.getRegister()); + break; + } + case DebugItemType.PROLOGUE_END: { + writer.writePrologueEnd(debugItem.getCodeAddress()); + break; + } + case DebugItemType.EPILOGUE_BEGIN: { + writer.writeEpilogueBegin(debugItem.getCodeAddress()); + break; + } + case DebugItemType.LINE_NUMBER: { + LineNumber lineNumber = (LineNumber)debugItem; + writer.writeLineNumber(lineNumber.getCodeAddress(), lineNumber.getLineNumber()); + break; + } + case DebugItemType.SET_SOURCE_FILE: { + SetSourceFile setSourceFile = (SetSourceFile)debugItem; + writer.writeSetSourceFile(setSourceFile.getCodeAddress(), setSourceFile.getSourceFile()); + } + default: + throw new ExceptionWithContext("Unexpected debug item type: %d", debugItem.getDebugItemType()); + } + } + + @Override public int getItemIndex(@Nonnull PoolClassDef classDef) { + return classDef.classDefOffset; + } + + @Nonnull @Override public Collection> getItems() { + class MapEntry implements Map.Entry { + PoolClassDef classDef = null; + + @Override public PoolClassDef getKey() { + return classDef; + } + + @Override public Integer getValue() { + return classDef.classDefOffset; + } + + @Override public Integer setValue(Integer value) { + int prev = classDef.classDefOffset; + classDef.classDefOffset = value; + return prev; + } + } + final MapEntry entry = new MapEntry(); + + return new AbstractCollection>() { + @Nonnull @Override public Iterator> iterator() { + return new Iterator>() { + Iterator iter = internedItems.values().iterator(); + + @Override public boolean hasNext() { + return iter.hasNext(); + } + + @Override public Entry next() { + entry.classDef = iter.next(); + return entry; + } + + @Override public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override public int size() { + return internedItems.size(); + } + }; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/DexPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/DexPool.java new file mode 100644 index 00000000..6e97cdf1 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/DexPool.java @@ -0,0 +1,190 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.*; +import org.jf.dexlib2.iface.debug.DebugItem; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.iface.reference.StringReference; +import org.jf.dexlib2.iface.reference.TypeReference; +import org.jf.dexlib2.iface.value.*; +import org.jf.dexlib2.immutable.instruction.ImmutableInstructionFactory; +import org.jf.dexlib2.writer.pool.ProtoPool.Key; +import org.jf.dexlib2.writer.DexWriter; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.Collection; +import java.util.Set; + +public class DexPool extends DexWriter, AnnotationSetRefPool.Key, + TypeListPool.Key>, EncodedArrayPool.Key, Field, PoolMethod, + EncodedValue, AnnotationElement, DebugItem, Instruction, ExceptionHandler> { + + public static DexPool makeDexPool() { + StringPool stringPool = new StringPool(); + TypePool typePool = new TypePool(stringPool); + FieldPool fieldPool = new FieldPool(stringPool, typePool); + TypeListPool typeListPool = new TypeListPool(typePool); + ProtoPool protoPool = new ProtoPool(stringPool, typePool, typeListPool); + MethodPool methodPool = new MethodPool(stringPool, typePool, protoPool); + EncodedArrayPool encodedArrayPool = new EncodedArrayPool(stringPool, typePool, fieldPool, methodPool); + AnnotationPool annotationPool = new AnnotationPool(stringPool, typePool, fieldPool, methodPool); + AnnotationSetPool annotationSetPool = new AnnotationSetPool(annotationPool); + AnnotationSetRefPool annotationSetRefPool = new AnnotationSetRefPool(annotationSetPool); + ClassPool classPool = new ClassPool(stringPool, typePool, fieldPool, methodPool, annotationSetPool, + annotationSetRefPool, typeListPool, encodedArrayPool); + + return new DexPool(stringPool, typePool, protoPool, fieldPool, methodPool, classPool, typeListPool, + annotationPool, annotationSetPool, annotationSetRefPool, encodedArrayPool); + + } + + private DexPool(StringPool stringPool, TypePool typePool, ProtoPool protoPool, FieldPool fieldPool, + MethodPool methodPool, ClassPool classPool, TypeListPool typeListPool, + AnnotationPool annotationPool, AnnotationSetPool annotationSetPool, + AnnotationSetRefPool annotationSetRefPool, EncodedArrayPool encodedArrayPool) { + super(ImmutableInstructionFactory.INSTANCE, stringPool, typePool, protoPool, fieldPool, methodPool, classPool, + typeListPool, annotationPool, annotationSetPool, annotationSetRefPool, encodedArrayPool); + } + + public static void writeTo(@Nonnull String path, @Nonnull org.jf.dexlib2.iface.DexFile input) throws IOException { + DexPool dexPool = makeDexPool(); + for (ClassDef classDef: input.getClasses()) { + ((ClassPool)dexPool.classSection).intern(classDef); + } + /*System.out.println("here"); + while(true);*/ + dexPool.writeTo(path); + } + + @Override protected void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer, + @Nonnull EncodedValue encodedValue) throws IOException { + switch (encodedValue.getValueType()) { + case ValueType.ANNOTATION: + AnnotationEncodedValue annotationEncodedValue = (AnnotationEncodedValue)encodedValue; + writer.writeAnnotation(annotationEncodedValue.getType(), annotationEncodedValue.getElements()); + break; + case ValueType.ARRAY: + ArrayEncodedValue arrayEncodedValue = (ArrayEncodedValue)encodedValue; + writer.writeArray(arrayEncodedValue.getValue()); + break; + case ValueType.BOOLEAN: + writer.writeBoolean(((BooleanEncodedValue)encodedValue).getValue()); + break; + case ValueType.BYTE: + writer.writeByte(((ByteEncodedValue)encodedValue).getValue()); + break; + case ValueType.CHAR: + writer.writeChar(((CharEncodedValue)encodedValue).getValue()); + break; + case ValueType.DOUBLE: + writer.writeDouble(((DoubleEncodedValue)encodedValue).getValue()); + break; + case ValueType.ENUM: + writer.writeEnum(((EnumEncodedValue)encodedValue).getValue()); + break; + case ValueType.FIELD: + writer.writeField(((FieldEncodedValue)encodedValue).getValue()); + break; + case ValueType.FLOAT: + writer.writeFloat(((FloatEncodedValue)encodedValue).getValue()); + break; + case ValueType.INT: + writer.writeInt(((IntEncodedValue)encodedValue).getValue()); + break; + case ValueType.LONG: + writer.writeLong(((LongEncodedValue)encodedValue).getValue()); + break; + case ValueType.METHOD: + writer.writeMethod(((MethodEncodedValue)encodedValue).getValue()); + break; + case ValueType.NULL: + writer.writeNull(); + break; + case ValueType.SHORT: + writer.writeShort(((ShortEncodedValue)encodedValue).getValue()); + break; + case ValueType.STRING: + writer.writeString(((StringEncodedValue)encodedValue).getValue()); + break; + case ValueType.TYPE: + writer.writeType(((TypeEncodedValue)encodedValue).getValue()); + break; + default: + throw new ExceptionWithContext("Unrecognized value type: %d", encodedValue.getValueType()); + } + } + + public static void internEncodedValue(@Nonnull EncodedValue encodedValue, + @Nonnull StringPool stringPool, + @Nonnull TypePool typePool, + @Nonnull FieldPool fieldPool, + @Nonnull MethodPool methodPool) { + switch (encodedValue.getValueType()) { + case ValueType.ANNOTATION: + AnnotationEncodedValue annotationEncodedValue = (AnnotationEncodedValue)encodedValue; + typePool.intern(annotationEncodedValue.getType()); + for (AnnotationElement element: annotationEncodedValue.getElements()) { + stringPool.intern(element.getName()); + internEncodedValue(element.getValue(), stringPool, typePool, fieldPool, methodPool); + } + break; + case ValueType.ARRAY: + for (EncodedValue element: ((ArrayEncodedValue)encodedValue).getValue()) { + internEncodedValue(element, stringPool, typePool, fieldPool, methodPool); + } + break; + case ValueType.STRING: + stringPool.intern(((StringEncodedValue)encodedValue).getValue()); + break; + case ValueType.TYPE: + typePool.intern(((TypeEncodedValue)encodedValue).getValue()); + break; + case ValueType.ENUM: + fieldPool.intern(((EnumEncodedValue)encodedValue).getValue()); + break; + case ValueType.FIELD: + fieldPool.intern(((FieldEncodedValue)encodedValue).getValue()); + break; + case ValueType.METHOD: + methodPool.intern(((MethodEncodedValue)encodedValue).getValue()); + break; + } + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedArrayPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/EncodedArrayPool.java similarity index 65% rename from dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedArrayPool.java rename to dexlib2/src/main/java/org/jf/dexlib2/writer/pool/EncodedArrayPool.java index 62410038..aa02e101 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedArrayPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/EncodedArrayPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2012, Google Inc. + * Copyright 2013, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,84 +29,64 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.jf.dexlib2.writer; +package org.jf.dexlib2.writer.pool; import com.google.common.base.Function; import com.google.common.base.Predicate; -import com.google.common.collect.*; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Iterables; +import com.google.common.collect.Ordering; import com.google.common.primitives.Ints; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.Field; import org.jf.dexlib2.iface.value.EncodedValue; import org.jf.dexlib2.immutable.value.ImmutableEncodedValueFactory; import org.jf.dexlib2.util.EncodedValueUtils; +import org.jf.dexlib2.writer.pool.EncodedArrayPool.Key; +import org.jf.dexlib2.writer.EncodedArraySection; import org.jf.util.CollectionUtils; -import org.jf.util.ExceptionWithContext; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.io.IOException; -import java.util.*; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; -public class EncodedArrayPool { - @Nonnull private final Map internedEncodedArrayItems = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; +public class EncodedArrayPool extends BaseNullableOffsetPool + implements EncodedArraySection { + @Nonnull private final StringPool stringPool; + @Nonnull private final TypePool typePool; + @Nonnull private final FieldPool fieldPool; + @Nonnull private final MethodPool methodPool; - public EncodedArrayPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; + public EncodedArrayPool(@Nonnull StringPool stringPool, + @Nonnull TypePool typePool, + @Nonnull FieldPool fieldPool, + @Nonnull MethodPool methodPool) { + this.stringPool = stringPool; + this.typePool = typePool; + this.fieldPool = fieldPool; + this.methodPool = methodPool; } public void intern(@Nonnull ClassDef classDef) { Key key = Key.of(classDef); if (key != null) { - Integer prev = internedEncodedArrayItems.put(key, 0); + Integer prev = internedItems.put(key, 0); if (prev == null) { - for (EncodedValue encodedValue: key.getElements()) { - dexFile.internEncodedValue(encodedValue); + for (EncodedValue encodedValue: key) { + DexPool.internEncodedValue(encodedValue, stringPool, typePool, fieldPool, methodPool); } } } } - public int getOffset(@Nonnull ClassDef classDef) { - Key key = Key.of(classDef); - if (key != null) { - Integer offset = internedEncodedArrayItems.get(key); - if (offset == null) { - throw new ExceptionWithContext("Encoded array not found."); - } - return offset; - } - return 0; + @Nonnull @Override public Collection getElements(@Nonnull Key key) { + return key; } - public int getNumItems() { - return internedEncodedArrayItems.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - List encodedArrays = Lists.newArrayList(internedEncodedArrayItems.keySet()); - Collections.sort(encodedArrays); - - sectionOffset = writer.getPosition(); - for (Key encodedArray: encodedArrays) { - internedEncodedArrayItems.put(encodedArray, writer.getPosition()); - writer.writeUleb128(encodedArray.getElementCount()); - for (EncodedValue value: encodedArray.getElements()) { - dexFile.writeEncodedValue(writer, value); - } - } - } - - public static class Key implements Comparable { + public static class Key extends AbstractCollection implements Comparable { private final List fields; private final int size; @@ -139,20 +119,9 @@ public class EncodedArrayPool { return null; } - public int getElementCount() { - return size; - } - - @Nonnull - public Iterable getElements() { - return FluentIterable.from(fields) - .limit(size) - .transform(GET_INITIAL_VALUE); - } - @Override public int hashCode() { - return CollectionUtils.listHashCode(getElements()); + return CollectionUtils.listHashCode(this); } @Override @@ -162,7 +131,7 @@ public class EncodedArrayPool { if (size != other.size) { return false; } - return Iterables.elementsEqual(getElements(), other.getElements()); + return Iterables.elementsEqual(this, other); } return false; } @@ -181,8 +150,8 @@ public class EncodedArrayPool { if (res != 0) { return res; } - Iterator otherElements = o.getElements().iterator(); - for (EncodedValue element: getElements()) { + Iterator otherElements = o.iterator(); + for (EncodedValue element: this) { res = element.compareTo(otherElements.next()); if (res != 0) { return res; @@ -190,5 +159,15 @@ public class EncodedArrayPool { } return 0; } + + @Nonnull @Override public Iterator iterator() { + return FluentIterable.from(fields) + .limit(size) + .transform(GET_INITIAL_VALUE).iterator(); + } + + @Override public int size() { + return size; + } } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/FieldPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/FieldPool.java new file mode 100644 index 00000000..6fdda28b --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/FieldPool.java @@ -0,0 +1,69 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.writer.FieldSection; + +import javax.annotation.Nonnull; + +public class FieldPool extends BaseIndexPool + implements FieldSection { + @Nonnull private final StringPool stringPool; + @Nonnull private final TypePool typePool; + + public FieldPool(@Nonnull StringPool stringPool, @Nonnull TypePool typePool) { + this.stringPool = stringPool; + this.typePool = typePool; + } + + public void intern(@Nonnull FieldReference field) { + Integer prev = internedItems.put(field, 0); + if (prev == null) { + typePool.intern(field.getDefiningClass()); + stringPool.intern(field.getName()); + typePool.intern(field.getType()); + } + } + + @Nonnull @Override public CharSequence getDefiningClass(@Nonnull FieldReference fieldReference) { + return fieldReference.getDefiningClass(); + } + + @Nonnull @Override public CharSequence getFieldType(@Nonnull FieldReference fieldReference) { + return fieldReference.getType(); + } + + @Nonnull @Override public CharSequence getName(@Nonnull FieldReference fieldReference) { + return fieldReference.getName(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/MethodPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/MethodPool.java new file mode 100644 index 00000000..3ea5fc6c --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/MethodPool.java @@ -0,0 +1,72 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.writer.MethodSection; + +import javax.annotation.Nonnull; + +public class MethodPool extends BaseIndexPool + implements MethodSection { + @Nonnull private final StringPool stringPool; + @Nonnull private final TypePool typePool; + @Nonnull private final ProtoPool protoPool; + + public MethodPool(@Nonnull StringPool stringPool, @Nonnull TypePool typePool, + @Nonnull ProtoPool protoPool) { + this.stringPool = stringPool; + this.typePool = typePool; + this.protoPool = protoPool; + } + + public void intern(@Nonnull MethodReference method) { + Integer prev = internedItems.put(method, 0); + if (prev == null) { + typePool.intern(method.getDefiningClass()); + protoPool.intern(method); + stringPool.intern(method.getName()); + } + } + + @Nonnull @Override public CharSequence getDefiningClass(@Nonnull MethodReference methodReference) { + return methodReference.getDefiningClass(); + } + + @Nonnull @Override public ProtoPool.Key getPrototype(@Nonnull MethodReference methodReference) { + return new ProtoPool.Key(methodReference); + } + + @Nonnull @Override public CharSequence getName(@Nonnull MethodReference methodReference) { + return methodReference.getName(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolClassDef.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolClassDef.java new file mode 100644 index 00000000..01ba9146 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolClassDef.java @@ -0,0 +1,134 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import com.google.common.collect.*; +import org.jf.dexlib2.base.reference.BaseTypeReference; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.Field; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; + +class PoolClassDef extends BaseTypeReference implements ClassDef { + @Nonnull final ClassDef classDef; + @Nonnull final TypeListPool.Key> interfaces; + @Nonnull final ImmutableSortedSet staticFields; + @Nonnull final ImmutableSortedSet instanceFields; + @Nonnull final ImmutableSortedSet directMethods; + @Nonnull final ImmutableSortedSet virtualMethods; + + int classDefOffset = DexPool.NO_OFFSET; + int annotationDirectoryOffset = DexPool.NO_OFFSET; + + PoolClassDef(@Nonnull ClassDef classDef) { + this.classDef = classDef; + + interfaces = new TypeListPool.Key>(ImmutableSortedSet.copyOf(classDef.getInterfaces())); + staticFields = ImmutableSortedSet.copyOf(classDef.getStaticFields()); + instanceFields = ImmutableSortedSet.copyOf(classDef.getInstanceFields()); + directMethods = ImmutableSortedSet.copyOf( + Iterables.transform(classDef.getDirectMethods(), PoolMethod.TRANSFORM)); + virtualMethods = ImmutableSortedSet.copyOf( + Iterables.transform(classDef.getVirtualMethods(), PoolMethod.TRANSFORM)); + } + + @Nonnull @Override public String getType() { + return classDef.getType(); + } + + @Override public int getAccessFlags() { + return classDef.getAccessFlags(); + } + + @Nullable @Override public String getSuperclass() { + return classDef.getSuperclass(); + } + + @Nonnull @Override public SortedSet getInterfaces() { + return interfaces.types; + } + + @Nullable @Override public String getSourceFile() { + return classDef.getSourceFile(); + } + + @Nonnull @Override public Set getAnnotations() { + return classDef.getAnnotations(); + } + + @Nonnull @Override public SortedSet getStaticFields() { + return staticFields; + } + + @Nonnull @Override public SortedSet getInstanceFields() { + return instanceFields; + } + + @Nonnull @Override public Collection getFields() { + return new AbstractCollection() { + @Nonnull @Override public Iterator iterator() { + return Iterators.mergeSorted( + ImmutableList.of(staticFields.iterator(), instanceFields.iterator()), + Ordering.natural()); + } + + @Override public int size() { + return staticFields.size() + instanceFields.size(); + } + }; + } + + @Nonnull @Override public SortedSet getDirectMethods() { + return directMethods; + } + + @Nonnull @Override public SortedSet getVirtualMethods() { + return virtualMethods; + } + + @Nonnull @Override public Iterable getMethods() { + return new AbstractCollection() { + @Nonnull @Override public Iterator iterator() { + return Iterators.mergeSorted( + ImmutableList.of(directMethods.iterator(), virtualMethods.iterator()), + Ordering.natural()); + } + + @Override public int size() { + return directMethods.size() + virtualMethods.size(); + } + }; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolMethod.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolMethod.java new file mode 100644 index 00000000..a924f8e4 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolMethod.java @@ -0,0 +1,93 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import com.google.common.base.Function; +import org.jf.dexlib2.base.reference.BaseMethodReference; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.MethodImplementation; +import org.jf.dexlib2.iface.MethodParameter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; +import java.util.Set; + + +class PoolMethod extends BaseMethodReference implements Method { + @Nonnull private final Method method; + protected int codeItemOffset = DexPool.NO_OFFSET; + protected int debugInfoOffset = DexPool.NO_OFFSET; + + public static final Function TRANSFORM = new Function() { + @Override public PoolMethod apply(Method method) { + return new PoolMethod(method); + } + }; + + PoolMethod(@Nonnull Method method) { + this.method = method; + } + + @Override @Nonnull public String getDefiningClass() { + return method.getDefiningClass(); + } + + @Override @Nonnull public String getName() { + return method.getName(); + } + + @Override @Nonnull public List getParameterTypes() { + return method.getParameterTypes(); + } + + @Override @Nonnull public List getParameters() { + return method.getParameters(); + } + + @Override @Nonnull public String getReturnType() { + return method.getReturnType(); + } + + @Override public int getAccessFlags() { + return method.getAccessFlags(); + } + + @Override @Nonnull public Set getAnnotations() { + return method.getAnnotations(); + } + + @Override @Nullable public MethodImplementation getImplementation() { + return method.getImplementation(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/ProtoPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ProtoPool.java similarity index 57% rename from dexlib2/src/main/java/org/jf/dexlib2/writer/ProtoPool.java rename to dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ProtoPool.java index b0b93ae8..bb7c265f 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/ProtoPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ProtoPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2012, Google Inc. + * Copyright 2013, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,87 +29,74 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.jf.dexlib2.writer; +package org.jf.dexlib2.writer.pool; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.common.collect.Ordering; import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.writer.pool.ProtoPool.Key; +import org.jf.dexlib2.writer.ProtoSection; import org.jf.util.CharSequenceUtils; import org.jf.util.CollectionUtils; -import org.jf.util.ExceptionWithContext; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.io.IOException; import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.Map; -public class ProtoPool { - public final static int PROTO_ID_ITEM_SIZE = 0x0C; +public class ProtoPool extends BaseIndexPool + implements ProtoSection>> { + @Nonnull private final StringPool stringPool; + @Nonnull private final TypePool typePool; + @Nonnull private final TypeListPool typeListPool; - @Nonnull private final Map internedProtoIdItems = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; - - public ProtoPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; + public ProtoPool(@Nonnull StringPool stringPool, @Nonnull TypePool typePool, + @Nonnull TypeListPool typeListPool) { + this.stringPool = stringPool; + this.typePool = typePool; + this.typeListPool = typeListPool; } public void intern(@Nonnull MethodReference method) { // We can't use method directly, because it is likely a full MethodReference. We use a wrapper that computes // hashCode and equals based only on the prototype fields Key key = new Key(method); - Integer prev = internedProtoIdItems.put(key, 0); + Integer prev = internedItems.put(key, 0); if (prev == null) { - dexFile.stringPool.intern(key.getShorty()); - dexFile.typePool.intern(method.getReturnType()); - dexFile.typeListPool.intern(method.getParameterTypes()); + stringPool.intern(key.getShorty()); + typePool.intern(method.getReturnType()); + typeListPool.intern(method.getParameterTypes()); } } - public int getIndex(@Nonnull MethodReference method) { - Key key = new Key(method); - Integer index = internedProtoIdItems.get(key); - if (index == null) { - throw new ExceptionWithContext("Prototype not found.: %s", key); + @Nonnull @Override public CharSequence getShorty(@Nonnull Key key) { + return key.getShorty(); + } + + @Nonnull @Override public CharSequence getReturnType(@Nonnull Key key) { + return key.getReturnType(); + } + + @Nullable @Override public TypeListPool.Key> getParameters(@Nonnull Key key) { + return new TypeListPool.Key>(key.getParameters()); + } + + private static char getShortyType(CharSequence type) { + if (type.length() > 1) { + return 'L'; } - return index; + return type.charAt(0); } - public int getIndexedSectionSize() { - return internedProtoIdItems.size() * PROTO_ID_ITEM_SIZE; - } - - public int getNumItems() { - return internedProtoIdItems.size(); - } - - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); + private static String getShorty(Collection params, CharSequence returnType) { + StringBuilder sb = new StringBuilder(params.size() + 1); + sb.append(getShortyType(returnType)); + for (CharSequence typeRef: params) { + sb.append(getShortyType(typeRef)); } - return sectionOffset; + return sb.toString(); } - public void write(@Nonnull DexWriter writer) throws IOException { - List prototypes = Lists.newArrayList(internedProtoIdItems.keySet()); - Collections.sort(prototypes); - - sectionOffset = writer.getPosition(); - int index = 0; - for (Key proto: prototypes) { - internedProtoIdItems.put(proto, index++); - - writer.writeInt(dexFile.stringPool.getIndex(proto.getShorty())); - writer.writeInt(dexFile.typePool.getIndex(proto.getReturnType())); - writer.writeInt(dexFile.typeListPool.getOffset(proto.getParameters())); - } - } - - private static class Key implements Comparable { + public static class Key implements Comparable { @Nonnull private final MethodReference method; public Key(@Nonnull MethodReference method) { @@ -122,20 +109,7 @@ public class ProtoPool { } public String getShorty() { - Collection params = getParameters(); - StringBuilder sb = new StringBuilder(params.size() + 1); - sb.append(getShortyType(method.getReturnType())); - for (CharSequence typeRef: params) { - sb.append(getShortyType(typeRef)); - } - return sb.toString(); - } - - private static char getShortyType(CharSequence type) { - if (type.length() > 1) { - return 'L'; - } - return type.charAt(0); + return ProtoPool.getShorty(method.getParameterTypes(), method.getReturnType()); } public String toString() { @@ -160,7 +134,7 @@ public class ProtoPool { if (o instanceof Key) { Key other = (Key)o; return getReturnType().equals(other.getReturnType()) && - CharSequenceUtils.listEquals(getParameters(), other.getParameters()); + CharSequenceUtils.listEquals(getParameters(), other.getParameters()); } return false; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/StringPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/StringPool.java new file mode 100644 index 00000000..e071582e --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/StringPool.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import org.jf.dexlib2.iface.reference.StringReference; +import org.jf.dexlib2.writer.StringSection; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class StringPool extends StringTypeBasePool implements StringSection { + public void intern(@Nonnull CharSequence string) { + internedItems.put(string.toString(), 0); + } + + public void internNullable(@Nullable CharSequence string) { + if (string != null) { + intern(string); + } + } + + @Override public int getItemIndex(@Nonnull StringReference key) { + return getItemIndex(key.getString()); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/StringTypeBasePool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/StringTypeBasePool.java new file mode 100644 index 00000000..768e562d --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/StringTypeBasePool.java @@ -0,0 +1,65 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import com.google.common.collect.Maps; +import org.jf.dexlib2.writer.DexWriter; +import org.jf.dexlib2.writer.NullableIndexSection; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Map; + +public abstract class StringTypeBasePool implements NullableIndexSection { + @Nonnull protected final Map internedItems = Maps.newHashMap(); + + @Nonnull @Override public Collection> getItems() { + return internedItems.entrySet(); + } + + @Override public int getItemIndex(@Nonnull CharSequence key) { + Integer index = internedItems.get(key.toString()); + if (index == null) { + throw new ExceptionWithContext("Item not found.: %s", key.toString()); + } + return index; + } + + @Override public int getNullableItemIndex(@Nullable CharSequence key) { + if (key == null) { + return DexWriter.NO_INDEX; + } + return getItemIndex(key); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeListPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/TypeListPool.java similarity index 60% rename from dexlib2/src/main/java/org/jf/dexlib2/writer/TypeListPool.java rename to dexlib2/src/main/java/org/jf/dexlib2/writer/pool/TypeListPool.java index 33c72995..50ffcb81 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeListPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/TypeListPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2012, Google Inc. + * Copyright 2013, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,76 +29,47 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.jf.dexlib2.writer; +package org.jf.dexlib2.writer.pool; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.jf.util.ExceptionWithContext; +import com.google.common.collect.ImmutableList; +import org.jf.dexlib2.writer.pool.TypeListPool.Key; +import org.jf.dexlib2.writer.TypeListSection; import javax.annotation.Nonnull; -import java.io.IOException; -import java.util.*; +import java.util.Collection; +import java.util.Iterator; -public class TypeListPool { - @Nonnull private final Map internedTypeListItems = Maps.newHashMap(); - @Nonnull private final DexFile dexFile; - private int sectionOffset = -1; +public class TypeListPool extends BaseNullableOffsetPool>> + implements TypeListSection>> { + @Nonnull private final TypePool typePool; - public TypeListPool(@Nonnull DexFile dexFile) { - this.dexFile = dexFile; + public TypeListPool(@Nonnull TypePool typePool) { + this.typePool = typePool; } public void intern(@Nonnull Collection types) { - Key key = new Key(types); - Integer prev = internedTypeListItems.put(key, 0); + Key> key = new Key>(types); + Integer prev = internedItems.put(key, 0); if (prev == null) { for (CharSequence type: types) { - dexFile.typePool.intern(type); + typePool.intern(type); } } } - public int getOffset(@Nonnull Collection types) { - Key key = new Key(types); - Integer offset = internedTypeListItems.get(key); - if (offset == null) { - throw new ExceptionWithContext("Type list not found.: %s", key); + @Nonnull @Override + public Collection getTypes(Key> typesKey) { + if (typesKey == null) { + return ImmutableList.of(); } - return offset; + return typesKey.types; } - public int getNumItems() { - return internedTypeListItems.size(); - } + public static class Key> + implements Comparable>> { + @Nonnull TypeCollection types; - public int getSectionOffset() { - if (sectionOffset < 0) { - throw new ExceptionWithContext("Section offset has not been set yet!"); - } - return sectionOffset; - } - - public void write(@Nonnull DexWriter writer) throws IOException { - List typeLists = Lists.newArrayList(internedTypeListItems.keySet()); - Collections.sort(typeLists); - - writer.align(); - sectionOffset = writer.getPosition(); - for (Key typeList: typeLists) { - writer.align(); - internedTypeListItems.put(typeList, writer.getPosition()); - Collection types = typeList.getTypes(); - writer.writeInt(types.size()); - for (CharSequence type: types) { - writer.writeUshort(dexFile.typePool.getIndex(type)); - } - } - } - - public static class Key implements Comparable { - @Nonnull private Collection types; - - public Key(@Nonnull Collection types) { + public Key(@Nonnull TypeCollection types) { this.types = types; } @@ -114,7 +85,8 @@ public class TypeListPool { @Override public boolean equals(Object o) { if (o instanceof Key) { - Key other = (Key)o; + Key> other = + (Key>)o; if (types.size() != other.types.size()) { return false; } @@ -138,13 +110,8 @@ public class TypeListPool { return sb.toString(); } - @Nonnull - public Collection getTypes() { - return types; - } - @Override - public int compareTo(Key o) { + public int compareTo(Key> o) { Iterator other = o.types.iterator(); for (CharSequence type: types) { if (!other.hasNext()) { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/TypePool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/TypePool.java new file mode 100644 index 00000000..13bcd8ad --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/TypePool.java @@ -0,0 +1,69 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import org.jf.dexlib2.iface.reference.TypeReference; +import org.jf.dexlib2.writer.TypeSection; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class TypePool extends StringTypeBasePool + implements TypeSection { + @Nonnull private final StringPool stringPool; + + public TypePool(@Nonnull StringPool stringPool) { + this.stringPool = stringPool; + } + + public void intern(@Nonnull CharSequence type) { + String typeString = type.toString(); + Integer prev = internedItems.put(typeString, 0); + if (prev == null) { + stringPool.intern(typeString); + } + } + + public void internNullable(@Nullable CharSequence type) { + if (type != null) { + intern(type); + } + } + + @Override public int getItemIndex(@Nonnull TypeReference key) { + return getItemIndex(key.getType()); + } + + @Nonnull @Override public CharSequence getString(@Nonnull CharSequence type) { + return type; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/util/InstructionWriteUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/util/InstructionWriteUtil.java index 3323b4d3..7d262e0d 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/util/InstructionWriteUtil.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/util/InstructionWriteUtil.java @@ -35,7 +35,6 @@ import com.google.common.collect.Lists; import org.jf.dexlib2.Format; import org.jf.dexlib2.Opcode; import org.jf.dexlib2.ReferenceType; -import org.jf.dexlib2.iface.MethodImplementation; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.ReferenceInstruction; import org.jf.dexlib2.iface.instruction.SwitchElement; @@ -45,7 +44,7 @@ import org.jf.dexlib2.iface.reference.*; import org.jf.dexlib2.immutable.instruction.*; import org.jf.dexlib2.util.InstructionUtil; import org.jf.dexlib2.util.MethodUtil; -import org.jf.dexlib2.writer.StringPool; +import org.jf.dexlib2.writer.InstructionFactory; import org.jf.util.ExceptionWithContext; import javax.annotation.Nonnull; @@ -53,28 +52,35 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -public class InstructionWriteUtil { - private final StringPool stringPool; - MethodImplementation methodImplementation; +public class InstructionWriteUtil { + private final StringIndexProvider stringIndexProvider; + private final InstructionFactory instructionFactory; + private final Iterable originalInstructions; - private List instructions; + private List instructions; private ArrayList codeOffsetShifts; private HashMap offsetToNewInstructionMap; private int codeUnitCount; private int outParamCount; - public InstructionWriteUtil(@Nonnull MethodImplementation methodImpl, @Nonnull StringPool stringPool) { - this.stringPool = stringPool; - methodImplementation = methodImpl; - + public static interface StringIndexProvider { + int getItemIndex(@Nonnull StringRef reference); + } + + public InstructionWriteUtil(@Nonnull Iterable instructions, + @Nonnull StringIndexProvider stringIndexProvider, + @Nonnull InstructionFactory instructionFactory) { + this.stringIndexProvider = stringIndexProvider; + this.instructionFactory = instructionFactory; + this.originalInstructions = instructions; calculateMaxOutParamCount(); findCodeOffsetShifts(); modifyInstructions(); } private void calculateMaxOutParamCount() { - for (Instruction instruction: methodImplementation.getInstructions()) { + for (Insn instruction: originalInstructions) { codeUnitCount += instruction.getCodeUnits(); if (instruction.getOpcode().referenceType == ReferenceType.METHOD) { ReferenceInstruction refInsn = (ReferenceInstruction)instruction; @@ -86,12 +92,11 @@ public class InstructionWriteUtil { } } } - - public Iterable getInstructions() { + public Iterable getInstructions() { if (instructions != null) { return instructions; } else { - return methodImplementation.getInstructions(); + return originalInstructions; } } @@ -142,16 +147,17 @@ public class InstructionWriteUtil { private void findCodeOffsetShifts() { // first, process const-string to const-string/jumbo conversions int currentCodeOffset = 0; - for (Instruction instruction: methodImplementation.getInstructions()) { + for (Instruction instruction: originalInstructions) { if (instruction.getOpcode().equals(Opcode.CONST_STRING)) { ReferenceInstruction refInstr = (ReferenceInstruction) instruction; - int referenceIndex = stringPool.getIndex((StringReference)refInstr.getReference()); + // TODO: add the necessary generic plumbing to the Instruction interface to make this work without a warning (ugh) + int referenceIndex = stringIndexProvider.getItemIndex((StringRef)refInstr.getReference()); if (referenceIndex > 0xFFFF) { if (codeOffsetShifts == null) { codeOffsetShifts = new ArrayList(); } if (offsetToNewInstructionMap == null) { - offsetToNewInstructionMap = new HashMap(); + offsetToNewInstructionMap = new HashMap(); } codeOffsetShifts.add(currentCodeOffset+instruction.getCodeUnits()); offsetToNewInstructionMap.put(currentCodeOffset, Opcode.CONST_STRING_JUMBO.format); @@ -172,16 +178,16 @@ public class InstructionWriteUtil { do { currentCodeOffset = 0; shiftsInserted = false; - for (Instruction instruction: methodImplementation.getInstructions()) { + for (Instruction instruction: originalInstructions) { if (instruction.getOpcode().format.equals(Format.Format10t) && !offsetToNewInstructionMap.containsKey(currentCodeOffset)) { int targetOffset = ((Instruction10t)instruction).getCodeOffset(); int codeOffsetDelta = codeOffsetShift(currentCodeOffset); int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset); if ((byte)newTargetOffset != newTargetOffset) { if ((short)newTargetOffset != newTargetOffset) { - // handling very small (negligible) possibility of goto becoming goto/32 - // we insert extra 1 code unit shift referring to the same position - // this will cause subsequent code offsets to be shifted by 2 code units + // handling very small (negligible) possibility of goto becoming goto/32 + // we insert extra 1 code unit shift referring to the same position + // this will cause subsequent code offsets to be shifted by 2 code units codeOffsetShifts.add(codeOffsetDelta, currentCodeOffset+instruction.getCodeUnits()); offsetToNewInstructionMap.put(currentCodeOffset, Format.Format30t); } else { @@ -228,8 +234,8 @@ public class InstructionWriteUtil { instructions = Lists.newArrayList(); int currentCodeOffset = 0; - for (Instruction instruction: methodImplementation.getInstructions()) { - Instruction modifiedInstruction = null; + for (Insn instruction: originalInstructions) { + Insn modifiedInstruction = null; switch (instruction.getOpcode().format) { case Format10t: { Instruction10t instr = (Instruction10t)instruction; @@ -237,13 +243,13 @@ public class InstructionWriteUtil { int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset); Format newInstructionFormat = offsetToNewInstructionMap.get(currentCodeOffset); if (newInstructionFormat != null) { - if (newInstructionFormat.equals(Format.Format30t)) { - modifiedInstruction = new ImmutableInstruction30t(Opcode.GOTO_32, newTargetOffset); + if (newInstructionFormat.equals(Format.Format30t)) { + modifiedInstruction = instructionFactory.makeInstruction30t(Opcode.GOTO_32, newTargetOffset); } else if (newInstructionFormat.equals(Format.Format20t)) { - modifiedInstruction = new ImmutableInstruction20t(Opcode.GOTO_16, newTargetOffset); + modifiedInstruction = instructionFactory.makeInstruction20t(Opcode.GOTO_16, newTargetOffset); } } else if (newTargetOffset != targetOffset) { - modifiedInstruction = new ImmutableInstruction10t(instr.getOpcode(), newTargetOffset); + modifiedInstruction = instructionFactory.makeInstruction10t(instr.getOpcode(), newTargetOffset); } break; } @@ -253,18 +259,19 @@ public class InstructionWriteUtil { int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset); Format newInstructionFormat = offsetToNewInstructionMap.get(currentCodeOffset); if (newInstructionFormat != null && newInstructionFormat.equals(Format.Format30t)) { - modifiedInstruction = new ImmutableInstruction30t(Opcode.GOTO_32, newTargetOffset); + modifiedInstruction = instructionFactory.makeInstruction30t(Opcode.GOTO_32, newTargetOffset); } else if (newTargetOffset != targetOffset) { - modifiedInstruction = new ImmutableInstruction20t(Opcode.GOTO_16, newTargetOffset); + modifiedInstruction = instructionFactory.makeInstruction20t(Opcode.GOTO_16, newTargetOffset); } break; } case Format21c: { Instruction21c instr = (Instruction21c)instruction; if (instr.getOpcode().equals(Opcode.CONST_STRING)) { - int referenceIndex = stringPool.getIndex((StringReference)instr.getReference()); + int referenceIndex = stringIndexProvider.getItemIndex((StringRef)instr.getReference()); if (referenceIndex > 0xFFFF) { - modifiedInstruction = new ImmutableInstruction31c(Opcode.CONST_STRING_JUMBO, instr.getRegisterA(), instr.getReference()); + modifiedInstruction = instructionFactory.makeInstruction31c(Opcode.CONST_STRING_JUMBO, + instr.getRegisterA(), instr.getReference()); } } break; @@ -274,7 +281,8 @@ public class InstructionWriteUtil { int targetOffset = instr.getCodeOffset(); int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset); if (newTargetOffset != targetOffset) { - modifiedInstruction = new ImmutableInstruction21t(instr.getOpcode(), instr.getRegisterA(), newTargetOffset); + modifiedInstruction = instructionFactory.makeInstruction21t(instr.getOpcode(), + instr.getRegisterA(), newTargetOffset); } break; } @@ -283,7 +291,8 @@ public class InstructionWriteUtil { int targetOffset = instr.getCodeOffset(); int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset); if (newTargetOffset != targetOffset) { - modifiedInstruction = new ImmutableInstruction22t(instr.getOpcode(), instr.getRegisterA(), instr.getRegisterB(), newTargetOffset); + modifiedInstruction = instructionFactory.makeInstruction22t(instr.getOpcode(), + instr.getRegisterA(), instr.getRegisterB(), newTargetOffset); } break; } @@ -292,7 +301,7 @@ public class InstructionWriteUtil { int targetOffset = instr.getCodeOffset(); int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset); if (newTargetOffset != targetOffset) { - modifiedInstruction = new ImmutableInstruction30t(instr.getOpcode(), newTargetOffset); + modifiedInstruction = instructionFactory.makeInstruction30t(instr.getOpcode(), newTargetOffset); } break; } @@ -301,40 +310,41 @@ public class InstructionWriteUtil { int targetOffset = instr.getCodeOffset(); int newTargetOffset = targetOffset + targetOffsetShift(currentCodeOffset, targetOffset); if (newTargetOffset != targetOffset) { - modifiedInstruction = new ImmutableInstruction31t(instr.getOpcode(), instr.getRegisterA(), newTargetOffset); + modifiedInstruction = instructionFactory.makeInstruction31t(instr.getOpcode(), + instr.getRegisterA(), newTargetOffset); } break; } case SparseSwitchPayload: { - alignPayload(currentCodeOffset); - int switchInstructionOffset = findSwitchInstructionOffset(currentCodeOffset); + alignPayload(currentCodeOffset); + int switchInstructionOffset = findSwitchInstructionOffset(currentCodeOffset); SwitchPayload payload = (SwitchPayload)instruction; if (isSwitchTargetOffsetChanged(payload, switchInstructionOffset)) { - List newSwitchElements = modifySwitchElements(payload, switchInstructionOffset); - modifiedInstruction = new ImmutableSparseSwitchPayload(newSwitchElements); + List newSwitchElements = modifySwitchElements(payload, switchInstructionOffset); + modifiedInstruction = instructionFactory.makeSparseSwitchPayload(newSwitchElements); } break; } case PackedSwitchPayload: { - alignPayload(currentCodeOffset); - int switchInstructionOffset = findSwitchInstructionOffset(currentCodeOffset); + alignPayload(currentCodeOffset); + int switchInstructionOffset = findSwitchInstructionOffset(currentCodeOffset); SwitchPayload payload = (SwitchPayload)instruction; if (isSwitchTargetOffsetChanged(payload, switchInstructionOffset)) { List newSwitchElements = modifySwitchElements(payload, switchInstructionOffset); - modifiedInstruction = new ImmutablePackedSwitchPayload(newSwitchElements); + modifiedInstruction = instructionFactory.makePackedSwitchPayload(newSwitchElements); } break; } case ArrayPayload: { - alignPayload(currentCodeOffset); + alignPayload(currentCodeOffset); break; } } if (modifiedInstruction != null) { - instructions.add(modifiedInstruction); + instructions.add(modifiedInstruction); } else { - instructions.add(instruction); + instructions.add(instruction); } currentCodeOffset += instruction.getCodeUnits(); @@ -342,25 +352,25 @@ public class InstructionWriteUtil { } private void alignPayload(int codeOffset) { - Format newInstructionFormat = offsetToNewInstructionMap.get(codeOffset); - if (newInstructionFormat != null && newInstructionFormat.equals(Format.Format10x)) { - instructions.add(new ImmutableInstruction10x(Opcode.NOP)); - } + Format newInstructionFormat = offsetToNewInstructionMap.get(codeOffset); + if (newInstructionFormat != null && newInstructionFormat.equals(Format.Format10x)) { + instructions.add(instructionFactory.makeInstruction10x(Opcode.NOP)); + } } private int findSwitchInstructionOffset(int payloadOffset) { int currentCodeOffset = 0; int switchInstructionOffset = -1; - for (Instruction instruction: methodImplementation.getInstructions()) { + for (Instruction instruction: originalInstructions) { if (instruction.getOpcode().equals(Opcode.PACKED_SWITCH) || instruction.getOpcode().equals(Opcode.SPARSE_SWITCH)) { int targetOffset = currentCodeOffset + ((Instruction31t)instruction).getCodeOffset(); if (targetOffset == payloadOffset) { - if (switchInstructionOffset < 0) { - switchInstructionOffset = currentCodeOffset; - } else { - throw new ExceptionWithContext("Multiple switch instructions refer to the same switch payload!"); - } + if (switchInstructionOffset < 0) { + switchInstructionOffset = currentCodeOffset; + } else { + throw new ExceptionWithContext("Multiple switch instructions refer to the same switch payload!"); + } } } currentCodeOffset += instruction.getCodeUnits(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/util/TryListBuilder.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/util/TryListBuilder.java index a153100c..eb70371e 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/util/TryListBuilder.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/util/TryListBuilder.java @@ -35,7 +35,6 @@ import com.google.common.collect.Lists; import org.jf.dexlib2.base.BaseTryBlock; import org.jf.dexlib2.iface.ExceptionHandler; import org.jf.dexlib2.iface.TryBlock; -import org.jf.dexlib2.immutable.ImmutableExceptionHandler; import org.jf.util.ExceptionWithContext; import javax.annotation.Nonnull; @@ -44,43 +43,40 @@ import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; -public class TryListBuilder +public class TryListBuilder { - /*TODO: add logic to merge adjacent, identical try blocks, and remove superflous handlers - Also provide a "strict" mode, where the above isn't performed, which will be useful to be able to - exactly reproduce the original .dex file (for testing/verification purposes)*/ - // Linked list sentinels that don't represent an actual try block // Their values are never modified, only their links - private final MutableTryBlock listStart; - private final MutableTryBlock listEnd; + private final MutableTryBlock listStart; + private final MutableTryBlock listEnd; public TryListBuilder() { - listStart = new MutableTryBlock(0, 0); - listEnd = new MutableTryBlock(0, 0); + listStart = new MutableTryBlock(0, 0); + listEnd = new MutableTryBlock(0, 0); listStart.next = listEnd; listEnd.prev = listStart; } - public static List massageTryBlocks(List tryBlocks) { - TryListBuilder tlb = new TryListBuilder(); - for (TryBlock tryBlock: tryBlocks) { + public static List> massageTryBlocks( + List> tryBlocks) { + TryListBuilder tlb = new TryListBuilder(); + + for (TryBlock tryBlock: tryBlocks) { int startAddress = tryBlock.getStartCodeAddress(); int endAddress = startAddress + tryBlock.getCodeUnitCount(); - for (ExceptionHandler exceptionHandler: tryBlock.getExceptionHandlers()) { - tlb.addHandler(exceptionHandler.getExceptionType(), startAddress, endAddress, - exceptionHandler.getHandlerCodeAddress()); + for (EH exceptionHandler: tryBlock.getExceptionHandlers()) { + tlb.addHandler(startAddress, endAddress, exceptionHandler); } } return tlb.getTryBlocks(); } - private static class TryBounds { - @Nonnull public final MutableTryBlock start; - @Nonnull public final MutableTryBlock end; + private static class TryBounds { + @Nonnull public final MutableTryBlock start; + @Nonnull public final MutableTryBlock end; - public TryBounds(@Nonnull MutableTryBlock start, @Nonnull MutableTryBlock end) { + public TryBounds(@Nonnull MutableTryBlock start, @Nonnull MutableTryBlock end) { this.start = start; this.end = end; } @@ -100,13 +96,13 @@ public class TryListBuilder } } - private static class MutableTryBlock extends BaseTryBlock implements TryBlock { - public MutableTryBlock prev = null; - public MutableTryBlock next = null; + private static class MutableTryBlock extends BaseTryBlock { + public MutableTryBlock prev = null; + public MutableTryBlock next = null; public int startCodeAddress; public int endCodeAddress; - @Nonnull public List exceptionHandlers = Lists.newArrayList(); + @Nonnull public List exceptionHandlers = Lists.newArrayList(); public MutableTryBlock(int startCodeAddress, int endCodeAddress) { this.startCodeAddress = startCodeAddress; @@ -114,7 +110,7 @@ public class TryListBuilder } public MutableTryBlock(int startCodeAddress, int endCodeAddress, - @Nonnull List exceptionHandlers) { + @Nonnull List exceptionHandlers) { this.startCodeAddress = startCodeAddress; this.endCodeAddress = endCodeAddress; this.exceptionHandlers = Lists.newArrayList(exceptionHandlers); @@ -128,13 +124,13 @@ public class TryListBuilder return endCodeAddress - startCodeAddress; } - @Nonnull @Override public List getExceptionHandlers() { + @Nonnull @Override public List getExceptionHandlers() { return exceptionHandlers; } @Nonnull - public MutableTryBlock split(int splitAddress) { - MutableTryBlock newTryBlock = new MutableTryBlock(splitAddress, endCodeAddress, exceptionHandlers); + public MutableTryBlock split(int splitAddress) { + MutableTryBlock newTryBlock = new MutableTryBlock(splitAddress, endCodeAddress, exceptionHandlers); endCodeAddress = splitAddress; append(newTryBlock); return newTryBlock; @@ -151,21 +147,21 @@ public class TryListBuilder next.delete(); } - public void append(@Nonnull MutableTryBlock tryBlock) { + public void append(@Nonnull MutableTryBlock tryBlock) { next.prev = tryBlock; tryBlock.next = next; tryBlock.prev = this; next = tryBlock; } - public void prepend(@Nonnull MutableTryBlock tryBlock) { + public void prepend(@Nonnull MutableTryBlock tryBlock) { prev.next = tryBlock; tryBlock.prev = prev; tryBlock.next = this; prev = tryBlock; } - public void addHandler(@Nonnull ExceptionHandler handler) { + public void addHandler(@Nonnull EH handler) { for (ExceptionHandler existingHandler: exceptionHandlers) { String existingType = existingHandler.getExceptionType(); String newType = handler.getExceptionType(); @@ -192,10 +188,10 @@ public class TryListBuilder } } - private TryBounds getBoundingRanges(int startAddress, int endAddress) { - MutableTryBlock startBlock = null; + private TryBounds getBoundingRanges(int startAddress, int endAddress) { + MutableTryBlock startBlock = null; - MutableTryBlock tryBlock = listStart.next; + MutableTryBlock tryBlock = listStart.next; while (tryBlock != listEnd) { int currentStartAddress = tryBlock.startCodeAddress; int currentEndAddress = tryBlock.endCodeAddress; @@ -220,16 +216,16 @@ public class TryListBuilder //^--^ /*Oops, totally too far! The new range doesn't overlap any existing ones, so we just add it and return*/ - startBlock = new MutableTryBlock(startAddress, endAddress); + startBlock = new MutableTryBlock(startAddress, endAddress); tryBlock.prepend(startBlock); - return new TryBounds(startBlock, startBlock); + return new TryBounds(startBlock, startBlock); } else { // |-----| //^--------- /*Oops, too far! We've passed the start of the range being added, but the new range does overlap this one. We need to add a new range just before this one*/ - startBlock = new MutableTryBlock(startAddress, currentStartAddress); + startBlock = new MutableTryBlock(startAddress, currentStartAddress); tryBlock.prepend(startBlock); break; } @@ -244,9 +240,9 @@ public class TryListBuilder end before the range being added starts. In either case, we just need to add a new range at the end of the list*/ if (startBlock == null) { - startBlock = new MutableTryBlock(startAddress, endAddress); + startBlock = new MutableTryBlock(startAddress, endAddress); listEnd.prepend(startBlock); - return new TryBounds(startBlock, startBlock); + return new TryBounds(startBlock, startBlock); } tryBlock = startBlock; @@ -258,7 +254,7 @@ public class TryListBuilder //|-----| //------^ /*Bam! We hit the end right on the head... err, tail.*/ - return new TryBounds(startBlock, tryBlock); + return new TryBounds(startBlock, tryBlock); } else if (endAddress > currentStartAddress && endAddress < currentEndAddress) { //|-----| //--^ @@ -266,16 +262,16 @@ public class TryListBuilder existing range. We need to split the existing range at the end of the range being added.*/ tryBlock.split(endAddress); - return new TryBounds(startBlock, tryBlock); + return new TryBounds(startBlock, tryBlock); } else if (endAddress <= currentStartAddress) { //|-----| |-----| //-----------^ /*Oops, too far! The current range starts after the range being added ends. We need to create a new range that starts at the end of the previous range, and ends at the end of the range being added*/ - MutableTryBlock endBlock = new MutableTryBlock(tryBlock.prev.endCodeAddress, endAddress); + MutableTryBlock endBlock = new MutableTryBlock(tryBlock.prev.endCodeAddress, endAddress); tryBlock.prepend(endBlock); - return new TryBounds(startBlock, endBlock); + return new TryBounds(startBlock, endBlock); } tryBlock = tryBlock.next; } @@ -285,21 +281,19 @@ public class TryListBuilder /*The last range in the list ended before the end of the range being added. We need to add a new range that starts at the end of the last range in the list, and ends at the end of the range being added.*/ - MutableTryBlock endBlock = new MutableTryBlock(listEnd.prev.endCodeAddress, endAddress); + MutableTryBlock endBlock = new MutableTryBlock(listEnd.prev.endCodeAddress, endAddress); listEnd.prepend(endBlock); - return new TryBounds(startBlock, endBlock); + return new TryBounds(startBlock, endBlock); } - public void addHandler(String type, int startAddress, int endAddress, int handlerAddress) { - TryBounds bounds = getBoundingRanges(startAddress, endAddress); + public void addHandler(int startAddress, int endAddress, EH handler) { + TryBounds bounds = getBoundingRanges(startAddress, endAddress); - MutableTryBlock startBlock = bounds.start; - MutableTryBlock endBlock = bounds.end; - - ExceptionHandler handler = new ImmutableExceptionHandler(type, handlerAddress); + MutableTryBlock startBlock = bounds.start; + MutableTryBlock endBlock = bounds.end; int previousEnd = startAddress; - MutableTryBlock tryBlock = startBlock; + MutableTryBlock tryBlock = startBlock; /*Now we have the start and end ranges that exactly match the start and end of the range being added. We need to iterate over all the ranges from the start @@ -309,7 +303,7 @@ public class TryListBuilder { //is there a hole? If so, add a new range to fill the hole if (tryBlock.startCodeAddress > previousEnd) { - MutableTryBlock newBlock = new MutableTryBlock(previousEnd, tryBlock.startCodeAddress); + MutableTryBlock newBlock = new MutableTryBlock(previousEnd, tryBlock.startCodeAddress); tryBlock.prepend(newBlock); tryBlock = newBlock; } @@ -320,10 +314,10 @@ public class TryListBuilder } while (tryBlock.prev != endBlock); } - public List getTryBlocks() { - return Lists.newArrayList(new Iterator() { + public List> getTryBlocks() { + return Lists.newArrayList(new Iterator>() { // The next TryBlock to return. This has already been merged, if needed. - @Nullable private MutableTryBlock next; + @Nullable private MutableTryBlock next; { next = listStart; @@ -334,9 +328,9 @@ public class TryListBuilder * Read the item that comes after the current value of the next field. * @return The next item, or null if there is no next item */ - @Nullable protected MutableTryBlock readNextItem() { - // We can assume that next is not null - MutableTryBlock ret = next.next; + @Nullable protected MutableTryBlock readNextItem() { + // We can assume that next is not null, due to the way iteration happens + MutableTryBlock ret = next.next; if (ret == listEnd) { return null; @@ -357,12 +351,13 @@ public class TryListBuilder return next != null; } - @Override public TryBlock next() { + @Override @Nonnull public TryBlock next() { if (!hasNext()) { throw new NoSuchElementException(); } - TryBlock ret = next; + TryBlock ret = next; next = readNextItem(); + // ret can't be null (ret=next and hasNext returned true) return ret; } diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/DexDataWriterTest.java similarity index 99% rename from dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java rename to dexlib2/src/test/java/org/jf/dexlib2/writer/DexDataWriterTest.java index 967a2243..fee32d89 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/DexDataWriterTest.java @@ -42,11 +42,11 @@ import java.io.IOException; import java.util.Arrays; import java.util.Random; -public class DexWriterTest { +public class DexDataWriterTest { private Random random; private NakedByteArrayOutputStream output = new NakedByteArrayOutputStream(); private int startPosition; - private DexWriter writer; + private DexDataWriter writer; @Before public void setup() throws IOException { @@ -55,7 +55,7 @@ public class DexWriterTest { output.reset(); startPosition = 123; int bufferSize = 256; - writer = new DexWriter(output, startPosition, bufferSize); + writer = new DexDataWriter(output, startPosition, bufferSize); } // Note: we use int[] rather than byte[] so that we don't have to cast every value when manually constructing an @@ -500,7 +500,7 @@ public class DexWriterTest { public void testAlign() throws IOException { // create a new writer so we can start at file position 0 startPosition = 0; - writer = new DexWriter(output, startPosition, 256); + writer = new DexDataWriter(output, startPosition, 256); writer.align(); writer.write(1); diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterSleb128Test.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterSleb128Test.java index 5d0e8841..b41cf461 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterSleb128Test.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterSleb128Test.java @@ -40,13 +40,13 @@ import java.io.IOException; public class DexWriterSleb128Test { private NakedByteArrayOutputStream output = new NakedByteArrayOutputStream(); private int startPosition; - private DexWriter writer; + private DexDataWriter writer; public void setup() throws IOException { output.reset(); startPosition = 123; int bufferSize = 256; - writer = new DexWriter(output, startPosition, bufferSize); + writer = new DexDataWriter(output, startPosition, bufferSize); } @Test diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterUleb128Test.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterUleb128Test.java index 2b88e333..bf993bcd 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterUleb128Test.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterUleb128Test.java @@ -40,13 +40,13 @@ import java.io.IOException; public class DexWriterUleb128Test { private NakedByteArrayOutputStream output = new NakedByteArrayOutputStream(); private int startPosition; - private DexWriter writer; + private DexDataWriter writer; public void setup() throws IOException { output.reset(); startPosition = 123; int bufferSize = 256; - writer = new DexWriter(output, startPosition, bufferSize); + writer = new DexDataWriter(output, startPosition, bufferSize); } @Test diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java index 5e0cf8f7..35d845a7 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java @@ -34,9 +34,11 @@ package org.jf.dexlib2.writer; import com.google.common.collect.Lists; import junit.framework.Assert; import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.MethodImplementation; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.SwitchElement; import org.jf.dexlib2.iface.instruction.formats.*; +import org.jf.dexlib2.iface.reference.StringReference; import org.jf.dexlib2.immutable.ImmutableMethodImplementation; import org.jf.dexlib2.immutable.instruction.*; import org.jf.dexlib2.immutable.reference.ImmutableStringReference; @@ -44,17 +46,24 @@ import org.jf.dexlib2.writer.util.InstructionWriteUtil; import org.junit.Before; import org.junit.Test; +import javax.annotation.Nonnull; import java.util.ArrayList; public class JumboStringConversionTest { private static final int MIN_NUM_JUMBO_STRINGS = 2; - private MockStringPool mStringPool; + private MockStringIndexProvider mockStringIndexProvider; ArrayList mJumboStrings; + private class InsnWriteUtil extends InstructionWriteUtil { + public InsnWriteUtil(@Nonnull MethodImplementation implementation) { + super(implementation.getInstructions(), mockStringIndexProvider, ImmutableInstructionFactory.INSTANCE); + } + } + @Before public void setup() { - mStringPool = new MockStringPool(); + mockStringIndexProvider = new MockStringIndexProvider(); StringBuilder stringBuilder = new StringBuilder("a"); mJumboStrings = Lists.newArrayList(); int index = 0; @@ -64,8 +73,8 @@ public class JumboStringConversionTest { for (int pos=stringBuilder.length()-1;pos>=0;pos--) { for (char ch='a';ch<='z';ch++) { stringBuilder.setCharAt(pos, ch); - mStringPool.intern(stringBuilder.toString(), index++); - if (mStringPool.getNumItems()>0xFFFF) { + mockStringIndexProvider.intern(stringBuilder.toString(), index++); + if (mockStringIndexProvider.getNumItems()>0xFFFF) { mJumboStrings.add(stringBuilder.toString()); } } @@ -81,13 +90,16 @@ public class JumboStringConversionTest { @Test public void testInstruction21c() { ArrayList instructions = Lists.newArrayList(); - instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(mJumboStrings.get(0)))); + instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0, + new ImmutableStringReference(mJumboStrings.get(0)))); - ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + ImmutableMethodImplementation methodImplementation = + new ImmutableMethodImplementation(1, instructions, null, null); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); for (Instruction instr: writeUtil.getInstructions()) { - Assert.assertEquals("Jumbo string conversion was not performed!", instr.getOpcode(), Opcode.CONST_STRING_JUMBO); + Assert.assertEquals("Jumbo string conversion was not performed!", + instr.getOpcode(), Opcode.CONST_STRING_JUMBO); } } @@ -111,7 +123,7 @@ public class JumboStringConversionTest { instructions.add(1, new ImmutableInstruction10t(Opcode.GOTO, 3)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); for (Instruction instr: writeUtil.getInstructions()) { if (instr instanceof Instruction10t) { @@ -128,7 +140,7 @@ public class JumboStringConversionTest { instructions.add(1, new ImmutableInstruction20t(Opcode.GOTO_16, 4)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); for (Instruction instr: writeUtil.getInstructions()) { if (instr instanceof Instruction20t) { @@ -145,7 +157,7 @@ public class JumboStringConversionTest { instructions.add(1, new ImmutableInstruction30t(Opcode.GOTO_32, 5)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); for (Instruction instr: writeUtil.getInstructions()) { if (instr instanceof Instruction30t) { @@ -162,7 +174,7 @@ public class JumboStringConversionTest { instructions.add(1, new ImmutableInstruction21t(Opcode.IF_EQZ, 0, 4)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); for (Instruction instr: writeUtil.getInstructions()) { if (instr instanceof Instruction21t) { @@ -179,7 +191,7 @@ public class JumboStringConversionTest { instructions.add(1, new ImmutableInstruction22t(Opcode.IF_EQ, 0, 1, 4)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); for (Instruction instr: writeUtil.getInstructions()) { if (instr instanceof Instruction22t) { @@ -196,7 +208,7 @@ public class JumboStringConversionTest { instructions.add(1, new ImmutableInstruction31t(Opcode.PACKED_SWITCH, 0, 5)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); for (Instruction instr: writeUtil.getInstructions()) { if (instr instanceof Instruction31t) { @@ -213,7 +225,7 @@ public class JumboStringConversionTest { instructions.add(1, new ImmutableInstruction31t(Opcode.PACKED_SWITCH, 0, 6)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); for (Instruction instr: writeUtil.getInstructions()) { if (instr instanceof PackedSwitchPayload) { @@ -232,7 +244,7 @@ public class JumboStringConversionTest { instructions.add(1, new ImmutableInstruction31t(Opcode.SPARSE_SWITCH, 0, 12)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); for (Instruction instr: writeUtil.getInstructions()) { if (instr instanceof SparseSwitchPayload) { @@ -253,7 +265,7 @@ public class JumboStringConversionTest { instructions.add(new ImmutableArrayPayload(4, null)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); int codeOffset = 0; for (Instruction instr: writeUtil.getInstructions()) { @@ -270,7 +282,7 @@ public class JumboStringConversionTest { // packed switch instruction is already misaligned ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); int codeOffset = 0; for (Instruction instr: writeUtil.getInstructions()) { @@ -288,7 +300,7 @@ public class JumboStringConversionTest { instructions.add(4, new ImmutableInstruction10x(Opcode.NOP)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); int codeOffset = 0; for (Instruction instr: writeUtil.getInstructions()) { @@ -309,7 +321,7 @@ public class JumboStringConversionTest { } ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); Instruction instr = writeUtil.getInstructions().iterator().next(); Assert.assertEquals("goto was not converted to goto/16 properly", instr.getOpcode(), Opcode.GOTO_16); @@ -325,7 +337,7 @@ public class JumboStringConversionTest { } ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); Instruction instr = writeUtil.getInstructions().iterator().next(); Assert.assertEquals("goto/16 was not converted to goto/32 properly", instr.getOpcode(), Opcode.GOTO_32); @@ -349,7 +361,7 @@ public class JumboStringConversionTest { instructions.add(new ImmutableArrayPayload(4, null)); ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); + InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); Instruction instr = writeUtil.getInstructions().iterator().next(); Assert.assertEquals("goto was not converted to goto/16 properly", instr.getOpcode(), Opcode.GOTO_16); diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/MockStringIndexProvider.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/MockStringIndexProvider.java new file mode 100644 index 00000000..442a1d22 --- /dev/null +++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/MockStringIndexProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import com.google.common.collect.Maps; +import org.jf.dexlib2.iface.reference.StringReference; +import org.jf.dexlib2.writer.util.InstructionWriteUtil.StringIndexProvider; + +import javax.annotation.Nonnull; +import java.util.HashMap; + +public class MockStringIndexProvider implements StringIndexProvider { + private HashMap internedItems = Maps.newHashMap(); + + public void intern(@Nonnull CharSequence string, int index) { + internedItems.put(string.toString(), index); + } + + @Override public int getItemIndex(@Nonnull StringReference reference) { + return internedItems.get(reference.getString()); + } + + public int getNumItems() { + return internedItems.size(); + } +} diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/util/TryListBuilderTest.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/util/TryListBuilderTest.java index aa5e7519..360b5840 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/writer/util/TryListBuilderTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/util/TryListBuilderTest.java @@ -33,6 +33,7 @@ package org.jf.dexlib2.writer.util; import com.google.common.collect.ImmutableList; import junit.framework.Assert; +import org.jf.dexlib2.iface.ExceptionHandler; import org.jf.dexlib2.iface.TryBlock; import org.jf.dexlib2.immutable.ImmutableExceptionHandler; import org.jf.dexlib2.immutable.ImmutableTryBlock; @@ -41,13 +42,16 @@ import org.junit.Test; import java.util.List; public class TryListBuilderTest { + private static class TryListBuilder extends org.jf.dexlib2.writer.util.TryListBuilder { + } + @Test public void testSingleCatchAll_Beginning() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler(null, 0, 10, 5); + tlb.addHandler(0, 10, new ImmutableExceptionHandler(null, 5)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of(new ImmutableTryBlock(0, 10, ImmutableList.of(new ImmutableExceptionHandler(null, 5)))); @@ -59,9 +63,9 @@ public class TryListBuilderTest { public void testSingleCatchAll_Middle() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler(null, 5, 10, 15); + tlb.addHandler(5, 10, new ImmutableExceptionHandler(null, 15)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of(new ImmutableTryBlock(5, 5, ImmutableList.of(new ImmutableExceptionHandler(null, 15)))); @@ -73,9 +77,9 @@ public class TryListBuilderTest { public void testSingleCatch_Beginning() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("Ljava/lang/Exception;", 0, 10, 5); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("Ljava/lang/Exception;", 5)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of(new ImmutableTryBlock(0, 10, ImmutableList.of(new ImmutableExceptionHandler("Ljava/lang/Exception;", 5)))); @@ -87,9 +91,9 @@ public class TryListBuilderTest { public void testSingleCatch_Middle() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("Ljava/lang/Exception;", 5, 10, 15); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("Ljava/lang/Exception;", 15)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of(new ImmutableTryBlock(5, 5, ImmutableList.of(new ImmutableExceptionHandler("Ljava/lang/Exception;", 15)))); @@ -101,10 +105,10 @@ public class TryListBuilderTest { public void testOverlap_End_After() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 0, 10, 5); - tlb.addHandler("LException2;", 10, 20, 6); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(10, 20, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 10, @@ -119,10 +123,10 @@ public class TryListBuilderTest { public void testOverlap_After_After() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 0, 10, 5); - tlb.addHandler("LException2;", 15, 20, 6); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(15, 20, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 10, @@ -137,10 +141,10 @@ public class TryListBuilderTest { public void testOverlap_Before_Start() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 5, 10, 5); - tlb.addHandler("LException2;", 0, 5, 6); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 5, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 5, @@ -155,10 +159,10 @@ public class TryListBuilderTest { public void testOverlap_Before_Before() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 5, 10, 5); - tlb.addHandler("LException2;", 0, 3, 6); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 3, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 3, @@ -173,10 +177,10 @@ public class TryListBuilderTest { public void testOverlap_Start_End() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 0, 10, 5); - tlb.addHandler("LException2;", 0, 10, 6); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 10, @@ -191,10 +195,10 @@ public class TryListBuilderTest { public void testOverlap_Start_Middle() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 0, 10, 5); - tlb.addHandler("LException2;", 0, 5, 6); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 5, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 5, @@ -212,10 +216,10 @@ public class TryListBuilderTest { public void testOverlap_Middle_Middle() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 0, 10, 5); - tlb.addHandler("LException2;", 2, 7, 6); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(2, 7, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 2, @@ -236,10 +240,10 @@ public class TryListBuilderTest { public void testOverlap_Middle_End() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 0, 10, 5); - tlb.addHandler("LException2;", 5, 10, 6); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 5, @@ -257,10 +261,10 @@ public class TryListBuilderTest { public void testOverlap_Beginning_After() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 0, 10, 5); - tlb.addHandler("LException2;", 0, 15, 6); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 15, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 10, @@ -278,10 +282,10 @@ public class TryListBuilderTest { public void testOverlap_Middle_After() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 0, 10, 5); - tlb.addHandler("LException2;", 5, 15, 6); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(5, 15, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 5, @@ -302,10 +306,10 @@ public class TryListBuilderTest { public void testOverlap_Before_End() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 5, 10, 5); - tlb.addHandler("LException2;", 0, 10, 6); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 10, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 5, @@ -323,10 +327,10 @@ public class TryListBuilderTest { public void testOverlap_Before_Middle() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 5, 10, 5); - tlb.addHandler("LException2;", 0, 7, 6); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 7, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 5, @@ -347,10 +351,10 @@ public class TryListBuilderTest { public void testOverlap_Before_After() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 5, 10, 5); - tlb.addHandler("LException2;", 0, 15, 6); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 15, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 5, @@ -371,11 +375,11 @@ public class TryListBuilderTest { public void testOverlap_Hole() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 1, 5, 5); - tlb.addHandler("LException1;", 10, 14, 5); - tlb.addHandler("LException2;", 0, 15, 6); + tlb.addHandler(1, 5, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(10, 14, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 15, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 1, @@ -403,10 +407,10 @@ public class TryListBuilderTest { public void testHandlerMerge_Same() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 5, 10, 5); - tlb.addHandler("LException1;", 0, 15, 5); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 15, new ImmutableExceptionHandler("LException1;", 5)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 15, @@ -420,10 +424,10 @@ public class TryListBuilderTest { public void testHandlerMerge_DifferentType() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 5, 10, 5); - tlb.addHandler("LException2;", 0, 15, 6); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 15, new ImmutableExceptionHandler("LException2;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 5, @@ -444,9 +448,9 @@ public class TryListBuilderTest { public void testHandlerMerge_DifferentAddress() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 5, 10, 5); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("LException1;", 5)); try { - tlb.addHandler("LException1;", 0, 15, 6); + tlb.addHandler(0, 15, new ImmutableExceptionHandler("LException1;", 6)); } catch (TryListBuilder.InvalidTryException ex) { return; } @@ -457,10 +461,10 @@ public class TryListBuilderTest { public void testHandlerMerge_Exception_Catchall() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler("LException1;", 5, 10, 5); - tlb.addHandler(null, 0, 15, 6); + tlb.addHandler(5, 10, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(0, 15, new ImmutableExceptionHandler(null, 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 5, @@ -481,10 +485,10 @@ public class TryListBuilderTest { public void testHandlerMerge_Catchall_Exception() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler(null, 5, 10, 5); - tlb.addHandler("LException1;", 0, 15, 6); + tlb.addHandler(5, 10, new ImmutableExceptionHandler(null, 5)); + tlb.addHandler(0, 15, new ImmutableExceptionHandler("LException1;", 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 5, @@ -505,10 +509,10 @@ public class TryListBuilderTest { public void testHandlerMerge_Catchall_Catchall() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler(null, 5, 10, 5); - tlb.addHandler(null, 0, 15, 5); + tlb.addHandler(5, 10, new ImmutableExceptionHandler(null, 5)); + tlb.addHandler(0, 15, new ImmutableExceptionHandler(null, 5)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 15, @@ -522,9 +526,9 @@ public class TryListBuilderTest { public void testHandlerMerge_Catchall_Catchall_DifferentAddress() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler(null, 5, 10, 5); + tlb.addHandler(5, 10, new ImmutableExceptionHandler(null, 5)); try { - tlb.addHandler(null, 0, 15, 6); + tlb.addHandler(0, 15, new ImmutableExceptionHandler(null, 6)); } catch (TryListBuilder.InvalidTryException ex) { return; } @@ -535,12 +539,12 @@ public class TryListBuilderTest { public void testHandlerMerge_MergeSame() { TryListBuilder tlb = new TryListBuilder(); - tlb.addHandler(null, 0, 15, 6); - tlb.addHandler("LException1;", 10, 20, 5); - tlb.addHandler("LException1;", 20, 30, 5); - tlb.addHandler(null, 25, 40, 6); + tlb.addHandler(0, 15, new ImmutableExceptionHandler(null, 6)); + tlb.addHandler(10, 20, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(20, 30, new ImmutableExceptionHandler("LException1;", 5)); + tlb.addHandler(25, 40, new ImmutableExceptionHandler(null, 6)); - List tryBlocks = tlb.getTryBlocks(); + List> tryBlocks = tlb.getTryBlocks(); List expected = ImmutableList.of( new ImmutableTryBlock(0, 10, diff --git a/smali/src/main/antlr3/smaliTreeWalker.g b/smali/src/main/antlr3/smaliTreeWalker.g index 330c1291..22b332bb 100644 --- a/smali/src/main/antlr3/smaliTreeWalker.g +++ b/smali/src/main/antlr3/smaliTreeWalker.g @@ -388,7 +388,7 @@ method returns[Method ret] annotations ) { - List tryBlocks = $catches.tryBlocks; + List> tryBlocks = $catches.tryBlocks; List debugItems = $ordered_debug_directives.debugItems; MethodImplementation methodImplementation = null; @@ -568,12 +568,12 @@ sparse_switch_declaration } }; -catches returns[List tryBlocks] +catches returns[List> tryBlocks] @init {tryBlocks = Lists.newArrayList();} : ^(I_CATCHES (catch_directive { tryBlocks.add($catch_directive.tryBlock); })* (catchall_directive { tryBlocks.add($catchall_directive.tryBlock); })*); -catch_directive returns[TryBlock tryBlock] +catch_directive returns[TryBlock tryBlock] : ^(I_CATCH address nonvoid_type_descriptor from=offset_or_label_absolute[$address.address] to=offset_or_label_absolute[$address.address] using=offset_or_label_absolute[$address.address]) { @@ -588,7 +588,7 @@ catch_directive returns[TryBlock tryBlock] ImmutableList.of(new ImmutableExceptionHandler(type, handlerAddress))); }; -catchall_directive returns[TryBlock tryBlock] +catchall_directive returns[TryBlock tryBlock] : ^(I_CATCHALL address from=offset_or_label_absolute[$address.address] to=offset_or_label_absolute[$address.address] using=offset_or_label_absolute[$address.address]) { diff --git a/util/src/main/java/org/jf/util/CollectionUtils.java b/util/src/main/java/org/jf/util/CollectionUtils.java index 23e23dc9..3399ef7e 100644 --- a/util/src/main/java/org/jf/util/CollectionUtils.java +++ b/util/src/main/java/org/jf/util/CollectionUtils.java @@ -79,6 +79,7 @@ public class CollectionUtils { for (T element1: it1) { T element2; try { + // TODO: would checking hasNext be more efficient? element2 = elements2.next(); } catch (NoSuchElementException ex) { return 1;