From 42627b850c8f68a594f105e04b97c512b292b698 Mon Sep 17 00:00:00 2001 From: Ben Gruver Date: Mon, 29 Apr 2013 22:35:03 -0700 Subject: [PATCH] Add new DexBuilder interface This is a different "front-end" for the existing writer functionality that is meant to be a more memory efficient method for building a new dex file from scratch --- .../ImmutableInstructionFactory.java | 2 +- .../java/org/jf/dexlib2/util/MethodUtil.java | 16 + .../org/jf/dexlib2/util/Preconditions.java | 30 ++ .../java/org/jf/dexlib2/writer/DexWriter.java | 65 ++- .../jf/dexlib2/writer/EncodedValueWriter.java | 8 +- .../org/jf/dexlib2/writer/FieldSection.java | 3 +- .../jf/dexlib2/writer/InstructionFactory.java | 14 +- .../jf/dexlib2/writer/InstructionWriter.java | 12 +- .../org/jf/dexlib2/writer/MethodSection.java | 4 +- .../org/jf/dexlib2/writer/TypeSection.java | 2 +- .../writer/builder/BuilderAnnotation.java | 64 +++ .../builder/BuilderAnnotationElement.java | 56 +++ .../writer/builder/BuilderAnnotationPool.java | 108 +++++ .../writer/builder/BuilderAnnotationSet.java | 60 +++ .../builder/BuilderAnnotationSetPool.java | 106 +++++ .../writer/builder/BuilderClassDef.java | 132 ++++++ .../writer/builder/BuilderClassPool.java | 418 +++++++++++++++++ .../writer/builder/BuilderContext.java | 175 +++++++ .../writer/builder/BuilderDebugItem.java | 168 +++++++ .../writer/builder/BuilderEncodedValues.java | 242 ++++++++++ .../builder/BuilderExceptionHandler.java | 54 +++ .../dexlib2/writer/builder/BuilderField.java | 80 ++++ .../writer/builder/BuilderFieldPool.java | 107 +++++ .../writer/builder/BuilderFieldReference.java | 72 +++ .../writer/builder/BuilderInstruction.java | 432 ++++++++++++++++++ .../builder/BuilderInstructionFactory.java | 223 +++++++++ .../builder/BuilderMapEntryCollection.java | 89 ++++ .../dexlib2/writer/builder/BuilderMethod.java | 73 +++ .../builder/BuilderMethodImplementation.java | 59 +++ .../builder/BuilderMethodParameter.java | 63 +++ .../writer/builder/BuilderMethodPool.java | 145 ++++++ .../builder/BuilderMethodReference.java | 76 +++ .../writer/builder/BuilderProtoPool.java | 145 ++++++ .../writer/builder/BuilderProtoReference.java | 86 ++++ .../writer/builder/BuilderReference.java | 39 ++ .../writer/builder/BuilderStringPool.java | 86 ++++ .../builder/BuilderStringReference.java | 58 +++ .../writer/builder/BuilderTryBlock.java | 62 +++ .../writer/builder/BuilderTypeList.java | 66 +++ .../writer/builder/BuilderTypeListPool.java | 104 +++++ .../writer/builder/BuilderTypePool.java | 95 ++++ .../writer/builder/BuilderTypeReference.java | 58 +++ .../jf/dexlib2/writer/builder/DexBuilder.java | 307 +++++++++++++ .../org/jf/dexlib2/writer/pool/DexPool.java | 9 +- .../org/jf/dexlib2/writer/pool/FieldPool.java | 7 +- .../jf/dexlib2/writer/pool/MethodPool.java | 10 +- .../org/jf/dexlib2/writer/pool/ProtoPool.java | 19 +- .../writer/util/InstructionWriteUtil.java | 9 +- .../writer/JumboStringConversionTest.java | 3 +- smali/src/main/antlr3/smaliTreeWalker.g | 240 +++++----- smali/src/main/java/org/jf/smali/main.java | 43 +- 51 files changed, 4376 insertions(+), 228 deletions(-) create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotation.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationElement.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationPool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationSet.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationSetPool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassDef.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderContext.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderDebugItem.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderEncodedValues.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderExceptionHandler.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderField.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderFieldPool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderFieldReference.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderInstruction.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderInstructionFactory.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMapEntryCollection.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethod.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodImplementation.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodParameter.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodPool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodReference.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderProtoPool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderProtoReference.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderReference.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderStringPool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderStringReference.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTryBlock.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeList.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeListPool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypePool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeReference.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java 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 index c3fd82b9..ad562019 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstructionFactory.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstructionFactory.java @@ -40,7 +40,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; -public class ImmutableInstructionFactory implements InstructionFactory { +public class ImmutableInstructionFactory implements InstructionFactory { public static final ImmutableInstructionFactory INSTANCE = new ImmutableInstructionFactory(); private ImmutableInstructionFactory() { 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 2cfa1b5c..631b8928 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java @@ -93,5 +93,21 @@ public final class MethodUtil { return regCount; } + private static char getShortyType(CharSequence type) { + if (type.length() > 1) { + return 'L'; + } + return type.charAt(0); + } + + public static String getShorty(Collection params, String returnType) { + StringBuilder sb = new StringBuilder(params.size() + 1); + sb.append(getShortyType(returnType)); + for (CharSequence typeRef: params) { + sb.append(getShortyType(typeRef)); + } + return sb.toString(); + } + private MethodUtil() {} } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/Preconditions.java b/dexlib2/src/main/java/org/jf/dexlib2/util/Preconditions.java index 917d9901..b20684d6 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/Preconditions.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/Preconditions.java @@ -33,7 +33,9 @@ package org.jf.dexlib2.util; import org.jf.dexlib2.Format; import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.ReferenceType; import org.jf.dexlib2.VerificationError; +import org.jf.dexlib2.iface.reference.*; public class Preconditions { public static void checkFormat(Opcode opcode, Format expectedFormat) { @@ -185,4 +187,32 @@ public class Preconditions { } return verificationError; } + + public static T checkReference(int referenceType, T reference) { + switch (referenceType) { + case ReferenceType.STRING: + if (!(reference instanceof StringReference)) { + throw new IllegalArgumentException("Invalid reference type, expecting a string reference"); + } + break; + case ReferenceType.TYPE: + if (!(reference instanceof TypeReference)) { + throw new IllegalArgumentException("Invalid reference type, expecting a type reference"); + } + break; + case ReferenceType.FIELD: + if (!(reference instanceof FieldReference)) { + throw new IllegalArgumentException("Invalid reference type, expecting a field reference"); + } + break; + case ReferenceType.METHOD: + if (!(reference instanceof MethodReference)) { + throw new IllegalArgumentException("Invalid reference type, expecting a method reference"); + } + break; + default: + throw new IllegalArgumentException(String.format("Not a valid reference type: %d", referenceType)); + } + return reference; + } } 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 58a25b55..63289fd9 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java @@ -43,10 +43,7 @@ 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.iface.reference.*; import org.jf.dexlib2.util.MethodUtil; import org.jf.dexlib2.writer.util.InstructionWriteUtil; import org.jf.dexlib2.writer.util.TryListBuilder; @@ -72,10 +69,11 @@ 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, + BaseReference extends Reference, ClassKey extends Comparable, AnnotationKey extends Annotation, AnnotationSetKey, TypeListKey, - FieldKey extends FieldRefKey, MethodKey extends MethodRefKey, + FieldKey, MethodKey, EncodedValue, AnnotationElement, DebugItem extends org.jf.dexlib2.iface.debug.DebugItem, Insn extends Instruction, ExceptionHandler extends org.jf.dexlib2.iface.ExceptionHandler> { @@ -108,26 +106,26 @@ public abstract class DexWriter< protected int numCodeItemItems = 0; protected int numClassDataItems = 0; - protected InstructionFactory instructionFactory; + protected final InstructionFactory instructionFactory; - protected StringSection stringSection; - protected TypeSection typeSection; - protected ProtoSection protoSection; - protected FieldSection fieldSection; - protected MethodSection methodSection; - protected ClassSection stringSection; + protected final TypeSection typeSection; + protected final ProtoSection protoSection; + protected final FieldSection fieldSection; + protected final MethodSection methodSection; + protected final ClassSection classSection; - protected TypeListSection typeListSection; - protected AnnotationSection annotationSection; - protected AnnotationSetSection annotationSetSection; + protected final TypeListSection typeListSection; + protected final AnnotationSection annotationSection; + protected final AnnotationSetSection annotationSetSection; - protected DexWriter(InstructionFactory instructionFactory, + protected DexWriter(InstructionFactory instructionFactory, StringSection stringSection, TypeSection typeSection, ProtoSection protoSection, - FieldSection fieldSection, - MethodSection methodSection, + FieldSection fieldSection, + MethodSection methodSection, ClassSection classSection, TypeListSection typeListSection, @@ -394,7 +392,7 @@ public abstract class DexWriter< ClassKey key = entry.getKey(); - // set a bogus index, to make sure we don't recuse and double-write it + // set a bogus index, to make sure we don't recurse and double-write it entry.setValue(0); // first, try to write the superclass @@ -459,7 +457,7 @@ public abstract class DexWriter< throws IOException { int prevIndex = 0; for (FieldKey key: fields) { - int index = fieldSection.getItemIndex(key); + int index = fieldSection.getFieldIndex(key); writer.writeUleb128(index-prevIndex); writer.writeUleb128(classSection.getFieldAccessFlags(key)); prevIndex = index; @@ -470,7 +468,7 @@ public abstract class DexWriter< throws IOException { int prevIndex = 0; for (MethodKey key: methods) { - int index = methodSection.getItemIndex(key); + int index = methodSection.getMethodIndex(key); writer.writeUleb128(index-prevIndex); writer.writeUleb128(classSection.getMethodAccessFlags(key)); writer.writeUleb128(classSection.getCodeItemOffset(key)); @@ -627,14 +625,11 @@ public abstract class DexWriter< // 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); + Collection fields = classSection.getSortedFields(key); + Collection methods = classSection.getSortedMethods(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; + int maxSize = fields.size() * 8 + methods.size() * 16; if (maxSize > tempBuffer.capacity()) { tempBuffer = ByteBuffer.allocate(maxSize); tempBuffer.order(ByteOrder.LITTLE_ENDIAN); @@ -646,29 +641,29 @@ public abstract class DexWriter< int methodAnnotations = 0; int parameterAnnotations = 0; - for (FieldKey field: classSection.getSortedFields(key)) { + for (FieldKey field: fields) { AnnotationSetKey fieldAnnotationsKey = classSection.getFieldAnnotations(field); if (fieldAnnotationsKey != null) { fieldAnnotations++; - tempBuffer.putInt(fieldSection.getItemIndex(field)); + tempBuffer.putInt(fieldSection.getFieldIndex(field)); tempBuffer.putInt(annotationSetSection.getItemOffset(fieldAnnotationsKey)); } } - for (MethodKey method: classSection.getSortedMethods(key)) { + for (MethodKey method: methods) { AnnotationSetKey methodAnnotationsKey = classSection.getMethodAnnotations(method); if (methodAnnotationsKey != null) { methodAnnotations++; - tempBuffer.putInt(methodSection.getItemIndex(method)); + tempBuffer.putInt(methodSection.getMethodIndex(method)); tempBuffer.putInt(annotationSetSection.getItemOffset(methodAnnotationsKey)); } } - for (MethodKey method: classSection.getSortedMethods(key)) { + for (MethodKey method: methods) { int offset = classSection.getAnnotationSetRefListOffset(method); if (offset != DexWriter.NO_OFFSET) { methodAnnotations++; - tempBuffer.putInt(methodSection.getItemIndex(method)); + tempBuffer.putInt(methodSection.getMethodIndex(method)); tempBuffer.putInt(offset); } } @@ -809,8 +804,8 @@ public abstract class DexWriter< if (instructions != null) { tryBlocks = TryListBuilder.massageTryBlocks(tryBlocks); - InstructionWriteUtil instrWriteUtil = - new InstructionWriteUtil(instructions, stringSection, instructionFactory); + InstructionWriteUtil instrWriteUtil = + new InstructionWriteUtil(instructions, stringSection, instructionFactory); writer.writeUshort(instrWriteUtil.getOutParamCount()); writer.writeUshort(tryBlocks.size()); writer.writeInt(debugItemOffset); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedValueWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedValueWriter.java index 09b7ccf9..53d77b39 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedValueWriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedValueWriter.java @@ -44,16 +44,16 @@ public abstract class EncodedValueWriter stringSection; @Nonnull private final TypeSection typeSection; - @Nonnull private final FieldSection fieldSection; - @Nonnull private final MethodSection methodSection; + @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 FieldSection fieldSection, + @Nonnull MethodSection methodSection, @Nonnull AnnotationSection annotationSection) { this.writer = writer; this.stringSection = stringSection; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/FieldSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/FieldSection.java index 626b8180..4ca51abe 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/FieldSection.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/FieldSection.java @@ -35,9 +35,10 @@ import org.jf.dexlib2.iface.reference.FieldReference; import javax.annotation.Nonnull; -public interface FieldSection +public interface FieldSection extends IndexSection { @Nonnull TypeKey getDefiningClass(@Nonnull FieldRefKey key); @Nonnull TypeKey getFieldType(@Nonnull FieldRefKey key); @Nonnull StringKey getName(@Nonnull FieldRefKey key); + int getFieldIndex(@Nonnull FieldKey key); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionFactory.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionFactory.java index 519f0a54..63a41562 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionFactory.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionFactory.java @@ -41,34 +41,34 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; -public interface InstructionFactory { +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 makeInstruction20bc(@Nonnull Opcode opcode, int verificationError, @Nonnull Ref reference); Insn makeInstruction20t(@Nonnull Opcode opcode, int codeOffset); - Insn makeInstruction21c(@Nonnull Opcode opcode, int registerA, @Nonnull Reference reference); + Insn makeInstruction21c(@Nonnull Opcode opcode, int registerA, @Nonnull Ref 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 makeInstruction22c(@Nonnull Opcode opcode, int registerA, int registerB, @Nonnull Ref 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 makeInstruction31c(@Nonnull Opcode opcode, int registerA, @Nonnull Ref 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); + int registerF, int registerG, @Nonnull Ref reference); Insn makeInstruction3rc(@Nonnull Opcode opcode, int startRegister, int registerCount, - @Nonnull Reference reference); + @Nonnull Ref reference); Insn makeInstruction51l(@Nonnull Opcode opcode, int registerA, long literal); Insn makeSparseSwitchPayload(@Nullable List switchElements); Insn makePackedSwitchPayload(@Nullable List switchElements); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java index f2e31132..17d31337 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java @@ -50,8 +50,8 @@ public class InstructionWriter stringSection; @Nonnull private final TypeSection typeSection; - @Nonnull private final FieldSection fieldSection; - @Nonnull private final MethodSection methodSection; + @Nonnull private final FieldSection fieldSection; + @Nonnull private final MethodSection methodSection; @Nonnull static InstructionWriter @@ -59,8 +59,8 @@ public class InstructionWriter stringSection, @Nonnull TypeSection typeSection, - @Nonnull FieldSection fieldSection, - @Nonnull MethodSection methodSection) { + @Nonnull FieldSection fieldSection, + @Nonnull MethodSection methodSection) { return new InstructionWriter( writer, stringSection, typeSection, fieldSection, methodSection); } @@ -68,8 +68,8 @@ public class InstructionWriter stringSection, @Nonnull TypeSection typeSection, - @Nonnull FieldSection fieldSection, - @Nonnull MethodSection methodSection) { + @Nonnull FieldSection fieldSection, + @Nonnull MethodSection methodSection) { this.writer = writer; this.stringSection = stringSection; this.typeSection = typeSection; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/MethodSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/MethodSection.java index 0ce47a6c..bf03b000 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/MethodSection.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/MethodSection.java @@ -35,9 +35,11 @@ import org.jf.dexlib2.iface.reference.MethodReference; import javax.annotation.Nonnull; -public interface MethodSection +public interface MethodSection extends IndexSection { @Nonnull TypeKey getDefiningClass(@Nonnull MethodRefKey key); @Nonnull ProtoKey getPrototype(@Nonnull MethodRefKey key); + @Nonnull ProtoKey getPrototype(@Nonnull MethodKey key); @Nonnull StringKey getName(@Nonnull MethodRefKey key); + int getMethodIndex(@Nonnull MethodKey key); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeSection.java index 62f32809..79ecd67b 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeSection.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/TypeSection.java @@ -37,5 +37,5 @@ import javax.annotation.Nonnull; public interface TypeSection extends NullableIndexSection { @Nonnull StringKey getString(@Nonnull TypeKey key); - int getItemIndex(@Nonnull TypeReference key); + int getItemIndex(@Nonnull TypeRef key); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotation.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotation.java new file mode 100644 index 00000000..3b413b00 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotation.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.builder; + +import org.jf.dexlib2.base.BaseAnnotation; +import org.jf.dexlib2.writer.DexWriter; + +import javax.annotation.Nonnull; +import java.util.Set; + +class BuilderAnnotation extends BaseAnnotation { + int visibility; + @Nonnull final BuilderTypeReference type; + @Nonnull final Set elements; + int offset = DexWriter.NO_OFFSET; + + public BuilderAnnotation(int visibility, @Nonnull BuilderTypeReference type, + @Nonnull Set elements) { + this.visibility = visibility; + this.type = type; + this.elements = elements; + } + + @Override public int getVisibility() { + return visibility; + } + + @Nonnull @Override public String getType() { + return type.getType(); + } + + @Nonnull @Override public Set getElements() { + return elements; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationElement.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationElement.java new file mode 100644 index 00000000..d758ddb2 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationElement.java @@ -0,0 +1,56 @@ +/* + * 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.builder; + +import org.jf.dexlib2.base.BaseAnnotationElement; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue; + +import javax.annotation.Nonnull; + +public class BuilderAnnotationElement extends BaseAnnotationElement { + @Nonnull final BuilderStringReference name; + @Nonnull final BuilderEncodedValue value; + + public BuilderAnnotationElement(@Nonnull BuilderStringReference name, @Nonnull BuilderEncodedValue value) { + this.name = name; + this.value = value; + } + + @Nonnull @Override public String getName() { + return name.getString(); + } + + @Nonnull @Override public EncodedValue getValue() { + return value; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationPool.java new file mode 100644 index 00000000..e16bff0c --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationPool.java @@ -0,0 +1,108 @@ +/* + * 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.builder; + +import com.google.common.collect.Maps; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.writer.AnnotationSection; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; + +class BuilderAnnotationPool implements AnnotationSection { + @Nonnull private final BuilderContext context; + @Nonnull private final ConcurrentMap internedItems = + Maps.newConcurrentMap(); + + BuilderAnnotationPool(@Nonnull BuilderContext context) { + this.context = context; + } + + @Nonnull public BuilderAnnotation internAnnotation(@Nonnull Annotation annotation) { + BuilderAnnotation ret = internedItems.get(annotation); + if (ret != null) { + return ret; + } + + BuilderAnnotation dexBuilderAnnotation = new BuilderAnnotation( + annotation.getVisibility(), + context.typePool.internType(annotation.getType()), + context.internAnnotationElements(annotation.getElements())); + ret = internedItems.putIfAbsent(dexBuilderAnnotation, dexBuilderAnnotation); + return ret==null?dexBuilderAnnotation:ret; + } + + @Override public int getVisibility(@Nonnull BuilderAnnotation key) { + return key.visibility; + } + + @Nonnull @Override public BuilderTypeReference getType(@Nonnull BuilderAnnotation key) { + return key.type; + } + + @Nonnull @Override + public Collection getElements(@Nonnull BuilderAnnotation key) { + return key.elements; + } + + @Nonnull @Override + public BuilderStringReference getElementName(@Nonnull BuilderAnnotationElement element) { + return element.name; + } + + @Nonnull @Override + public BuilderEncodedValue getElementValue(@Nonnull BuilderAnnotationElement element) { + return element.value; + } + + @Override public int getItemOffset(@Nonnull BuilderAnnotation key) { + return key.offset; + } + + @Nonnull @Override public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override protected int getValue(@Nonnull BuilderAnnotation key) { + return key.offset; + } + + @Override protected int setValue(@Nonnull BuilderAnnotation key, int value) { + int prev = key.offset; + key.offset = value; + return prev; + } + }; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationSet.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationSet.java new file mode 100644 index 00000000..43ca7452 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationSet.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.builder; + +import com.google.common.collect.ImmutableSet; +import org.jf.dexlib2.writer.DexWriter; + +import javax.annotation.Nonnull; +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Set; + +class BuilderAnnotationSet extends AbstractSet { + public static final BuilderAnnotationSet EMPTY = + new BuilderAnnotationSet(ImmutableSet.of()); + + @Nonnull final Set annotations; + int offset = DexWriter.NO_OFFSET; + + public BuilderAnnotationSet(@Nonnull Set annotations) { + this.annotations = annotations; + } + + @Nonnull @Override public Iterator iterator() { + return annotations.iterator(); + } + + @Override public int size() { + return annotations.size(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationSetPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationSetPool.java new file mode 100644 index 00000000..353190fb --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderAnnotationSetPool.java @@ -0,0 +1,106 @@ +/* + * 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.builder; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterators; +import com.google.common.collect.Maps; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.writer.AnnotationSetSection; +import org.jf.dexlib2.writer.DexWriter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +class BuilderAnnotationSetPool implements AnnotationSetSection { + @Nonnull private final BuilderContext context; + @Nonnull private final ConcurrentMap, BuilderAnnotationSet> internedItems = + Maps.newConcurrentMap(); + + BuilderAnnotationSetPool(@Nonnull BuilderContext context) { + this.context = context; + } + + @Nonnull public BuilderAnnotationSet internAnnotationSet(@Nullable Set annotations) { + if (annotations == null) { + return BuilderAnnotationSet.EMPTY; + } + + BuilderAnnotationSet ret = internedItems.get(annotations); + if (ret != null) { + return ret; + } + + BuilderAnnotationSet annotationSet = new BuilderAnnotationSet( + ImmutableSet.copyOf(Iterators.transform(annotations.iterator(), + new Function() { + @Nullable @Override public BuilderAnnotation apply(Annotation input) { + return context.annotationPool.internAnnotation(input); + } + }))); + + ret = internedItems.putIfAbsent(annotationSet, annotationSet); + return ret==null?annotationSet:ret; + } + + @Nonnull @Override + public Collection getAnnotations(@Nonnull BuilderAnnotationSet key) { + return key.annotations; + } + + @Override public int getNullableItemOffset(@Nullable BuilderAnnotationSet key) { + return key==null?DexWriter.NO_OFFSET:key.offset; + } + + @Override public int getItemOffset(@Nonnull BuilderAnnotationSet key) { + return key.offset; + } + + @Nonnull @Override public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override protected int getValue(@Nonnull BuilderAnnotationSet key) { + return key.offset; + } + + @Override protected int setValue(@Nonnull BuilderAnnotationSet key, int value) { + int prev = key.offset; + key.offset = value; + return prev; + } + }; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassDef.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassDef.java new file mode 100644 index 00000000..a793b9ea --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassDef.java @@ -0,0 +1,132 @@ +/* + * 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.builder; + +import com.google.common.base.Functions; +import com.google.common.collect.*; +import org.jf.dexlib2.base.reference.BaseTypeReference; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.util.FieldUtil; +import org.jf.dexlib2.util.MethodUtil; +import org.jf.dexlib2.writer.DexWriter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; + +public class BuilderClassDef extends BaseTypeReference implements ClassDef { + @Nonnull final BuilderTypeReference type; + final int accessFlags; + @Nullable final BuilderTypeReference superclass; + @Nonnull final BuilderTypeList interfaces; + @Nullable final BuilderStringReference sourceFile; + @Nonnull final BuilderAnnotationSet annotations; + @Nonnull final SortedSet staticFields; + @Nonnull final SortedSet instanceFields; + @Nonnull final SortedSet directMethods; + @Nonnull final SortedSet virtualMethods; + + int classDefIndex = DexWriter.NO_INDEX; + int encodedArrayOffset = DexWriter.NO_OFFSET; + int annotationDirectoryOffset = DexWriter.NO_OFFSET; + + BuilderClassDef(@Nonnull BuilderTypeReference type, + int accessFlags, + @Nullable BuilderTypeReference superclass, + @Nonnull BuilderTypeList interfaces, + @Nullable BuilderStringReference sourceFile, + @Nonnull BuilderAnnotationSet annotations, + @Nullable Iterable fields, + @Nullable Iterable methods) { + this.type = type; + this.accessFlags = accessFlags; + this.superclass = superclass; + this.interfaces = interfaces; + this.sourceFile = sourceFile; + this.annotations = annotations; + this.staticFields = ImmutableSortedSet.copyOf(Iterables.filter(fields, FieldUtil.FIELD_IS_STATIC)); + this.instanceFields = ImmutableSortedSet.copyOf(Iterables.filter(fields, FieldUtil.FIELD_IS_INSTANCE)); + this.directMethods = ImmutableSortedSet.copyOf(Iterables.filter(methods, MethodUtil.METHOD_IS_DIRECT)); + this.virtualMethods = ImmutableSortedSet.copyOf(Iterables.filter(methods, MethodUtil.METHOD_IS_VIRTUAL)); + } + + @Nonnull @Override public String getType() { return type.getType(); } + @Override public int getAccessFlags() { return accessFlags; } + @Nullable @Override public String getSuperclass() { return superclass==null?null:superclass.getType(); } + @Nullable @Override public String getSourceFile() { return sourceFile==null?null:sourceFile.getString(); } + @Nonnull @Override public BuilderAnnotationSet getAnnotations() { return annotations; } + @Nonnull @Override public SortedSet getStaticFields() { return staticFields; } + @Nonnull @Override public SortedSet getInstanceFields() { return instanceFields; } + @Nonnull @Override public SortedSet getDirectMethods() { return directMethods; } + @Nonnull @Override public SortedSet getVirtualMethods() { return virtualMethods; } + + @Nonnull @Override + public Set getInterfaces() { + return new AbstractSet() { + @Nonnull @Override public Iterator iterator() { + return Iterators.transform(interfaces.iterator(), Functions.toStringFunction()); + } + + @Override public int size() { + return interfaces.size(); + } + }; + } + + @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 Collection 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/builder/BuilderClassPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java new file mode 100644 index 00000000..cda9a4e2 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java @@ -0,0 +1,418 @@ +/* + * 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.builder; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.*; +import org.jf.dexlib2.DebugItemType; +import org.jf.dexlib2.iface.Field; +import org.jf.dexlib2.iface.TryBlock; +import org.jf.dexlib2.iface.debug.EndLocal; +import org.jf.dexlib2.iface.debug.LineNumber; +import org.jf.dexlib2.iface.debug.RestartLocal; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.util.EncodedValueUtils; +import org.jf.dexlib2.writer.ClassSection; +import org.jf.dexlib2.writer.DebugWriter; +import org.jf.dexlib2.writer.builder.BuilderDebugItem.BuilderSetSourceFile; +import org.jf.dexlib2.writer.builder.BuilderDebugItem.BuilderStartLocal; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue; +import org.jf.util.AbstractForwardSequentialList; +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.Map.Entry; +import java.util.concurrent.ConcurrentMap; + +public class BuilderClassPool implements ClassSection { + @Nonnull private final ConcurrentMap internedItems = + Maps.newConcurrentMap(); + + BuilderClassPool() { + } + + @Nonnull BuilderClassDef internClass(@Nonnull BuilderClassDef classDef) { + BuilderClassDef prev = internedItems.put(classDef.getType(), classDef); + if (prev != null) { + throw new ExceptionWithContext("Class %s has already been interned", classDef.getType()); + } + return classDef; + } + + private ImmutableList sortedClasses = null; + @Nonnull @Override public Collection getSortedClasses() { + if (sortedClasses == null) { + sortedClasses = Ordering.natural().immutableSortedCopy(internedItems.values()); + } + return sortedClasses; + } + + @Nullable @Override + public Entry getClassEntryByType(@Nullable BuilderTypeReference type) { + if (type == null) { + return null; + } + + final BuilderClassDef classDef = internedItems.get(type.getType()); + if (classDef == null) { + return null; + } + + return new Map.Entry() { + @Override public BuilderClassDef getKey() { + return classDef; + } + + @Override public Integer getValue() { + return classDef.classDefIndex; + } + + @Override public Integer setValue(Integer value) { + return classDef.classDefIndex = value; + } + }; + } + + @Nonnull @Override public BuilderTypeReference getType(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.type; + } + + @Override public int getAccessFlags(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.accessFlags; + } + + @Nullable @Override public BuilderTypeReference getSuperclass(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.superclass; + } + + @Nullable @Override public BuilderTypeList getSortedInterfaces(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.interfaces; + } + + @Nullable @Override public BuilderStringReference getSourceFile(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.sourceFile; + } + + private static final Predicate HAS_INITIALIZER = new Predicate() { + @Override + public boolean apply(Field input) { + EncodedValue encodedValue = input.getInitialValue(); + return encodedValue != null && !EncodedValueUtils.isDefaultValue(encodedValue); + } + }; + + private static final Function GET_INITIAL_VALUE = + new Function() { + @Override + public BuilderEncodedValue apply(BuilderField input) { + BuilderEncodedValue initialValue = input.getInitialValue(); + if (initialValue == null) { + return BuilderEncodedValues.defaultValueForType(input.getType()); + } + return initialValue; + } + }; + + @Nullable @Override + public Collection getStaticInitializers(@Nonnull BuilderClassDef classDef) { + final SortedSet sortedStaticFields = classDef.getStaticFields(); + + final int lastIndex = CollectionUtils.lastIndexOf(sortedStaticFields, HAS_INITIALIZER); + if (lastIndex > -1) { + return new AbstractCollection() { + @Nonnull @Override public Iterator iterator() { + return FluentIterable.from(sortedStaticFields) + .limit(lastIndex+1) + .transform(GET_INITIAL_VALUE).iterator(); + } + + @Override public int size() { + return lastIndex+1; + } + }; + } + return null; + } + + @Nonnull @Override + public Collection getSortedStaticFields(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.getStaticFields(); + } + + @Nonnull @Override + public Collection getSortedInstanceFields(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.getInstanceFields(); + } + + @Nonnull @Override + public Collection getSortedFields(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.getFields(); + } + + @Nonnull @Override + public Collection getSortedDirectMethods(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.getDirectMethods(); + } + + @Nonnull @Override + public Collection getSortedVirtualMethods(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.getVirtualMethods(); + } + + @Nonnull @Override + public Collection getSortedMethods(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.getMethods(); + } + + @Override public int getFieldAccessFlags(@Nonnull BuilderField builderField) { + return builderField.accessFlags; + } + + @Override public int getMethodAccessFlags(@Nonnull BuilderMethod builderMethod) { + return builderMethod.accessFlags; + } + + @Nullable @Override public BuilderAnnotationSet getClassAnnotations(@Nonnull BuilderClassDef builderClassDef) { + if (builderClassDef.annotations.isEmpty()) { + return null; + } + return builderClassDef.annotations; + } + + @Nullable @Override public BuilderAnnotationSet getFieldAnnotations(@Nonnull BuilderField builderField) { + if (builderField.annotations.isEmpty()) { + return null; + } + return builderField.annotations; + } + + @Nullable @Override public BuilderAnnotationSet getMethodAnnotations(@Nonnull BuilderMethod builderMethod) { + if (builderMethod.annotations.isEmpty()) { + return null; + } + return builderMethod.annotations; + } + + private static final Predicate HAS_PARAMETER_ANNOTATIONS = + new Predicate() { + @Override + public boolean apply(BuilderMethodParameter input) { + return input.getAnnotations().size() > 0; + } + }; + + private static final Function PARAMETER_ANNOTATIONS = + new Function() { + @Override + public BuilderAnnotationSet apply(BuilderMethodParameter input) { + return input.getAnnotations(); + } + }; + + @Nullable @Override public List getParameterAnnotations( + @Nonnull final BuilderMethod method) { + final int lastIndex = CollectionUtils.lastIndexOf(method.getParameters(), HAS_PARAMETER_ANNOTATIONS); + + if (lastIndex > -1) { + return new AbstractForwardSequentialList() { + @Nonnull @Override public Iterator iterator() { + return FluentIterable.from(method.getParameters()) + .limit(lastIndex+1) + .transform(PARAMETER_ANNOTATIONS).iterator(); + } + + @Override public int size() { + return lastIndex+1; + } + }; + } + return null; + } + + @Nullable @Override + public Iterable getDebugItems(@Nonnull BuilderMethod builderMethod) { + BuilderMethodImplementation impl = builderMethod.getImplementation(); + if (impl == null) { + return null; + } + return impl.getDebugItems(); + } + + @Nullable @Override + public Iterable getParameterNames(@Nonnull BuilderMethod method) { + return Iterables.transform(method.getParameters(), new Function() { + @Nullable @Override public BuilderStringReference apply(BuilderMethodParameter input) { + return input.name; + } + }); + } + + @Override public int getRegisterCount(@Nonnull BuilderMethod builderMethod) { + BuilderMethodImplementation impl = builderMethod.getImplementation(); + if (impl == null) { + return 0; + } + return impl.registerCount; + } + + @Nullable @Override + public Iterable getInstructions(@Nonnull BuilderMethod builderMethod) { + BuilderMethodImplementation impl = builderMethod.getImplementation(); + if (impl == null) { + return null; + } + return impl.getInstructions(); + } + + @Nonnull @Override + public List> getTryBlocks(@Nonnull BuilderMethod builderMethod) { + BuilderMethodImplementation impl = builderMethod.getImplementation(); + if (impl == null) { + return ImmutableList.of(); + } + return impl.getTryBlocks(); + } + + @Nullable @Override public BuilderTypeReference getExceptionType(@Nonnull BuilderExceptionHandler handler) { + return handler.exceptionType; + } + + @Override public void setEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef, int offset) { + builderClassDef.encodedArrayOffset = offset; + } + + @Override public int getEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.encodedArrayOffset; + } + + @Override public void setAnnotationDirectoryOffset(@Nonnull BuilderClassDef builderClassDef, int offset) { + builderClassDef.annotationDirectoryOffset = offset; + } + + @Override public int getAnnotationDirectoryOffset(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.annotationDirectoryOffset; + } + + @Override public void setAnnotationSetRefListOffset(@Nonnull BuilderMethod builderMethod, int offset) { + builderMethod.annotationSetRefListOffset = offset; + } + + @Override public int getAnnotationSetRefListOffset(@Nonnull BuilderMethod builderMethod) { + return builderMethod.annotationSetRefListOffset; + } + + @Override public void setCodeItemOffset(@Nonnull BuilderMethod builderMethod, int offset) { + builderMethod.codeItemOffset = offset; + } + + @Override public int getCodeItemOffset(@Nonnull BuilderMethod builderMethod) { + return builderMethod.codeItemOffset; + } + + @Override public void setDebugItemOffset(@Nonnull BuilderMethod builderMethod, int offset) { + builderMethod.debugInfoOffset = offset; + } + + @Override public int getDebugItemOffset(@Nonnull BuilderMethod builderMethod) { + return builderMethod.debugInfoOffset; + } + + @Override + public void writeDebugItem(@Nonnull DebugWriter writer, + BuilderDebugItem debugItem) throws IOException { + switch (debugItem.getDebugItemType()) { + case DebugItemType.START_LOCAL: { + BuilderStartLocal startLocal = (BuilderStartLocal)debugItem; + writer.writeStartLocal(startLocal.getCodeAddress(), + startLocal.register, + startLocal.name, + startLocal.type, + startLocal.signature); + 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: { + BuilderSetSourceFile setSourceFile = (BuilderSetSourceFile)debugItem; + writer.writeSetSourceFile(setSourceFile.getCodeAddress(), setSourceFile.sourceFile); + } + default: + throw new ExceptionWithContext("Unexpected debug item type: %d", debugItem.getDebugItemType()); + } + } + + @Override public int getItemIndex(@Nonnull BuilderClassDef builderClassDef) { + return builderClassDef.classDefIndex; + } + + @Nonnull @Override public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override protected int getValue(@Nonnull BuilderClassDef key) { + return key.classDefIndex; + } + + @Override protected int setValue(@Nonnull BuilderClassDef key, int value) { + int prev = key.classDefIndex; + key.classDefIndex = value; + return prev; + } + }; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderContext.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderContext.java new file mode 100644 index 00000000..e6f8e225 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderContext.java @@ -0,0 +1,175 @@ +/* + * 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.builder; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterators; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.AnnotationElement; +import org.jf.dexlib2.iface.value.*; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.*; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Set; + +class BuilderContext { + // keep our own local references to the various pools, using the Builder specific pool type; + @Nonnull final BuilderStringPool stringPool; + @Nonnull final BuilderTypePool typePool; + @Nonnull final BuilderFieldPool fieldPool; + @Nonnull final BuilderMethodPool methodPool; + @Nonnull final BuilderProtoPool protoPool; + @Nonnull final BuilderClassPool classPool; + + @Nonnull final BuilderTypeListPool typeListPool; + @Nonnull final BuilderAnnotationPool annotationPool; + @Nonnull final BuilderAnnotationSetPool annotationSetPool; + + + BuilderContext() { + this.stringPool = new BuilderStringPool(); + this.typePool = new BuilderTypePool(this); + this.fieldPool = new BuilderFieldPool(this); + this.methodPool = new BuilderMethodPool(this); + this.protoPool = new BuilderProtoPool(this); + this.classPool = new BuilderClassPool(); + + this.typeListPool = new BuilderTypeListPool(this); + this.annotationPool = new BuilderAnnotationPool(this); + this.annotationSetPool = new BuilderAnnotationSetPool(this); + } + + @Nonnull Set internAnnotationElements( + @Nonnull Set elements) { + return ImmutableSet.copyOf( + Iterators.transform(elements.iterator(), + new Function() { + @Nullable @Override + public BuilderAnnotationElement apply(AnnotationElement input) { + return internAnnotationElement(input); + } + })); + } + + @Nonnull private BuilderAnnotationElement internAnnotationElement(@Nonnull AnnotationElement annotationElement) { + return new BuilderAnnotationElement(stringPool.internString(annotationElement.getName()), + internEncodedValue(annotationElement.getValue())); + } + + @Nullable BuilderEncodedValue internNullableEncodedValue(@Nullable EncodedValue encodedValue) { + if (encodedValue == null) { + return null; + } + return internEncodedValue(encodedValue); + } + + @Nonnull private BuilderEncodedValue internEncodedValue(@Nonnull EncodedValue encodedValue) { + switch (encodedValue.getValueType()) { + case ValueType.ANNOTATION: + return internAnnotationEncodedValue((AnnotationEncodedValue)encodedValue); + case ValueType.ARRAY: + return internArrayEncodedValue((ArrayEncodedValue)encodedValue); + case ValueType.BOOLEAN: + boolean value = ((BooleanEncodedValue)encodedValue).getValue(); + return value?BuilderBooleanEncodedValue.TRUE_VALUE:BuilderBooleanEncodedValue.FALSE_VALUE; + case ValueType.BYTE: + return new BuilderByteEncodedValue(((ByteEncodedValue)encodedValue).getValue()); + case ValueType.CHAR: + return new BuilderCharEncodedValue(((CharEncodedValue)encodedValue).getValue()); + case ValueType.DOUBLE: + return new BuilderDoubleEncodedValue(((DoubleEncodedValue)encodedValue).getValue()); + case ValueType.ENUM: + return internEnumEncodedValue((EnumEncodedValue)encodedValue); + case ValueType.FIELD: + return internFieldEncodedValue((FieldEncodedValue)encodedValue); + case ValueType.FLOAT: + return new BuilderFloatEncodedValue(((FloatEncodedValue)encodedValue).getValue()); + case ValueType.INT: + return new BuilderIntEncodedValue(((IntEncodedValue)encodedValue).getValue()); + case ValueType.LONG: + return new BuilderLongEncodedValue(((LongEncodedValue)encodedValue).getValue()); + case ValueType.METHOD: + return internMethodEncodedValue((MethodEncodedValue)encodedValue); + case ValueType.NULL: + return BuilderNullEncodedValue.INSTANCE; + case ValueType.SHORT: + return new BuilderShortEncodedValue(((ShortEncodedValue)encodedValue).getValue()); + case ValueType.STRING: + return internStringEncodedValue((StringEncodedValue)encodedValue); + case ValueType.TYPE: + return internTypeEncodedValue((TypeEncodedValue)encodedValue); + default: + throw new ExceptionWithContext("Unexpected encoded value type: %d", encodedValue.getValueType()); + } + } + + @Nonnull private BuilderAnnotationEncodedValue internAnnotationEncodedValue(@Nonnull AnnotationEncodedValue value) { + return new BuilderAnnotationEncodedValue( + typePool.internType(value.getType()), + internAnnotationElements(value.getElements())); + } + + @Nonnull private BuilderArrayEncodedValue internArrayEncodedValue(@Nonnull ArrayEncodedValue value) { + return new BuilderArrayEncodedValue( + ImmutableList.copyOf( + Iterators.transform(value.getValue().iterator(), + new Function() { + @Nullable @Override public BuilderEncodedValue apply(EncodedValue input) { + return internEncodedValue(input); + } + }))); + } + + @Nonnull private BuilderEnumEncodedValue internEnumEncodedValue(@Nonnull EnumEncodedValue value) { + return new BuilderEnumEncodedValue(fieldPool.internField(value.getValue())); + } + + @Nonnull private BuilderFieldEncodedValue internFieldEncodedValue(@Nonnull FieldEncodedValue value) { + return new BuilderFieldEncodedValue(fieldPool.internField(value.getValue())); + } + + @Nonnull private BuilderMethodEncodedValue internMethodEncodedValue(@Nonnull MethodEncodedValue value) { + return new BuilderMethodEncodedValue(methodPool.internMethod(value.getValue())); + } + + @Nonnull private BuilderStringEncodedValue internStringEncodedValue(@Nonnull StringEncodedValue string) { + return new BuilderStringEncodedValue(stringPool.internString(string.getValue())); + } + + @Nonnull private BuilderTypeEncodedValue internTypeEncodedValue(@Nonnull TypeEncodedValue type) { + return new BuilderTypeEncodedValue(typePool.internType(type.getValue())); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderDebugItem.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderDebugItem.java new file mode 100644 index 00000000..85a62f45 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderDebugItem.java @@ -0,0 +1,168 @@ +/* + * 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.builder; + +import org.jf.dexlib2.DebugItemType; +import org.jf.dexlib2.iface.debug.*; +import org.jf.dexlib2.immutable.debug.ImmutableEpilogueBegin; +import org.jf.dexlib2.immutable.debug.ImmutableLineNumber; +import org.jf.dexlib2.immutable.debug.ImmutablePrologueEnd; + +import javax.annotation.Nullable; + +public abstract interface BuilderDebugItem extends DebugItem { + abstract static class BaseBuilderDebugItem implements BuilderDebugItem { + final int codeAddress; + + public BaseBuilderDebugItem(int codeAddress) { + this.codeAddress = codeAddress; + } + + @Override public int getCodeAddress() { return codeAddress; } + } + + public static class BuilderStartLocal extends BaseBuilderDebugItem implements StartLocal { + final int register; + @Nullable final BuilderStringReference name; + @Nullable final BuilderTypeReference type; + @Nullable final BuilderStringReference signature; + + BuilderStartLocal(int codeAddress, + int register, + @Nullable BuilderStringReference name, + @Nullable BuilderTypeReference type, + @Nullable BuilderStringReference signature) { + super(codeAddress); + this.register = register; + this.name = name; + this.type = type; + this.signature = signature; + } + + @Override public int getRegister() { return register; } + @Nullable @Override public String getName() { return name==null?null:name.getString(); } + @Nullable @Override public String getType() { return type==null?null:type.getType(); } + @Nullable @Override public String getSignature() { return signature==null?null:signature.getString(); } + + @Override public int getDebugItemType() { return DebugItemType.START_LOCAL; } + } + + public static class BuilderEndLocal extends BaseBuilderDebugItem implements EndLocal { + private final int register; + + BuilderEndLocal(int codeAddress, int register) { + super(codeAddress); + this.register = register; + } + + @Override public int getRegister() { + return register; + } + + @Override public int getDebugItemType() { + return DebugItemType.END_LOCAL; + } + + @Nullable @Override public String getName() { + return null; + } + + @Nullable @Override public String getType() { + return null; + } + + @Nullable @Override public String getSignature() { + return null; + } + } + + public static class BuilderRestartLocal extends BaseBuilderDebugItem implements RestartLocal { + private final int register; + + BuilderRestartLocal(int codeAddress, int register) { + super(codeAddress); + this.register = register; + } + + @Override public int getRegister() { + return register; + } + + @Override public int getDebugItemType() { + return DebugItemType.RESTART_LOCAL; + } + + @Nullable @Override public String getName() { + return null; + } + + @Nullable @Override public String getType() { + return null; + } + + @Nullable @Override public String getSignature() { + return null; + } + } + + public static class BuilderPrologueEnd extends ImmutablePrologueEnd implements BuilderDebugItem { + BuilderPrologueEnd(int codeAddress) { + super(codeAddress); + } + } + + public static class BuilderEpilogueBegin extends ImmutableEpilogueBegin implements BuilderDebugItem { + BuilderEpilogueBegin(int codeAddress) { + super(codeAddress); + } + } + + public static class BuilderLineNumber extends ImmutableLineNumber implements BuilderDebugItem { + BuilderLineNumber(int codeAddress, int lineNumber) { + super(codeAddress, lineNumber); + } + } + + public static class BuilderSetSourceFile extends BaseBuilderDebugItem implements SetSourceFile { + @Nullable final BuilderStringReference sourceFile; + + BuilderSetSourceFile(int codeAddress, + @Nullable BuilderStringReference sourceFile) { + super(codeAddress); + this.sourceFile = sourceFile; + } + + @Nullable @Override public String getSourceFile() { return sourceFile==null?null:sourceFile.getString(); } + + @Override public int getDebugItemType() { return DebugItemType.SET_SOURCE_FILE; } + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderEncodedValues.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderEncodedValues.java new file mode 100644 index 00000000..0f047124 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderEncodedValues.java @@ -0,0 +1,242 @@ +/* + * 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.builder; + +import org.jf.dexlib2.base.value.*; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.immutable.value.*; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Set; + +public abstract class BuilderEncodedValues { + public static interface BuilderEncodedValue extends EncodedValue { + } + + public static class BuilderAnnotationEncodedValue extends BaseAnnotationEncodedValue + implements BuilderEncodedValue { + @Nonnull final BuilderTypeReference typeReference; + @Nonnull final Set elements; + + BuilderAnnotationEncodedValue(@Nonnull BuilderTypeReference typeReference, + @Nonnull Set elements) { + this.typeReference = typeReference; + this.elements = elements; + } + + @Nonnull @Override public String getType() { + return typeReference.getType(); + } + + @Nonnull @Override public Set getElements() { + return elements; + } + } + + public static class BuilderArrayEncodedValue extends BaseArrayEncodedValue implements BuilderEncodedValue { + @Nonnull final List elements; + + BuilderArrayEncodedValue(@Nonnull List elements) { + this.elements = elements; + } + + @Nonnull @Override public List getValue() { + return elements; + } + } + + @Nonnull + public static BuilderEncodedValue defaultValueForType(String type) { + switch (type.charAt(0)) { + case 'Z': + return BuilderBooleanEncodedValue.FALSE_VALUE; + case 'B': + return new BuilderByteEncodedValue((byte)0); + case 'S': + return new BuilderShortEncodedValue((short)0); + case 'C': + return new BuilderCharEncodedValue((char)0); + case 'I': + return new BuilderIntEncodedValue(0); + case 'J': + return new BuilderLongEncodedValue(0); + case 'F': + return new BuilderFloatEncodedValue(0); + case 'D': + return new BuilderDoubleEncodedValue(0); + case 'L': + case '[': + return BuilderNullEncodedValue.INSTANCE; + default: + throw new ExceptionWithContext("Unrecognized type: %s", type); + } + } + + public static class BuilderBooleanEncodedValue extends BaseBooleanEncodedValue + implements BuilderEncodedValue { + public static final BuilderBooleanEncodedValue TRUE_VALUE = new BuilderBooleanEncodedValue(true); + public static final BuilderBooleanEncodedValue FALSE_VALUE = new BuilderBooleanEncodedValue(false); + + private final boolean value; + + private BuilderBooleanEncodedValue(boolean value) { + this.value = value; + } + + @Override public boolean getValue() { + return value; + } + } + + public static class BuilderByteEncodedValue extends ImmutableByteEncodedValue + implements BuilderEncodedValue { + public BuilderByteEncodedValue(byte value) { + super(value); + } + } + + public static class BuilderCharEncodedValue extends ImmutableCharEncodedValue + implements BuilderEncodedValue { + public BuilderCharEncodedValue(char value) { + super(value); + } + } + + public static class BuilderDoubleEncodedValue extends ImmutableDoubleEncodedValue + implements BuilderEncodedValue { + public BuilderDoubleEncodedValue(double value) { + super(value); + } + } + + public static class BuilderEnumEncodedValue extends BaseEnumEncodedValue + implements BuilderEncodedValue { + @Nonnull final BuilderFieldReference enumReference; + + BuilderEnumEncodedValue(@Nonnull BuilderFieldReference enumReference) { + this.enumReference = enumReference; + } + + @Nonnull @Override public BuilderFieldReference getValue() { + return enumReference; + } + } + + public static class BuilderFieldEncodedValue extends BaseFieldEncodedValue + implements BuilderEncodedValue { + @Nonnull final BuilderFieldReference fieldReference; + + BuilderFieldEncodedValue(@Nonnull BuilderFieldReference fieldReference) { + this.fieldReference = fieldReference; + } + + @Nonnull @Override public BuilderFieldReference getValue() { + return fieldReference; + } + } + + public static class BuilderFloatEncodedValue extends ImmutableFloatEncodedValue + implements BuilderEncodedValue { + public BuilderFloatEncodedValue(float value) { + super(value); + } + } + + public static class BuilderIntEncodedValue extends ImmutableIntEncodedValue + implements BuilderEncodedValue { + public BuilderIntEncodedValue(int value) { + super(value); + } + } + + public static class BuilderLongEncodedValue extends ImmutableLongEncodedValue + implements BuilderEncodedValue { + public BuilderLongEncodedValue(long value) { + super(value); + } + } + + public static class BuilderMethodEncodedValue extends BaseMethodEncodedValue + implements BuilderEncodedValue { + @Nonnull final BuilderMethodReference methodReference; + + BuilderMethodEncodedValue(@Nonnull BuilderMethodReference methodReference) { + this.methodReference = methodReference; + } + + @Override public BuilderMethodReference getValue() { + return methodReference; + } + } + + public static class BuilderNullEncodedValue extends BaseNullEncodedValue + implements BuilderEncodedValue { + public static final BuilderNullEncodedValue INSTANCE = new BuilderNullEncodedValue(); + + private BuilderNullEncodedValue() {} + } + + public static class BuilderShortEncodedValue extends ImmutableShortEncodedValue + implements BuilderEncodedValue { + public BuilderShortEncodedValue(short value) { + super(value); + } + } + + public static class BuilderStringEncodedValue extends BaseStringEncodedValue + implements BuilderEncodedValue { + @Nonnull final BuilderStringReference stringReference; + + BuilderStringEncodedValue(@Nonnull BuilderStringReference stringReference) { + this.stringReference = stringReference; + } + + @Nonnull @Override public String getValue() { + return stringReference.getString(); + } + } + + public static class BuilderTypeEncodedValue extends BaseTypeEncodedValue + implements BuilderEncodedValue { + @Nonnull final BuilderTypeReference typeReference; + + BuilderTypeEncodedValue(@Nonnull BuilderTypeReference typeReference) { + this.typeReference = typeReference; + } + + @Nonnull @Override public String getValue() { + return typeReference.getType(); + } + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderExceptionHandler.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderExceptionHandler.java new file mode 100644 index 00000000..713226b3 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderExceptionHandler.java @@ -0,0 +1,54 @@ +/* + * 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.builder; + +import org.jf.dexlib2.base.BaseExceptionHandler; + +import javax.annotation.Nullable; + +public class BuilderExceptionHandler extends BaseExceptionHandler { + @Nullable final BuilderTypeReference exceptionType; + final int handlerCodeAddress; + + BuilderExceptionHandler(@Nullable BuilderTypeReference exceptionType, int handlerCodeAddress) { + this.exceptionType = exceptionType; + this.handlerCodeAddress = handlerCodeAddress; + } + + @Nullable @Override public String getExceptionType() { + return exceptionType==null?null:exceptionType.getType(); + } + + @Override public int getHandlerCodeAddress() { + return handlerCodeAddress; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderField.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderField.java new file mode 100644 index 00000000..c5185cde --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderField.java @@ -0,0 +1,80 @@ +/* + * 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.builder; + +import org.jf.dexlib2.base.reference.BaseFieldReference; +import org.jf.dexlib2.iface.Field; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class BuilderField extends BaseFieldReference implements Field { + @Nonnull final BuilderFieldReference fieldReference; + final int accessFlags; + @Nullable final BuilderEncodedValue initialValue; + @Nonnull final BuilderAnnotationSet annotations; + + BuilderField(@Nonnull BuilderFieldReference fieldReference, + int accessFlags, + @Nullable BuilderEncodedValue initialValue, + @Nonnull BuilderAnnotationSet annotations) { + this.fieldReference = fieldReference; + this.accessFlags = accessFlags; + this.initialValue = initialValue; + this.annotations = annotations; + } + + @Override public int getAccessFlags() { + return accessFlags; + } + + @Nullable @Override public BuilderEncodedValue getInitialValue() { + return initialValue; + } + + @Nonnull @Override public BuilderAnnotationSet getAnnotations() { + return annotations; + } + + @Nonnull @Override public String getDefiningClass() { + return fieldReference.definingClass.getType(); + } + + @Nonnull @Override public String getName() { + return fieldReference.name.getString(); + } + + @Nonnull @Override public String getType() { + return fieldReference.fieldType.getType(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderFieldPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderFieldPool.java new file mode 100644 index 00000000..7a90649f --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderFieldPool.java @@ -0,0 +1,107 @@ +/* + * 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.builder; + +import com.google.common.collect.Maps; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.immutable.reference.ImmutableFieldReference; +import org.jf.dexlib2.writer.FieldSection; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; + +public class BuilderFieldPool + implements FieldSection { + @Nonnull private final BuilderContext context; + @Nonnull private final ConcurrentMap internedItems = + Maps.newConcurrentMap(); + + BuilderFieldPool(@Nonnull BuilderContext context) { + this.context = context; + } + + @Nonnull BuilderFieldReference internField(@Nonnull String definingClass, String name, String type) { + ImmutableFieldReference fieldReference = new ImmutableFieldReference(definingClass, name, type); + return internField(fieldReference); + } + + @Nonnull public BuilderFieldReference internField(@Nonnull FieldReference fieldReference) { + BuilderFieldReference ret = internedItems.get(fieldReference); + if (ret != null) { + return ret; + } + + BuilderFieldReference dexPoolFieldReference = new BuilderFieldReference( + context.typePool.internType(fieldReference.getDefiningClass()), + context.stringPool.internString(fieldReference.getName()), + context.typePool.internType(fieldReference.getType())); + ret = internedItems.putIfAbsent(dexPoolFieldReference, dexPoolFieldReference); + return ret==null?dexPoolFieldReference:ret; + } + + @Nonnull @Override + public BuilderTypeReference getDefiningClass(@Nonnull BuilderFieldReference key) { + return key.definingClass; + } + + @Nonnull @Override public BuilderTypeReference getFieldType(@Nonnull BuilderFieldReference key) { + return key.fieldType; + } + + @Nonnull @Override public BuilderStringReference getName(@Nonnull BuilderFieldReference key) { + return key.name; + } + + @Override public int getFieldIndex(@Nonnull BuilderField builderField) { + return builderField.fieldReference.getIndex(); + } + + @Override public int getItemIndex(@Nonnull BuilderFieldReference key) { + return key.index; + } + + @Nonnull @Override public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override protected int getValue(@Nonnull BuilderFieldReference key) { + return key.index; + } + + @Override protected int setValue(@Nonnull BuilderFieldReference key, int value) { + int prev = key.index; + key.index = value; + return prev; + } + }; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderFieldReference.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderFieldReference.java new file mode 100644 index 00000000..e64aa673 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderFieldReference.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.builder; + +import org.jf.dexlib2.base.reference.BaseFieldReference; +import org.jf.dexlib2.writer.DexWriter; + +import javax.annotation.Nonnull; + +public class BuilderFieldReference extends BaseFieldReference implements BuilderReference { + @Nonnull final BuilderTypeReference definingClass; + @Nonnull final BuilderStringReference name; + @Nonnull final BuilderTypeReference fieldType; + int index = DexWriter.NO_INDEX; + + BuilderFieldReference(@Nonnull BuilderTypeReference definingClass, + @Nonnull BuilderStringReference name, + @Nonnull BuilderTypeReference fieldType) { + this.definingClass = definingClass; + this.name = name; + this.fieldType = fieldType; + } + + @Nonnull @Override public String getDefiningClass() { + return definingClass.getType(); + } + + @Nonnull @Override public String getName() { + return name.getString(); + } + + @Nonnull @Override public String getType() { + return fieldType.getType(); + } + + @Override public int getIndex() { + return index; + } + + @Override public void setIndex(int index) { + this.index = index; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderInstruction.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderInstruction.java new file mode 100644 index 00000000..e6f56d4d --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderInstruction.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.builder; + +import com.google.common.collect.ImmutableList; +import org.jf.dexlib2.Format; +import org.jf.dexlib2.Opcode; +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.immutable.instruction.*; +import org.jf.dexlib2.util.Preconditions; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public interface BuilderInstruction extends Instruction { + static abstract class BaseBuilderInstruction implements BuilderInstruction { + @Nonnull protected final Opcode opcode; + + public BaseBuilderInstruction(@Nonnull Opcode opcode) { + this.opcode = opcode; + } + + @Nonnull public abstract Format getFormat(); + + @Nonnull @Override public Opcode getOpcode() { + return opcode; + } + + @Override public int getCodeUnits() { + return getFormat().size/2; + } + } + + public static class BuilderInstruction10t extends ImmutableInstruction10t implements BuilderInstruction { + public BuilderInstruction10t(@Nonnull Opcode opcode, int codeOffset) { + super(opcode, codeOffset); + } + } + + public static class BuilderInstruction10x extends ImmutableInstruction10x implements BuilderInstruction { + public BuilderInstruction10x(@Nonnull Opcode opcode) { + super(opcode); + } + } + + public static class BuilderInstruction11n extends ImmutableInstruction11n implements BuilderInstruction { + public BuilderInstruction11n(@Nonnull Opcode opcode, int registerA, int literal) { + super(opcode, registerA, literal); + } + } + + public static class BuilderInstruction11x extends ImmutableInstruction11x implements BuilderInstruction { + public BuilderInstruction11x(@Nonnull Opcode opcode, int registerA) { + super(opcode, registerA); + } + } + + public static class BuilderInstruction12x extends ImmutableInstruction12x implements BuilderInstruction { + public BuilderInstruction12x(@Nonnull Opcode opcode, int registerA, int registerB) { + super(opcode, registerA, registerB); + } + } + + public static class BuilderInstruction20bc extends BaseBuilderInstruction implements Instruction20bc { + public static final Format FORMAT = Format.Format20bc; + + protected final int verificationError; + @Nonnull protected final BuilderReference reference; + + public BuilderInstruction20bc(@Nonnull Opcode opcode, + int verificationError, + @Nonnull BuilderReference reference) { + super(opcode); + Preconditions.checkFormat(opcode, FORMAT); + this.verificationError = Preconditions.checkVerificationError(verificationError); + this.reference = Preconditions.checkReference(opcode.referenceType, reference); + } + + @Override public int getVerificationError() { return verificationError; } + @Nonnull @Override public BuilderReference getReference() { return reference; } + + @Nonnull @Override public Format getFormat() { return FORMAT; } + } + + public static class BuilderInstruction20t extends ImmutableInstruction20t implements BuilderInstruction { + public BuilderInstruction20t(@Nonnull Opcode opcode, int codeOffset) { + super(opcode, codeOffset); + } + } + + public static class BuilderInstruction21c extends BaseBuilderInstruction implements Instruction21c { + public static final Format FORMAT = Format.Format21c; + + protected final int registerA; + @Nonnull protected final BuilderReference reference; + + public BuilderInstruction21c(@Nonnull Opcode opcode, + int registerA, + @Nonnull BuilderReference reference) { + super(opcode); + Preconditions.checkFormat(opcode, FORMAT); + this.registerA = Preconditions.checkByteRegister(registerA); + this.reference = Preconditions.checkReference(opcode.referenceType, reference); + } + + @Override public int getRegisterA() { return registerA; } + @Nonnull @Override public BuilderReference getReference() { return reference; } + + @Nonnull @Override public Format getFormat() { return FORMAT; } + } + + public static class BuilderInstruction21ih extends ImmutableInstruction21ih implements BuilderInstruction { + public BuilderInstruction21ih(@Nonnull Opcode opcode, int registerA, int literal) { + super(opcode, registerA, literal); + } + } + + public static class BuilderInstruction21lh extends ImmutableInstruction21lh implements BuilderInstruction { + public BuilderInstruction21lh(@Nonnull Opcode opcode, int registerA, long literal) { + super(opcode, registerA, literal); + } + } + + public static class BuilderInstruction21s extends ImmutableInstruction21s implements BuilderInstruction { + public BuilderInstruction21s(@Nonnull Opcode opcode, int registerA, int literal) { + super(opcode, registerA, literal); + } + } + + public static class BuilderInstruction21t extends ImmutableInstruction21t implements BuilderInstruction { + public BuilderInstruction21t(@Nonnull Opcode opcode, int registerA, int codeOffset) { + super(opcode, registerA, codeOffset); + } + } + + public static class BuilderInstruction22b extends ImmutableInstruction22b implements BuilderInstruction { + public BuilderInstruction22b(@Nonnull Opcode opcode, int registerA, int registerB, int literal) { + super(opcode, registerA, registerB, literal); + } + } + + public static class BuilderInstruction22c extends BaseBuilderInstruction implements Instruction22c { + public static final Format FORMAT = Format.Format22c; + + protected final int registerA; + protected final int registerB; + @Nonnull protected final BuilderReference reference; + + public BuilderInstruction22c(@Nonnull Opcode opcode, + int registerA, + int registerB, + @Nonnull BuilderReference reference) { + super(opcode); + Preconditions.checkFormat(opcode, FORMAT); + this.registerA = Preconditions.checkNibbleRegister(registerA); + this.registerB = Preconditions.checkNibbleRegister(registerB); + this.reference = Preconditions.checkReference(opcode.referenceType, reference); + } + + @Override public int getRegisterA() { return registerA; } + @Override public int getRegisterB() { return registerB; } + @Nonnull @Override public BuilderReference getReference() { return reference; } + + @Nonnull @Override public Format getFormat() { return FORMAT; } + } + + public static class BuilderInstruction22s extends ImmutableInstruction22s implements BuilderInstruction { + public BuilderInstruction22s(@Nonnull Opcode opcode, int registerA, int registerB, int literal) { + super(opcode, registerA, registerB, literal); + } + } + + public static class BuilderInstruction22t extends ImmutableInstruction22t implements BuilderInstruction { + public BuilderInstruction22t(@Nonnull Opcode opcode, int registerA, int registerB, int codeOffset) { + super(opcode, registerA, registerB, codeOffset); + } + } + + public static class BuilderInstruction22x extends ImmutableInstruction22x implements BuilderInstruction { + public BuilderInstruction22x(@Nonnull Opcode opcode, int registerA, int registerB) { + super(opcode, registerA, registerB); + } + } + + public static class BuilderInstruction23x extends ImmutableInstruction23x implements BuilderInstruction { + public BuilderInstruction23x(@Nonnull Opcode opcode, int registerA, int registerB, int registerC) { + super(opcode, registerA, registerB, registerC); + } + } + + public static class BuilderInstruction30t extends ImmutableInstruction30t implements BuilderInstruction { + public BuilderInstruction30t(@Nonnull Opcode opcode, int codeOffset) { + super(opcode, codeOffset); + } + } + + public static class BuilderInstruction31c extends BaseBuilderInstruction implements Instruction31c { + public static final Format FORMAT = Format.Format31c; + + protected final int registerA; + @Nonnull protected final BuilderReference reference; + + public BuilderInstruction31c(@Nonnull Opcode opcode, + int registerA, + @Nonnull BuilderReference reference) { + super(opcode); + Preconditions.checkFormat(opcode, FORMAT); + this.registerA = Preconditions.checkByteRegister(registerA); + this.reference = Preconditions.checkReference(opcode.referenceType, reference); + } + + @Override public int getRegisterA() { return registerA; } + @Nonnull @Override public BuilderReference getReference() { return reference; } + + @Nonnull @Override public Format getFormat() { return FORMAT; } + } + + public static class BuilderInstruction31i extends ImmutableInstruction31i implements BuilderInstruction { + public BuilderInstruction31i(@Nonnull Opcode opcode, int registerA, int literal) { + super(opcode, registerA, literal); + } + } + + public static class BuilderInstruction31t extends ImmutableInstruction31t implements BuilderInstruction { + public BuilderInstruction31t(@Nonnull Opcode opcode, int registerA, int codeOffset) { + super(opcode, registerA, codeOffset); + } + } + + public static class BuilderInstruction32x extends ImmutableInstruction32x implements BuilderInstruction { + public BuilderInstruction32x(@Nonnull Opcode opcode, int registerA, int registerB) { + super(opcode, registerA, registerB); + } + } + + public static class BuilderInstruction35c extends BaseBuilderInstruction implements Instruction35c { + public static final Format FORMAT = Format.Format35c; + + protected final int registerCount; + protected final int registerC; + protected final int registerD; + protected final int registerE; + protected final int registerF; + protected final int registerG; + @Nonnull protected final BuilderReference reference; + + public BuilderInstruction35c(@Nonnull Opcode opcode, + int registerCount, + int registerC, + int registerD, + int registerE, + int registerF, + int registerG, + @Nonnull BuilderReference reference) { + super(opcode); + Preconditions.checkFormat(opcode, FORMAT); + this.registerCount = Preconditions.check35cRegisterCount(registerCount); + this.registerC = (registerCount>0) ? Preconditions.checkNibbleRegister(registerC) : 0; + this.registerD = (registerCount>1) ? Preconditions.checkNibbleRegister(registerD) : 0; + this.registerE = (registerCount>2) ? Preconditions.checkNibbleRegister(registerE) : 0; + this.registerF = (registerCount>3) ? Preconditions.checkNibbleRegister(registerF) : 0; + this.registerG = (registerCount>4) ? Preconditions.checkNibbleRegister(registerG) : 0; + this.reference = Preconditions.checkReference(opcode.referenceType, reference); + } + + @Override public int getRegisterCount() { return registerCount; } + @Override public int getRegisterC() { return registerC; } + @Override public int getRegisterD() { return registerD; } + @Override public int getRegisterE() { return registerE; } + @Override public int getRegisterF() { return registerF; } + @Override public int getRegisterG() { return registerG; } + @Nonnull @Override public BuilderReference getReference() { return reference; } + + @Nonnull @Override public Format getFormat() { return FORMAT; } + } + + public static class BuilderInstruction3rc extends BaseBuilderInstruction implements Instruction3rc { + public static final Format FORMAT = Format.Format3rc; + + private final int startRegister; + private final int registerCount; + + @Nonnull protected final BuilderReference reference; + + public BuilderInstruction3rc(@Nonnull Opcode opcode, + int startRegister, + int registerCount, + @Nonnull BuilderReference reference) { + super(opcode); + + Preconditions.checkFormat(opcode, FORMAT); + this.startRegister = Preconditions.checkShortRegister(startRegister); + this.registerCount = Preconditions.checkRegisterRangeCount(registerCount); + this.reference = Preconditions.checkReference(opcode.referenceType, reference); + } + + @Nonnull @Override public BuilderReference getReference() { + return reference; + } + + @Override public int getStartRegister() { + return startRegister; + } + + @Override public int getRegisterCount() { + return registerCount; + } + + @Nonnull @Override public Format getFormat() { + return FORMAT; + } + } + + public static class BuilderInstruction51l extends ImmutableInstruction51l implements BuilderInstruction { + public BuilderInstruction51l(@Nonnull Opcode opcode, int registerA, long literal) { + super(opcode, registerA, literal); + } + } + + public static class BuilderArrayPayload extends BaseBuilderInstruction implements ArrayPayload { + public static final Format FORMAT = Format.ArrayPayload; + private final int elementWidth; + @Nonnull private final List arrayElements; + + public BuilderArrayPayload(int elementWidth, @Nullable List arrayElements) { + super(Opcode.ARRAY_PAYLOAD); + this.elementWidth = elementWidth; + if (arrayElements == null) { + arrayElements = ImmutableList.of(); + } + this.arrayElements = arrayElements; + } + + @Override public int getElementWidth() { + return elementWidth; + } + + @Nonnull @Override public List getArrayElements() { + return arrayElements; + } + + @Nonnull @Override public Format getFormat() { + return FORMAT; + } + + @Override public int getCodeUnits() { + return 4 + (elementWidth * arrayElements.size() + 1) / 2; + } + } + + public static class BuilderPackedSwitchPayload extends BaseBuilderInstruction implements PackedSwitchPayload { + public static final Format FORMAT = Format.PackedSwitchPayload; + @Nonnull private final List elements; + + public BuilderPackedSwitchPayload(@Nullable List switchElements) { + super(Opcode.PACKED_SWITCH_PAYLOAD); + if (switchElements == null) { + switchElements = ImmutableList.of(); + } + this.elements = switchElements; + } + + @Nonnull @Override public List getSwitchElements() { + return elements; + } + + @Nonnull @Override public Format getFormat() { + return FORMAT; + } + + @Override public int getCodeUnits() { + return 4 + elements.size() * 2; + } + } + + public static class BuilderSparseSwitchPayload extends BaseBuilderInstruction implements SparseSwitchPayload { + public static final Format FORMAT = Format.SparseSwitchPayload; + @Nonnull private final List elements; + + public BuilderSparseSwitchPayload(@Nullable List switchElements) { + super(Opcode.SPARSE_SWITCH_PAYLOAD); + if (switchElements == null) { + switchElements = ImmutableList.of(); + } + this.elements = switchElements; + } + + @Nonnull @Override public List getSwitchElements() { + return elements; + } + + @Nonnull @Override public Format getFormat() { + return FORMAT; + } + + @Override public int getCodeUnits() { + return 2 + elements.size() * 4; + } + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderInstructionFactory.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderInstructionFactory.java new file mode 100644 index 00000000..349c7e23 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderInstructionFactory.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.writer.builder; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.instruction.SwitchElement; +import org.jf.dexlib2.writer.InstructionFactory; +import org.jf.dexlib2.writer.builder.BuilderInstruction.*; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public class BuilderInstructionFactory implements InstructionFactory { + public static final BuilderInstructionFactory INSTANCE = new BuilderInstructionFactory(); + + private BuilderInstructionFactory() { + } + + public BuilderInstruction10t makeInstruction10t(@Nonnull Opcode opcode, + int codeOffset) { + return new BuilderInstruction10t(opcode, codeOffset); + } + + public BuilderInstruction10x makeInstruction10x(@Nonnull Opcode opcode) { + return new BuilderInstruction10x(opcode); + } + + public BuilderInstruction11n makeInstruction11n(@Nonnull Opcode opcode, + int registerA, + int literal) { + return new BuilderInstruction11n(opcode, registerA, literal); + } + + public BuilderInstruction11x makeInstruction11x(@Nonnull Opcode opcode, + int registerA) { + return new BuilderInstruction11x(opcode, registerA); + } + + public BuilderInstruction12x makeInstruction12x(@Nonnull Opcode opcode, + int registerA, + int registerB) { + return new BuilderInstruction12x(opcode, registerA, registerB); + } + + public BuilderInstruction20bc makeInstruction20bc(@Nonnull Opcode opcode, + int verificationError, + @Nonnull BuilderReference reference) { + return new BuilderInstruction20bc(opcode, verificationError, reference); + } + + public BuilderInstruction20t makeInstruction20t(@Nonnull Opcode opcode, + int codeOffset) { + return new BuilderInstruction20t(opcode, codeOffset); + } + + public BuilderInstruction21c makeInstruction21c(@Nonnull Opcode opcode, + int registerA, + @Nonnull BuilderReference reference) { + return new BuilderInstruction21c(opcode, registerA, reference); + } + + public BuilderInstruction21ih makeInstruction21ih(@Nonnull Opcode opcode, + int registerA, + int literal) { + return new BuilderInstruction21ih(opcode, registerA, literal); + } + + public BuilderInstruction21lh makeInstruction21lh(@Nonnull Opcode opcode, + int registerA, + long literal) { + return new BuilderInstruction21lh(opcode, registerA, literal); + } + + public BuilderInstruction21s makeInstruction21s(@Nonnull Opcode opcode, + int registerA, + int literal) { + return new BuilderInstruction21s(opcode, registerA, literal); + } + + public BuilderInstruction21t makeInstruction21t(@Nonnull Opcode opcode, + int registerA, + int codeOffset) { + return new BuilderInstruction21t(opcode, registerA, codeOffset); + } + + public BuilderInstruction22b makeInstruction22b(@Nonnull Opcode opcode, + int registerA, + int registerB, + int literal) { + return new BuilderInstruction22b(opcode, registerA, registerB, literal); + } + + public BuilderInstruction22c makeInstruction22c(@Nonnull Opcode opcode, + int registerA, + int registerB, + @Nonnull BuilderReference reference) { + return new BuilderInstruction22c(opcode, registerA, registerB, reference); + } + + public BuilderInstruction22s makeInstruction22s(@Nonnull Opcode opcode, + int registerA, + int registerB, + int literal) { + return new BuilderInstruction22s(opcode, registerA, registerB, literal); + } + + public BuilderInstruction22t makeInstruction22t(@Nonnull Opcode opcode, + int registerA, + int registerB, + int codeOffset) { + return new BuilderInstruction22t(opcode, registerA, registerB, codeOffset); + } + + public BuilderInstruction22x makeInstruction22x(@Nonnull Opcode opcode, + int registerA, + int registerB) { + return new BuilderInstruction22x(opcode, registerA, registerB); + } + + public BuilderInstruction23x makeInstruction23x(@Nonnull Opcode opcode, + int registerA, + int registerB, + int registerC) { + return new BuilderInstruction23x(opcode, registerA, registerB, registerC); + } + + public BuilderInstruction30t makeInstruction30t(@Nonnull Opcode opcode, + int codeOffset) { + return new BuilderInstruction30t(opcode, codeOffset); + } + + public BuilderInstruction31c makeInstruction31c(@Nonnull Opcode opcode, + int registerA, + @Nonnull BuilderReference reference) { + return new BuilderInstruction31c(opcode, registerA, reference); + } + + public BuilderInstruction31i makeInstruction31i(@Nonnull Opcode opcode, + int registerA, + int literal) { + return new BuilderInstruction31i(opcode, registerA, literal); + } + + public BuilderInstruction31t makeInstruction31t(@Nonnull Opcode opcode, + int registerA, + int codeOffset) { + return new BuilderInstruction31t(opcode, registerA, codeOffset); + } + + public BuilderInstruction32x makeInstruction32x(@Nonnull Opcode opcode, + int registerA, + int registerB) { + return new BuilderInstruction32x(opcode, registerA, registerB); + } + + public BuilderInstruction35c makeInstruction35c(@Nonnull Opcode opcode, + int registerCount, + int registerC, + int registerD, + int registerE, + int registerF, + int registerG, + @Nonnull BuilderReference reference) { + return new BuilderInstruction35c(opcode, registerCount, registerC, registerD, registerE, registerF, registerG, + reference); + } + + public BuilderInstruction3rc makeInstruction3rc(@Nonnull Opcode opcode, + int startRegister, + int registerCount, + @Nonnull BuilderReference reference) { + return new BuilderInstruction3rc(opcode, startRegister, registerCount, reference); + } + + public BuilderInstruction51l makeInstruction51l(@Nonnull Opcode opcode, + int registerA, + long literal) { + return new BuilderInstruction51l(opcode, registerA, literal); + } + + public BuilderSparseSwitchPayload makeSparseSwitchPayload(@Nullable List switchElements) { + return new BuilderSparseSwitchPayload(switchElements); + } + + public BuilderPackedSwitchPayload makePackedSwitchPayload(@Nullable List switchElements) { + return new BuilderPackedSwitchPayload(switchElements); + } + + public BuilderArrayPayload makeArrayPayload(int elementWidth, + @Nullable List arrayElements) { + return new BuilderArrayPayload(elementWidth, arrayElements); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMapEntryCollection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMapEntryCollection.java new file mode 100644 index 00000000..aea95a6e --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMapEntryCollection.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.builder; + +import javax.annotation.Nonnull; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +public abstract class BuilderMapEntryCollection extends AbstractCollection> { + @Nonnull private final Collection keys; + + public BuilderMapEntryCollection(@Nonnull Collection keys) { + this.keys = keys; + } + + private class MapEntry implements Map.Entry { + @Nonnull private Key key; + + @Nonnull @Override public Key getKey() { + return key; + } + + @Override public Integer getValue() { + return BuilderMapEntryCollection.this.getValue(key); + } + + @Override public Integer setValue(Integer value) { + return BuilderMapEntryCollection.this.setValue(key, value); + } + } + + @Nonnull @Override public Iterator> iterator() { + final Iterator iter = keys.iterator(); + + return new Iterator>() { + @Override public boolean hasNext() { + return iter.hasNext(); + } + + @Override public Map.Entry next() { + MapEntry entry = new MapEntry(); + entry.key = iter.next(); + return entry; + } + + @Override public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override public int size() { + return keys.size(); + } + + protected abstract int getValue(@Nonnull Key key); + protected abstract int setValue(@Nonnull Key key, int value); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethod.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethod.java new file mode 100644 index 00000000..fea8fd0a --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethod.java @@ -0,0 +1,73 @@ +/* + * 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.builder; + +import org.jf.dexlib2.base.reference.BaseMethodReference; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.writer.DexWriter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public class BuilderMethod extends BaseMethodReference implements Method { + @Nonnull final BuilderMethodReference methodReference; + @Nonnull final List parameters; + final int accessFlags; + @Nonnull final BuilderAnnotationSet annotations; + @Nullable final BuilderMethodImplementation methodImplementation; + + int annotationSetRefListOffset = DexWriter.NO_OFFSET; + int codeItemOffset = DexWriter.NO_OFFSET; + int debugInfoOffset = DexWriter.NO_OFFSET; + + BuilderMethod(@Nonnull BuilderMethodReference methodReference, + @Nonnull List parameters, + int accessFlags, + @Nonnull BuilderAnnotationSet annotations, + @Nullable BuilderMethodImplementation methodImplementation) { + this.methodReference = methodReference; + this.parameters = parameters; + this.accessFlags = accessFlags; + this.annotations = annotations; + this.methodImplementation = methodImplementation; + } + + @Override @Nonnull public String getDefiningClass() { return methodReference.definingClass.getType(); } + @Override @Nonnull public String getName() { return methodReference.name.getString(); } + @Override @Nonnull public BuilderTypeList getParameterTypes() { return methodReference.proto.parameterTypes; } + @Nonnull @Override public String getReturnType() { return methodReference.proto.returnType.getType(); } + @Override @Nonnull public List getParameters() { return parameters; } + @Override public int getAccessFlags() { return accessFlags; } + @Override @Nonnull public BuilderAnnotationSet getAnnotations() { return annotations; } + @Override @Nullable public BuilderMethodImplementation getImplementation() { return methodImplementation; } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodImplementation.java new file mode 100644 index 00000000..5c901c02 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodImplementation.java @@ -0,0 +1,59 @@ +/* + * 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.builder; + +import org.jf.dexlib2.iface.MethodImplementation; + +import javax.annotation.Nonnull; +import java.util.List; + +public class BuilderMethodImplementation implements MethodImplementation { + protected final int registerCount; + @Nonnull protected final List instructions; + @Nonnull protected final List tryBlocks; + @Nonnull protected final List debugItems; + + public BuilderMethodImplementation(int registerCount, + @Nonnull List instructions, + @Nonnull List tryBlocks, + @Nonnull List debugItems) { + this.registerCount = registerCount; + this.instructions = instructions; + this.tryBlocks = tryBlocks; + this.debugItems = debugItems; + } + + @Override public int getRegisterCount() { return registerCount; } + @Nonnull @Override public List getInstructions() { return instructions; } + @Nonnull @Override public List getTryBlocks() { return tryBlocks; } + @Nonnull @Override public List getDebugItems() { return debugItems; } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodParameter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodParameter.java new file mode 100644 index 00000000..28586622 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodParameter.java @@ -0,0 +1,63 @@ +/* + * 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.builder; + +import org.jf.dexlib2.base.BaseMethodParameter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class BuilderMethodParameter extends BaseMethodParameter { + @Nonnull final BuilderTypeReference type; + @Nullable final BuilderStringReference name; + @Nonnull final BuilderAnnotationSet annotations; + + public BuilderMethodParameter(@Nonnull BuilderTypeReference type, + @Nullable BuilderStringReference name, + @Nonnull BuilderAnnotationSet annotations) { + this.type = type; + this.name = name; + this.annotations = annotations; + } + + @Nonnull @Override public String getType() { + return type.getType(); + } + + @Nullable @Override public String getName() { + return name==null?null:name.getString(); + } + + @Nonnull @Override public BuilderAnnotationSet getAnnotations() { + return annotations; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodPool.java new file mode 100644 index 00000000..7dc924e7 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodPool.java @@ -0,0 +1,145 @@ +/* + * 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.builder; + +import com.google.common.collect.Maps; +import org.jf.dexlib2.base.reference.BaseMethodReference; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.writer.MethodSection; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; + +class BuilderMethodPool implements MethodSection{ + @Nonnull private final BuilderContext context; + @Nonnull private final ConcurrentMap internedItems = + Maps.newConcurrentMap(); + + BuilderMethodPool(@Nonnull BuilderContext context) { + this.context = context; + } + + @Nonnull public BuilderMethodReference internMethod(@Nonnull MethodReference methodReference) { + BuilderMethodReference ret = internedItems.get(methodReference); + if (ret != null) { + return ret; + } + + BuilderMethodReference dexPoolMethodReference = new BuilderMethodReference( + context.typePool.internType(methodReference.getDefiningClass()), + context.stringPool.internString(methodReference.getName()), + context.protoPool.internProto(methodReference)); + ret = internedItems.putIfAbsent(dexPoolMethodReference, dexPoolMethodReference); + return ret==null?dexPoolMethodReference:ret; + } + + @Nonnull public BuilderMethodReference internMethod(@Nonnull String definingClass, @Nonnull String name, + @Nonnull List parameters, + @Nonnull String returnType) { + return internMethod(new MethodKey(definingClass, name, parameters, returnType)); + } + + @Nonnull @Override + public BuilderTypeReference getDefiningClass(@Nonnull BuilderMethodReference key) { + return key.definingClass; + } + + @Nonnull @Override + public BuilderProtoReference getPrototype(@Nonnull BuilderMethodReference key) { + return key.proto; + } + + @Nonnull @Override public BuilderProtoReference getPrototype(@Nonnull BuilderMethod builderMethod) { + return builderMethod.methodReference.proto; + } + + @Nonnull @Override public BuilderStringReference getName(@Nonnull BuilderMethodReference key) { + return key.name; + } + + @Override public int getMethodIndex(@Nonnull BuilderMethod builderMethod) { + return builderMethod.methodReference.index; + } + + @Override public int getItemIndex(@Nonnull BuilderMethodReference key) { + return key.index; + } + + @Nonnull @Override public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override protected int getValue(@Nonnull BuilderMethodReference key) { + return key.index; + } + + @Override protected int setValue(@Nonnull BuilderMethodReference key, int value) { + int prev = key.index; + key.index = value; + return prev; + } + }; + } + + private static class MethodKey extends BaseMethodReference implements MethodReference { + @Nonnull private final String definingClass; + @Nonnull private final String name; + @Nonnull private final List parameterTypes; + @Nonnull private final String returnType; + + public MethodKey(@Nonnull String definingClass, @Nonnull String name, + @Nonnull List parameterTypes, @Nonnull String returnType) { + this.definingClass = definingClass; + this.name = name; + this.parameterTypes = parameterTypes; + this.returnType = returnType; + } + + @Nonnull @Override public String getDefiningClass() { + return definingClass; + } + + @Nonnull @Override public String getName() { + return name; + } + + @Nonnull @Override public List getParameterTypes() { + return parameterTypes; + } + + @Nonnull @Override public String getReturnType() { + return returnType; + } + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodReference.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodReference.java new file mode 100644 index 00000000..c913efa5 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodReference.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.builder; + +import org.jf.dexlib2.base.reference.BaseMethodReference; +import org.jf.dexlib2.writer.DexWriter; + +import javax.annotation.Nonnull; + +public class BuilderMethodReference extends BaseMethodReference implements BuilderReference { + @Nonnull final BuilderTypeReference definingClass; + @Nonnull final BuilderStringReference name; + @Nonnull final BuilderProtoReference proto; + int index = DexWriter.NO_INDEX; + + BuilderMethodReference(@Nonnull BuilderTypeReference definingClass, + @Nonnull BuilderStringReference name, + @Nonnull BuilderProtoReference proto) { + this.definingClass = definingClass; + this.name = name; + this.proto = proto; + } + + @Nonnull @Override public String getDefiningClass() { + return definingClass.getType(); + } + + @Nonnull @Override public String getName() { + return this.name.getString(); + } + + @Nonnull @Override public BuilderTypeList getParameterTypes() { + return proto.parameterTypes; + } + + @Nonnull @Override public String getReturnType() { + return proto.returnType.getType(); + } + + @Override public int getIndex() { + return index; + } + + @Override public void setIndex(int index) { + this.index = index; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderProtoPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderProtoPool.java new file mode 100644 index 00000000..6ed18fe8 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderProtoPool.java @@ -0,0 +1,145 @@ +/* + * 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.builder; + +import com.google.common.collect.Maps; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.util.MethodUtil; +import org.jf.dexlib2.writer.ProtoSection; +import org.jf.util.CharSequenceUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; + +class BuilderProtoPool + implements ProtoSection { + @Nonnull private final BuilderContext context; + @Nonnull private final ConcurrentMap internedItems = + Maps.newConcurrentMap(); + + BuilderProtoPool(@Nonnull BuilderContext context) { + this.context = context; + } + + @Nonnull public BuilderProtoReference internProto(@Nonnull List parameters, + @Nonnull String returnType) { + ProtoKey key = new Key(parameters, returnType); + BuilderProtoReference ret = internedItems.get(key); + if (ret != null) { + return ret; + } + + BuilderProtoReference protoReference = new BuilderProtoReference( + context.stringPool.internString(MethodUtil.getShorty(parameters, returnType)), + context.typeListPool.internTypeList(parameters), + context.typePool.internType(returnType)); + ret = internedItems.putIfAbsent(protoReference, protoReference); + return ret==null?protoReference:ret; + } + + @Nonnull public BuilderProtoReference internProto(@Nonnull MethodReference methodReference) { + return internProto(methodReference.getParameterTypes(), methodReference.getReturnType()); + } + + @Nonnull @Override public BuilderStringReference getShorty(@Nonnull BuilderProtoReference key) { + return key.shorty; + } + + @Nonnull @Override public BuilderTypeReference getReturnType(@Nonnull BuilderProtoReference key) { + return key.returnType; + } + + @Nullable @Override public BuilderTypeList getParameters(@Nonnull BuilderProtoReference key) { + return key.parameterTypes; + } + + @Override public int getItemIndex(@Nonnull BuilderProtoReference key) { + return key.index; + } + + @Nonnull @Override public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override protected int getValue(@Nonnull BuilderProtoReference key) { + return key.index; + } + + @Override protected int setValue(@Nonnull BuilderProtoReference key, int value) { + int prev = key.index; + key.index = value; + return prev; + } + }; + } + + // a placeholder interface to unify the temporary probing key and the BuilderProtoReference class + interface ProtoKey { + @Nonnull List getParameterTypes(); + @Nonnull String getReturnType(); + } + + // a temporary lightweight class to allow a quick probe if the given prototype has already been interned + private static class Key implements ProtoKey { + @Nonnull private final List parameters; + @Nonnull private final String returnType; + + public Key(@Nonnull List parameters, @Nonnull String returnType) { + this.parameters = parameters; + this.returnType = returnType; + } + + @Nonnull public List getParameterTypes() { + return parameters; + } + + @Nonnull public String getReturnType() { + return returnType; + } + + @Override public int hashCode() { + int hashCode = returnType.hashCode(); + return hashCode*31 + parameters.hashCode(); + } + + @Override public boolean equals(Object o) { + if (o != null && o instanceof ProtoKey) { + ProtoKey other = (ProtoKey)o; + return getReturnType().equals(other.getReturnType()) && + CharSequenceUtils.listEquals(getParameterTypes(), other.getParameterTypes()); + } + return false; + } + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderProtoReference.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderProtoReference.java new file mode 100644 index 00000000..ef45229e --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderProtoReference.java @@ -0,0 +1,86 @@ +/* + * 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.builder; + +import com.google.common.collect.Ordering; +import org.jf.dexlib2.writer.DexWriter; +import org.jf.util.CharSequenceUtils; +import org.jf.util.CollectionUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public class BuilderProtoReference implements BuilderProtoPool.ProtoKey, Comparable { + @Nonnull final BuilderStringReference shorty; + @Nonnull final BuilderTypeList parameterTypes; + @Nonnull final BuilderTypeReference returnType; + int index = DexWriter.NO_INDEX; + + public BuilderProtoReference(@Nonnull BuilderStringReference shorty, @Nonnull BuilderTypeList parameterTypes, + @Nonnull BuilderTypeReference returnType) { + this.shorty = shorty; + this.parameterTypes = parameterTypes; + this.returnType = returnType; + } + + @Nonnull @Override public List getParameterTypes() { + return parameterTypes; + } + + @Nonnull @Override public String getReturnType() { + return returnType.getType(); + } + + @Override + public int hashCode() { + int hashCode = getReturnType().hashCode(); + return hashCode*31 + getParameterTypes().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o != null && o instanceof BuilderProtoReference) { + BuilderProtoReference other = (BuilderProtoReference)o; + return returnType.equals(other.returnType) && + CharSequenceUtils.listEquals(parameterTypes, other.parameterTypes); + } + return false; + } + + @Override + public int compareTo(@Nonnull BuilderProtoReference o) { + int res = returnType.compareTo(o.returnType); + if (res != 0) return res; + return CollectionUtils.compareAsIterable(Ordering.usingToString(), parameterTypes, o.parameterTypes); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderReference.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderReference.java new file mode 100644 index 00000000..27ad5afb --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderReference.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.builder; + +import org.jf.dexlib2.iface.reference.Reference; + +public interface BuilderReference extends Reference { + int getIndex(); + void setIndex(int index); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderStringPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderStringPool.java new file mode 100644 index 00000000..ea1568e7 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderStringPool.java @@ -0,0 +1,86 @@ +/* + * 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.builder; + +import com.google.common.collect.Maps; +import org.jf.dexlib2.writer.DexWriter; +import org.jf.dexlib2.writer.StringSection; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; + +class BuilderStringPool implements StringSection { + @Nonnull private final ConcurrentMap internedItems = Maps.newConcurrentMap(); + + @Nonnull BuilderStringReference internString(@Nonnull String string) { + BuilderStringReference ret = internedItems.get(string); + if (ret != null) { + return ret; + } + BuilderStringReference stringReference = new BuilderStringReference(string); + ret = internedItems.putIfAbsent(string, stringReference); + return ret==null?stringReference:ret; + } + + @Nullable BuilderStringReference internNullableString(@Nullable String string) { + if (string == null) { + return null; + } + return internString(string); + } + + @Override public int getNullableItemIndex(@Nullable BuilderStringReference key) { + return key==null?DexWriter.NO_INDEX:key.index; + } + + @Override public int getItemIndex(@Nonnull BuilderStringReference key) { + return key.index; + } + + @Nonnull @Override public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override protected int getValue(@Nonnull BuilderStringReference key) { + // TODO: see what the performance of using key.getIndex() for everything is like + return key.index; + } + + @Override protected int setValue(@Nonnull BuilderStringReference key, int value) { + int prev = key.index; + key.index = value; + return prev; + } + }; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderStringReference.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderStringReference.java new file mode 100644 index 00000000..eddb4a62 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderStringReference.java @@ -0,0 +1,58 @@ +/* + * 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.builder; + +import org.jf.dexlib2.base.reference.BaseStringReference; +import org.jf.dexlib2.writer.DexWriter; + +import javax.annotation.Nonnull; + +public class BuilderStringReference extends BaseStringReference implements BuilderReference { + @Nonnull final String string; + int index = DexWriter.NO_INDEX; + + BuilderStringReference(@Nonnull String string) { + this.string = string; + } + + @Nonnull @Override public String getString() { + return string; + } + + @Override public int getIndex() { + return index; + } + + @Override public void setIndex(int index) { + this.index = index; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTryBlock.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTryBlock.java new file mode 100644 index 00000000..178357ea --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTryBlock.java @@ -0,0 +1,62 @@ +/* + * 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.builder; + +import org.jf.dexlib2.base.BaseTryBlock; + +import javax.annotation.Nonnull; +import java.util.List; + +public class BuilderTryBlock extends BaseTryBlock { + private final int startCodeAddress; + private final int codeUnitCount; + @Nonnull private final List exceptionHandlers; + + public BuilderTryBlock(int startCodeAddress, int codeUnitCount, + @Nonnull List exceptionHandlers) { + this.startCodeAddress = startCodeAddress; + this.codeUnitCount = codeUnitCount; + this.exceptionHandlers = exceptionHandlers; + } + + @Override public int getStartCodeAddress() { + return startCodeAddress; + } + + @Override public int getCodeUnitCount() { + return codeUnitCount; + } + + @Nonnull @Override public List getExceptionHandlers() { + return exceptionHandlers; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeList.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeList.java new file mode 100644 index 00000000..87f7115f --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeList.java @@ -0,0 +1,66 @@ +/* + * 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.builder; + +import com.google.common.collect.ImmutableList; +import org.jf.dexlib2.writer.DexWriter; + +import javax.annotation.Nonnull; +import java.util.AbstractList; +import java.util.List; + +public class BuilderTypeList extends AbstractList { + static final BuilderTypeList EMPTY = new BuilderTypeList(ImmutableList.of()); + + @Nonnull final List types; + int offset = DexWriter.NO_OFFSET; + + public BuilderTypeList(@Nonnull List types) { + this.types = types; + } + + @Override public BuilderTypeReference get(int index) { + return types.get(index); + } + + @Override public int size() { + return types.size(); + } + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeListPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeListPool.java new file mode 100644 index 00000000..f8818f7e --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeListPool.java @@ -0,0 +1,104 @@ +/* + * 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.builder; + +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 org.jf.dexlib2.writer.DexWriter; +import org.jf.dexlib2.writer.TypeListSection; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; + +class BuilderTypeListPool implements TypeListSection { + @Nonnull private final BuilderContext context; + @Nonnull private final ConcurrentMap, BuilderTypeList> internedItems = + Maps.newConcurrentMap(); + + BuilderTypeListPool(@Nonnull BuilderContext context) { + this.context = context; + } + + @Nonnull public BuilderTypeList internTypeList(@Nullable List types) { + if (types == null) { + return BuilderTypeList.EMPTY; + } + + BuilderTypeList ret = internedItems.get(types); + if (ret != null) { + return ret; + } + + BuilderTypeList typeList = new BuilderTypeList( + ImmutableList.copyOf(Iterables.transform(types, new Function() { + @Nonnull @Override public BuilderTypeReference apply(CharSequence input) { + return context.typePool.internType(input.toString()); + } + }))); + + ret = internedItems.putIfAbsent(typeList, typeList); + return ret==null?typeList:ret; + } + + @Override public int getNullableItemOffset(@Nullable BuilderTypeList key) { + return key==null?DexWriter.NO_OFFSET:key.offset; + } + + @Nonnull @Override + public Collection getTypes(@Nullable BuilderTypeList key) { + return key==null?BuilderTypeList.EMPTY:key.types; + } + + @Override public int getItemOffset(@Nonnull BuilderTypeList key) { + return key.offset; + } + + @Nonnull @Override public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override protected int getValue(@Nonnull BuilderTypeList key) { + return key.offset; + } + + @Override protected int setValue(@Nonnull BuilderTypeList key, int value) { + int prev = key.offset; + key.offset = value; + return prev; + } + }; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypePool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypePool.java new file mode 100644 index 00000000..29871fc9 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypePool.java @@ -0,0 +1,95 @@ +/* + * 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.builder; + +import com.google.common.collect.Maps; +import org.jf.dexlib2.writer.DexWriter; +import org.jf.dexlib2.writer.TypeSection; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; + +class BuilderTypePool implements TypeSection { + @Nonnull private final BuilderContext context; + @Nonnull private final ConcurrentMap internedItems = Maps.newConcurrentMap(); + + BuilderTypePool(@Nonnull BuilderContext context) { + this.context = context; + } + + @Nonnull public BuilderTypeReference internType(@Nonnull String type) { + BuilderTypeReference ret = internedItems.get(type); + if (ret != null) { + return ret; + } + BuilderStringReference stringRef = context.stringPool.internString(type); + BuilderTypeReference typeReference = new BuilderTypeReference(stringRef); + ret = internedItems.putIfAbsent(type, typeReference); + return ret==null?typeReference:ret; + } + + @Nullable public BuilderTypeReference internNullableType(@Nullable String type) { + if (type == null) { + return null; + } + return internType(type); + } + + @Nonnull @Override public BuilderStringReference getString(@Nonnull BuilderTypeReference key) { + return key.stringReference; + } + + @Override public int getNullableItemIndex(@Nullable BuilderTypeReference key) { + return key==null?DexWriter.NO_INDEX:key.index; + } + + @Override public int getItemIndex(@Nonnull BuilderTypeReference key) { + return key.getIndex(); + } + + @Nonnull @Override public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override protected int getValue(@Nonnull BuilderTypeReference key) { + return key.index; + } + + @Override protected int setValue(@Nonnull BuilderTypeReference key, int value) { + int prev = key.index; + key.index = value; + return prev; + } + }; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeReference.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeReference.java new file mode 100644 index 00000000..c6561bf4 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderTypeReference.java @@ -0,0 +1,58 @@ +/* + * 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.builder; + +import org.jf.dexlib2.base.reference.BaseTypeReference; +import org.jf.dexlib2.writer.DexWriter; + +import javax.annotation.Nonnull; + +public class BuilderTypeReference extends BaseTypeReference implements BuilderReference { + @Nonnull final BuilderStringReference stringReference; + int index = DexWriter.NO_INDEX; + + BuilderTypeReference(@Nonnull BuilderStringReference stringReference) { + this.stringReference = stringReference; + } + + @Nonnull @Override public String getType() { + return stringReference.getString(); + } + + @Override public int getIndex() { + return index; + } + + @Override public void setIndex(int index) { + this.index = index; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java new file mode 100644 index 00000000..c5777eee --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java @@ -0,0 +1,307 @@ +/* + * 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.builder; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.MethodParameter; +import org.jf.dexlib2.iface.reference.*; +import org.jf.dexlib2.iface.value.*; +import org.jf.dexlib2.writer.DexWriter; +import org.jf.dexlib2.writer.builder.BuilderDebugItem.*; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.*; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +public class DexBuilder extends DexWriter { + + private final BuilderContext context; + + public static DexBuilder makeDexBuilder() { + BuilderContext context = new BuilderContext(); + return new DexBuilder(context); + } + + private DexBuilder(@Nonnull BuilderContext context) { + super(BuilderInstructionFactory.INSTANCE, context.stringPool, context.typePool, context.protoPool, + context.fieldPool, context.methodPool, context.classPool, context.typeListPool, context.annotationPool, + context.annotationSetPool); + this.context = context; + } + + @Nonnull public BuilderField internField(@Nonnull String definingClass, + @Nonnull String name, + @Nonnull String type, + int accessFlags, + @Nullable EncodedValue initialValue, + @Nonnull Set annotations) { + return new BuilderField(context.fieldPool.internField(definingClass, name, type), + accessFlags, + context.internNullableEncodedValue(initialValue), + context.annotationSetPool.internAnnotationSet(annotations)); + } + + @Nonnull public BuilderMethod internMethod(@Nonnull String definingClass, + @Nonnull String name, + @Nullable List parameters, + @Nonnull String returnType, + int accessFlags, + @Nonnull Set annotations, + @Nullable BuilderMethodImplementation methodImplementation) { + if (parameters == null) { + parameters = ImmutableList.of(); + } + return new BuilderMethod(context.methodPool.internMethod(definingClass, name, parameters, returnType), + internMethodParameters(parameters), + accessFlags, + context.annotationSetPool.internAnnotationSet(annotations), + methodImplementation); + } + + @Nonnull public BuilderMethodImplementation internMethodImplementation( + int registerCount, + @Nullable List instructions, + @Nullable List tryBlocks, + @Nullable List debugItems) { + if (instructions == null) { + instructions = ImmutableList.of(); + } + + if (tryBlocks == null) { + tryBlocks = ImmutableList.of(); + } + + if (debugItems == null) { + debugItems = ImmutableList.of(); + } + + return new BuilderMethodImplementation(registerCount, instructions, tryBlocks, debugItems); + } + + @Nonnull public BuilderClassDef internClassDef(@Nonnull String type, + int accessFlags, + @Nullable String superclass, + @Nullable List interfaces, + @Nullable String sourceFile, + @Nonnull Set annotations, + @Nullable Iterable fields, + @Nullable Iterable methods) { + if (interfaces == null) { + interfaces = ImmutableList.of(); + } else { + interfaces = Lists.newArrayList(interfaces); + Collections.sort(interfaces); + String prev = null; + Iterator interfaceIterator = interfaces.iterator(); + while (interfaceIterator.hasNext()) { + String iface = interfaceIterator.next(); + if (prev != null && iface.equals(prev)) { + interfaceIterator.remove(); + } + prev = iface; + } + } + + return context.classPool.internClass(new BuilderClassDef(context.typePool.internType(type), + accessFlags, + context.typePool.internNullableType(superclass), + context.typeListPool.internTypeList(interfaces), + context.stringPool.internNullableString(sourceFile), + context.annotationSetPool.internAnnotationSet(annotations), + fields, + methods)); + } + + @Nonnull public BuilderStringReference internStringReference(@Nonnull String string) { + return context.stringPool.internString(string); + } + + @Nonnull public BuilderTypeReference internTypeReference(@Nonnull String type) { + return context.typePool.internType(type); + } + + @Nonnull public BuilderFieldReference internFieldReference(@Nonnull FieldReference field) { + return context.fieldPool.internField(field); + } + + @Nonnull public BuilderMethodReference internMethodReference(@Nonnull MethodReference method) { + return context.methodPool.internMethod(method); + } + + @Nonnull public BuilderReference internReference(@Nonnull Reference reference) { + if (reference instanceof StringReference) { + return internStringReference(((StringReference)reference).getString()); + } + if (reference instanceof TypeReference) { + return internTypeReference(((TypeReference)reference).getType()); + } + if (reference instanceof MethodReference) { + return internMethodReference((MethodReference)reference); + } + if (reference instanceof FieldReference) { + return internFieldReference((FieldReference)reference); + } + throw new IllegalArgumentException("Could not determine type of reference"); + } + + @Nonnull private List internMethodParameters( + @Nullable List methodParameters) { + if (methodParameters == null) { + return ImmutableList.of(); + } + return ImmutableList.copyOf(Iterators.transform(methodParameters.iterator(), + new Function() { + @Nullable @Override public BuilderMethodParameter apply(MethodParameter input) { + return internMethodParameter(input); + } + })); + } + + @Nonnull private BuilderMethodParameter internMethodParameter(@Nonnull MethodParameter methodParameter) { + return new BuilderMethodParameter( + context.typePool.internType(methodParameter.getType()), + context.stringPool.internNullableString(methodParameter.getName()), + context.annotationSetPool.internAnnotationSet(methodParameter.getAnnotations())); + } + + @Nonnull public BuilderExceptionHandler internExceptionHandler(@Nullable String exceptionType, + int handlerCodeAddress) { + return new BuilderExceptionHandler(context.typePool.internNullableType(exceptionType), + handlerCodeAddress); + } + + @Nonnull public BuilderStartLocal internStartLocal(int codeAddress, int register, @Nullable String name, + @Nullable String type, @Nullable String signature) { + return new BuilderStartLocal(codeAddress, + register, + context.stringPool.internNullableString(name), + context.typePool.internNullableType(type), + context.stringPool.internNullableString(signature)); + } + + @Nonnull public BuilderSetSourceFile internSetSourceFile(int codeAddress, @Nullable String sourceFile) { + return new BuilderSetSourceFile(codeAddress, + context.stringPool.internNullableString(sourceFile)); + } + + @Nonnull public BuilderEndLocal internEndLocal(int codeAddress, int register) { + return new BuilderEndLocal(codeAddress, register); + } + + @Nonnull public BuilderRestartLocal internRestartLocal(int codeAddress, int register) { + return new BuilderRestartLocal(codeAddress, register); + } + + @Nonnull public BuilderPrologueEnd internPrologueEnd(int codeAddress) { + return new BuilderPrologueEnd(codeAddress); + } + + @Nonnull public BuilderEpilogueBegin internEpilogueBegin(int codeAddress) { + return new BuilderEpilogueBegin(codeAddress); + } + + @Nonnull public BuilderLineNumber internLineNumber(int codeAddress, int lineNumber) { + return new BuilderLineNumber(codeAddress, lineNumber); + } + + @Override protected void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer, + @Nonnull BuilderEncodedValue encodedValue) throws IOException { + switch (encodedValue.getValueType()) { + case ValueType.ANNOTATION: + BuilderAnnotationEncodedValue annotationEncodedValue = (BuilderAnnotationEncodedValue)encodedValue; + writer.writeAnnotation(annotationEncodedValue.typeReference, annotationEncodedValue.elements); + break; + case ValueType.ARRAY: + BuilderArrayEncodedValue arrayEncodedValue = (BuilderArrayEncodedValue)encodedValue; + writer.writeArray(arrayEncodedValue.elements); + 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(((BuilderEnumEncodedValue)encodedValue).getValue()); + break; + case ValueType.FIELD: + writer.writeField(((BuilderFieldEncodedValue)encodedValue).fieldReference); + 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(((BuilderMethodEncodedValue)encodedValue).methodReference); + break; + case ValueType.NULL: + writer.writeNull(); + break; + case ValueType.SHORT: + writer.writeShort(((ShortEncodedValue)encodedValue).getValue()); + break; + case ValueType.STRING: + writer.writeString(((BuilderStringEncodedValue)encodedValue).stringReference); + break; + case ValueType.TYPE: + writer.writeType(((BuilderTypeEncodedValue)encodedValue).typeReference); + break; + default: + throw new ExceptionWithContext("Unrecognized value type: %d", encodedValue.getValueType()); + } + } +} 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 index 854cd8d5..3b1674ab 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/DexPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/DexPool.java @@ -35,14 +35,11 @@ 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.reference.*; 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.dexlib2.writer.pool.ProtoPool.Key; import org.jf.util.ExceptionWithContext; import javax.annotation.Nonnull; @@ -51,7 +48,7 @@ import java.util.Collection; import java.util.Set; public class DexPool extends DexWriter, TypeListPool.Key>, Field, PoolMethod, EncodedValue, AnnotationElement, DebugItem, Instruction, ExceptionHandler> { 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 index 6fdda28b..3403d12e 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/FieldPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/FieldPool.java @@ -31,13 +31,14 @@ package org.jf.dexlib2.writer.pool; +import org.jf.dexlib2.iface.Field; import org.jf.dexlib2.iface.reference.FieldReference; import org.jf.dexlib2.writer.FieldSection; import javax.annotation.Nonnull; public class FieldPool extends BaseIndexPool - implements FieldSection { + implements FieldSection { @Nonnull private final StringPool stringPool; @Nonnull private final TypePool typePool; @@ -66,4 +67,8 @@ public class FieldPool extends BaseIndexPool @Nonnull @Override public CharSequence getName(@Nonnull FieldReference fieldReference) { return fieldReference.getName(); } + + @Override public int getFieldIndex(@Nonnull Field field) { + return getItemIndex(field); + } } 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 index 3ea5fc6c..7ae42fb6 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/MethodPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/MethodPool.java @@ -37,7 +37,7 @@ import org.jf.dexlib2.writer.MethodSection; import javax.annotation.Nonnull; public class MethodPool extends BaseIndexPool - implements MethodSection { + implements MethodSection { @Nonnull private final StringPool stringPool; @Nonnull private final TypePool typePool; @Nonnull private final ProtoPool protoPool; @@ -66,7 +66,15 @@ public class MethodPool extends BaseIndexPool return new ProtoPool.Key(methodReference); } + @Nonnull @Override public ProtoPool.Key getPrototype(@Nonnull PoolMethod poolMethod) { + return new ProtoPool.Key(poolMethod); + } + @Nonnull @Override public CharSequence getName(@Nonnull MethodReference methodReference) { return methodReference.getName(); } + + @Override public int getMethodIndex(@Nonnull PoolMethod poolMethod) { + return getItemIndex(poolMethod); + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ProtoPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ProtoPool.java index bb7c265f..eeabdf4a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ProtoPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ProtoPool.java @@ -33,6 +33,7 @@ package org.jf.dexlib2.writer.pool; import com.google.common.collect.Ordering; import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.util.MethodUtil; import org.jf.dexlib2.writer.pool.ProtoPool.Key; import org.jf.dexlib2.writer.ProtoSection; import org.jf.util.CharSequenceUtils; @@ -80,22 +81,6 @@ public class ProtoPool extends BaseIndexPool return new TypeListPool.Key>(key.getParameters()); } - private static char getShortyType(CharSequence type) { - if (type.length() > 1) { - return 'L'; - } - return type.charAt(0); - } - - 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 sb.toString(); - } - public static class Key implements Comparable { @Nonnull private final MethodReference method; @@ -109,7 +94,7 @@ public class ProtoPool extends BaseIndexPool } public String getShorty() { - return ProtoPool.getShorty(method.getParameterTypes(), method.getReturnType()); + return MethodUtil.getShorty(method.getParameterTypes(), method.getReturnType()); } public String toString() { 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 7d262e0d..df5d8529 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 @@ -52,9 +52,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -public class InstructionWriteUtil { +public class InstructionWriteUtil { private final StringIndexProvider stringIndexProvider; - private final InstructionFactory instructionFactory; + private final InstructionFactory instructionFactory; private final Iterable originalInstructions; private List instructions; @@ -70,7 +71,7 @@ public class InstructionWriteUtil instructions, @Nonnull StringIndexProvider stringIndexProvider, - @Nonnull InstructionFactory instructionFactory) { + @Nonnull InstructionFactory instructionFactory) { this.stringIndexProvider = stringIndexProvider; this.instructionFactory = instructionFactory; this.originalInstructions = instructions; @@ -271,7 +272,7 @@ public class InstructionWriteUtil 0xFFFF) { modifiedInstruction = instructionFactory.makeInstruction31c(Opcode.CONST_STRING_JUMBO, - instr.getRegisterA(), instr.getReference()); + instr.getRegisterA(), (BaseReference)instr.getReference()); } } break; 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 35d845a7..d77f1117 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java @@ -38,6 +38,7 @@ 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.Reference; import org.jf.dexlib2.iface.reference.StringReference; import org.jf.dexlib2.immutable.ImmutableMethodImplementation; import org.jf.dexlib2.immutable.instruction.*; @@ -55,7 +56,7 @@ public class JumboStringConversionTest { private MockStringIndexProvider mockStringIndexProvider; ArrayList mJumboStrings; - private class InsnWriteUtil extends InstructionWriteUtil { + private class InsnWriteUtil extends InstructionWriteUtil { public InsnWriteUtil(@Nonnull MethodImplementation implementation) { super(implementation.getInstructions(), mockStringIndexProvider, ImmutableInstructionFactory.INSTANCE); } diff --git a/smali/src/main/antlr3/smaliTreeWalker.g b/smali/src/main/antlr3/smaliTreeWalker.g index 22b332bb..cc68073e 100644 --- a/smali/src/main/antlr3/smaliTreeWalker.g +++ b/smali/src/main/antlr3/smaliTreeWalker.g @@ -59,10 +59,10 @@ import org.jf.dexlib2.immutable.debug.*; import org.jf.dexlib2.immutable.instruction.*; import org.jf.dexlib2.immutable.reference.*; import org.jf.dexlib2.immutable.value.*; +import org.jf.dexlib2.writer.InstructionFactory; +import org.jf.dexlib2.writer.builder.*; import org.jf.dexlib2.util.MethodUtil; import org.jf.util.LinearSearch; - - } @members { @@ -70,6 +70,13 @@ import org.jf.util.LinearSearch; private boolean verboseErrors = false; private int apiLevel = 15; private Opcodes opcodes = new Opcodes(apiLevel); + private DexBuilder dexBuilder; + private InstructionFactory instructionFactory = + BuilderInstructionFactory.INSTANCE; + + public void setDexBuilder(DexBuilder dexBuilder) { + this.dexBuilder = dexBuilder; + } public void setApiLevel(int apiLevel) { this.opcodes = new Opcodes(apiLevel); @@ -146,7 +153,7 @@ import org.jf.util.LinearSearch; smali_file returns[ClassDef classDef] : ^(I_CLASS_DEF header methods fields annotations) { - $classDef = new ImmutableClassDef($header.classType, $header.accessFlags, $header.superType, + $classDef = dexBuilder.internClassDef($header.classType, $header.accessFlags, $header.superType, $header.implementsList, $header.sourceSpec, $annotations.annotations, $fields.fields, $methods.methods); }; catch [Exception ex] { @@ -220,7 +227,7 @@ access_list returns [int value] )*); -fields returns[List fields] +fields returns[List fields] @init {$fields = Lists.newArrayList();} : ^(I_FIELDS (field @@ -228,7 +235,7 @@ fields returns[List fields] $fields.add($field.field); })*); -methods returns[List methods] +methods returns[List methods] @init {$methods = Lists.newArrayList();} : ^(I_METHODS (method @@ -236,7 +243,7 @@ methods returns[List methods] $methods.add($method.ret); })*); -field returns [Field field] +field returns [BuilderField field] :^(I_FIELD SIMPLE_NAME access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) field_initial_value annotations?) { int accessFlags = $access_list.value; @@ -246,7 +253,7 @@ field returns [Field field] throw new SemanticException(input, "Initial field values can only be specified for static fields."); } - $field = new ImmutableField(classType, $SIMPLE_NAME.text, $nonvoid_type_descriptor.type, $access_list.value, + $field = dexBuilder.internField(classType, $SIMPLE_NAME.text, $nonvoid_type_descriptor.type, $access_list.value, $field_initial_value.encodedValue, $annotations.annotations); }; @@ -337,7 +344,7 @@ sparse_switch_elements[int baseAddress] returns[List elements] })* ); -method returns[Method ret] +method returns[BuilderMethod ret] scope { HashMap labels; @@ -388,10 +395,10 @@ method returns[Method ret] annotations ) { - List> tryBlocks = $catches.tryBlocks; - List debugItems = $ordered_debug_directives.debugItems; + List tryBlocks = $catches.tryBlocks; + List debugItems = $ordered_debug_directives.debugItems; - MethodImplementation methodImplementation = null; + BuilderMethodImplementation methodImplementation = null; boolean isAbstract = false; boolean isNative = false; @@ -451,14 +458,14 @@ method returns[Method ret] " registers, for the method parameters"); } - methodImplementation = new ImmutableMethodImplementation( + methodImplementation = dexBuilder.internMethodImplementation( $method::totalMethodRegisters, $statements.instructions, tryBlocks, debugItems); } - $ret = new ImmutableMethod( + $ret = dexBuilder.internMethod( classType, $method_name_and_prototype.name, $method_name_and_prototype.parameters, @@ -568,12 +575,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[BuilderTryBlock 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]) { @@ -584,11 +591,11 @@ catch_directive returns[TryBlock tryBlock] // We always create try blocks with a single exception handler. These will be merged appropriately when written // to a dex file - $tryBlock = new ImmutableTryBlock(startAddress, endAddress-startAddress, - ImmutableList.of(new ImmutableExceptionHandler(type, handlerAddress))); + $tryBlock = new BuilderTryBlock(startAddress, endAddress-startAddress, + ImmutableList.of(dexBuilder.internExceptionHandler(type, handlerAddress))); }; -catchall_directive returns[TryBlock tryBlock] +catchall_directive returns[BuilderTryBlock 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]) { @@ -598,8 +605,8 @@ catchall_directive returns[TryBlock tryBlock] // We always create try blocks with a single exception handler. These will be merged appropriately when written // to a dex file - $tryBlock = new ImmutableTryBlock(startAddress, endAddress-startAddress, - ImmutableList.of(new ImmutableExceptionHandler(null, handlerAddress))); + $tryBlock = new BuilderTryBlock(startAddress, endAddress-startAddress, + ImmutableList.of(dexBuilder.internExceptionHandler(null, handlerAddress))); }; address returns[int address] @@ -645,7 +652,7 @@ parameter[List parameters] } }; -ordered_debug_directives returns[List debugItems] +ordered_debug_directives returns[List debugItems] @init {debugItems = Lists.newArrayList();} : ^(I_ORDERED_DEBUG_DIRECTIVES ( line { $debugItems.add($line.debugItem); } @@ -658,56 +665,56 @@ ordered_debug_directives returns[List debugItems] )* ); -line returns[DebugItem debugItem] +line returns[BuilderDebugItem debugItem] : ^(I_LINE integral_literal address) { - $debugItem = new ImmutableLineNumber($address.address, $integral_literal.value); + $debugItem = dexBuilder.internLineNumber($address.address, $integral_literal.value); }; -local returns[DebugItem debugItem] +local returns[BuilderDebugItem debugItem] : ^(I_LOCAL REGISTER ((NULL_LITERAL | name=string_literal) nonvoid_type_descriptor? signature=string_literal?)? address) { int registerNumber = parseRegister_short($REGISTER.text); - $debugItem = new ImmutableStartLocal($address.address, registerNumber, $name.value, + $debugItem = dexBuilder.internStartLocal($address.address, registerNumber, $name.value, $nonvoid_type_descriptor.type, $signature.value); }; -end_local returns[DebugItem debugItem] +end_local returns[BuilderDebugItem debugItem] : ^(I_END_LOCAL REGISTER address) { int registerNumber = parseRegister_short($REGISTER.text); - $debugItem = new ImmutableEndLocal($address.address, registerNumber); + $debugItem = dexBuilder.internEndLocal($address.address, registerNumber); }; -restart_local returns[DebugItem debugItem] +restart_local returns[BuilderDebugItem debugItem] : ^(I_RESTART_LOCAL REGISTER address) { int registerNumber = parseRegister_short($REGISTER.text); - $debugItem = new ImmutableRestartLocal($address.address, registerNumber); + $debugItem = dexBuilder.internRestartLocal($address.address, registerNumber); }; -prologue returns[DebugItem debugItem] +prologue returns[BuilderDebugItem debugItem] : ^(I_PROLOGUE address) { - $debugItem = new ImmutablePrologueEnd($address.address); + $debugItem = dexBuilder.internPrologueEnd($address.address); }; -epilogue returns[DebugItem debugItem] +epilogue returns[BuilderDebugItem debugItem] : ^(I_EPILOGUE address) { - $debugItem = new ImmutableEpilogueBegin($address.address); + $debugItem = dexBuilder.internEpilogueBegin($address.address); }; -source returns[DebugItem debugItem] +source returns[BuilderDebugItem debugItem] : ^(I_SOURCE string_literal? address) { - $debugItem = new ImmutableSetSourceFile($address.address, $string_literal.value); + $debugItem = dexBuilder.internSetSourceFile($address.address, $string_literal.value); }; -statements returns[List instructions, int maxOutRegisters] +statements returns[List instructions, int maxOutRegisters] @init { $instructions = Lists.newArrayList(); @@ -809,7 +816,7 @@ verification_error_type returns[int verificationError] $verificationError = VerificationError.getVerificationError($VERIFICATION_ERROR_TYPE.text); }; -instruction[List instructions] returns[int outRegisters] +instruction[List instructions] returns[int outRegisters] : insn_format10t[$instructions] { $outRegisters = $insn_format10t.outRegisters; } | insn_format10x[$instructions] { $outRegisters = $insn_format10x.outRegisters; } | insn_format11n[$instructions] { $outRegisters = $insn_format11n.outRegisters; } @@ -850,7 +857,7 @@ instruction[List instructions] returns[int outRegisters] } -insn_format10t[List instructions] returns[int outRegisters] +insn_format10t[List instructions] returns[int outRegisters] : //e.g. goto endloop: {$outRegisters = 0;} ^(I_STATEMENT_FORMAT10t INSTRUCTION_FORMAT10t offset_or_label) @@ -859,18 +866,18 @@ insn_format10t[List instructions] returns[int outRegisters] int addressOffset = $offset_or_label.offsetValue; - $instructions.add(new ImmutableInstruction10t(opcode, addressOffset)); + $instructions.add(instructionFactory.makeInstruction10t(opcode, addressOffset)); }; -insn_format10x[List instructions] returns[int outRegisters] +insn_format10x[List instructions] returns[int outRegisters] : //e.g. return ^(I_STATEMENT_FORMAT10x INSTRUCTION_FORMAT10x) { Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT10x.text); - $instructions.add(new ImmutableInstruction10x(opcode)); + $instructions.add(instructionFactory.makeInstruction10x(opcode)); }; -insn_format11n[List instructions] returns[int outRegisters] +insn_format11n[List instructions] returns[int outRegisters] : //e.g. const/4 v0, 5 ^(I_STATEMENT_FORMAT11n INSTRUCTION_FORMAT11n REGISTER short_integral_literal) { @@ -880,20 +887,20 @@ insn_format11n[List instructions] returns[int outRegisters] short litB = $short_integral_literal.value; LiteralTools.checkNibble(litB); - $instructions.add(new ImmutableInstruction11n(opcode, regA, litB)); + $instructions.add(instructionFactory.makeInstruction11n(opcode, regA, litB)); }; -insn_format11x[List instructions] returns[int outRegisters] +insn_format11x[List instructions] returns[int outRegisters] : //e.g. move-result-object v1 ^(I_STATEMENT_FORMAT11x INSTRUCTION_FORMAT11x REGISTER) { Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT11x.text); short regA = parseRegister_byte($REGISTER.text); - $instructions.add(new ImmutableInstruction11x(opcode, regA)); + $instructions.add(instructionFactory.makeInstruction11x(opcode, regA)); }; -insn_format12x[List instructions] returns[int outRegisters] +insn_format12x[List instructions] returns[int outRegisters] : //e.g. move v1 v2 ^(I_STATEMENT_FORMAT12x INSTRUCTION_FORMAT12x registerA=REGISTER registerB=REGISTER) { @@ -901,10 +908,10 @@ insn_format12x[List instructions] returns[int outRegisters] byte regA = parseRegister_nibble($registerA.text); byte regB = parseRegister_nibble($registerB.text); - $instructions.add(new ImmutableInstruction12x(opcode, regA, regB)); + $instructions.add(instructionFactory.makeInstruction12x(opcode, regA, regB)); }; -insn_format20bc[List instructions] returns[int outRegisters] +insn_format20bc[List instructions] returns[int outRegisters] : //e.g. throw-verification-error generic-error, Lsome/class; ^(I_STATEMENT_FORMAT20bc INSTRUCTION_FORMAT20bc verification_error_type verification_error_reference) { @@ -913,10 +920,11 @@ insn_format20bc[List instructions] returns[int outRegisters] int verificationError = $verification_error_type.verificationError; ImmutableReference referencedItem = $verification_error_reference.reference; - $instructions.add(new ImmutableInstruction20bc(opcode, verificationError, referencedItem)); + $instructions.add(instructionFactory.makeInstruction20bc(opcode, verificationError, + dexBuilder.internReference(referencedItem))); }; -insn_format20t[List instructions] returns[int outRegisters] +insn_format20t[List instructions] returns[int outRegisters] : //e.g. goto/16 endloop: ^(I_STATEMENT_FORMAT20t INSTRUCTION_FORMAT20t offset_or_label) { @@ -924,10 +932,10 @@ insn_format20t[List instructions] returns[int outRegisters] int addressOffset = $offset_or_label.offsetValue; - $instructions.add(new ImmutableInstruction20t(opcode, addressOffset)); + $instructions.add(instructionFactory.makeInstruction20t(opcode, addressOffset)); }; -insn_format21c_field[List instructions] returns[int outRegisters] +insn_format21c_field[List instructions] returns[int outRegisters] : //e.g. sget_object v0, java/lang/System/out LJava/io/PrintStream; ^(I_STATEMENT_FORMAT21c_FIELD inst=(INSTRUCTION_FORMAT21c_FIELD | INSTRUCTION_FORMAT21c_FIELD_ODEX) REGISTER fully_qualified_field) { @@ -936,34 +944,33 @@ insn_format21c_field[List instructions] returns[int outRegisters] ImmutableFieldReference fieldReference = $fully_qualified_field.fieldReference; - $instructions.add(new ImmutableInstruction21c(opcode, regA, fieldReference)); + $instructions.add(instructionFactory.makeInstruction21c(opcode, regA, + dexBuilder.internFieldReference(fieldReference))); }; -insn_format21c_string[List instructions] returns[int outRegisters] +insn_format21c_string[List instructions] returns[int outRegisters] : //e.g. const-string v1, "Hello World!" ^(I_STATEMENT_FORMAT21c_STRING INSTRUCTION_FORMAT21c_STRING REGISTER string_literal) { Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_STRING.text); short regA = parseRegister_byte($REGISTER.text); - ImmutableStringReference stringReference = new ImmutableStringReference($string_literal.value); - - instructions.add(new ImmutableInstruction21c(opcode, regA, stringReference)); + instructions.add(instructionFactory.makeInstruction21c(opcode, regA, + dexBuilder.internStringReference($string_literal.value))); }; -insn_format21c_type[List instructions] returns[int outRegisters] +insn_format21c_type[List instructions] returns[int outRegisters] : //e.g. const-class v2, org/jf/HelloWorld2/HelloWorld2 ^(I_STATEMENT_FORMAT21c_TYPE INSTRUCTION_FORMAT21c_TYPE REGISTER reference_type_descriptor) { Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT21c_TYPE.text); short regA = parseRegister_byte($REGISTER.text); - ImmutableTypeReference typeReference = new ImmutableTypeReference($reference_type_descriptor.type); - - $instructions.add(new ImmutableInstruction21c(opcode, regA, typeReference)); + $instructions.add(instructionFactory.makeInstruction21c(opcode, regA, + dexBuilder.internTypeReference($reference_type_descriptor.type))); }; -insn_format21ih[List instructions] returns[int outRegisters] +insn_format21ih[List instructions] returns[int outRegisters] : //e.g. const/high16 v1, 1234 ^(I_STATEMENT_FORMAT21ih INSTRUCTION_FORMAT21ih REGISTER fixed_32bit_literal) { @@ -972,10 +979,10 @@ insn_format21ih[List instructions] returns[int outRegisters] int litB = $fixed_32bit_literal.value; - instructions.add(new ImmutableInstruction21ih(opcode, regA, litB)); + instructions.add(instructionFactory.makeInstruction21ih(opcode, regA, litB)); }; -insn_format21lh[List instructions] returns[int outRegisters] +insn_format21lh[List instructions] returns[int outRegisters] : //e.g. const-wide/high16 v1, 1234 ^(I_STATEMENT_FORMAT21lh INSTRUCTION_FORMAT21lh REGISTER fixed_64bit_literal) { @@ -984,10 +991,10 @@ insn_format21lh[List instructions] returns[int outRegisters] long litB = $fixed_64bit_literal.value; - instructions.add(new ImmutableInstruction21lh(opcode, regA, litB)); + instructions.add(instructionFactory.makeInstruction21lh(opcode, regA, litB)); }; -insn_format21s[List instructions] returns[int outRegisters] +insn_format21s[List instructions] returns[int outRegisters] : //e.g. const/16 v1, 1234 ^(I_STATEMENT_FORMAT21s INSTRUCTION_FORMAT21s REGISTER short_integral_literal) { @@ -996,10 +1003,10 @@ insn_format21s[List instructions] returns[int outRegisters] short litB = $short_integral_literal.value; - $instructions.add(new ImmutableInstruction21s(opcode, regA, litB)); + $instructions.add(instructionFactory.makeInstruction21s(opcode, regA, litB)); }; -insn_format21t[List instructions] returns[int outRegisters] +insn_format21t[List instructions] returns[int outRegisters] : //e.g. if-eqz v0, endloop: ^(I_STATEMENT_FORMAT21t INSTRUCTION_FORMAT21t REGISTER offset_or_label) { @@ -1012,10 +1019,10 @@ insn_format21t[List instructions] returns[int outRegisters] throw new SemanticException(input, $offset_or_label.start, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); } - $instructions.add(new ImmutableInstruction21t(opcode, regA, addressOffset)); + $instructions.add(instructionFactory.makeInstruction21t(opcode, regA, addressOffset)); }; -insn_format22b[List instructions] returns[int outRegisters] +insn_format22b[List instructions] returns[int outRegisters] : //e.g. add-int v0, v1, 123 ^(I_STATEMENT_FORMAT22b INSTRUCTION_FORMAT22b registerA=REGISTER registerB=REGISTER short_integral_literal) { @@ -1026,10 +1033,10 @@ insn_format22b[List instructions] returns[int outRegisters] short litC = $short_integral_literal.value; LiteralTools.checkByte(litC); - $instructions.add(new ImmutableInstruction22b(opcode, regA, regB, litC)); + $instructions.add(instructionFactory.makeInstruction22b(opcode, regA, regB, litC)); }; -insn_format22c_field[List instructions] returns[int outRegisters] +insn_format22c_field[List instructions] returns[int outRegisters] : //e.g. iput-object v1, v0, org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String; ^(I_STATEMENT_FORMAT22c_FIELD inst=(INSTRUCTION_FORMAT22c_FIELD | INSTRUCTION_FORMAT22c_FIELD_ODEX) registerA=REGISTER registerB=REGISTER fully_qualified_field) { @@ -1039,10 +1046,11 @@ insn_format22c_field[List instructions] returns[int outRegisters] ImmutableFieldReference fieldReference = $fully_qualified_field.fieldReference; - $instructions.add(new ImmutableInstruction22c(opcode, regA, regB, fieldReference)); + $instructions.add(instructionFactory.makeInstruction22c(opcode, regA, regB, + dexBuilder.internFieldReference(fieldReference))); }; -insn_format22c_type[List instructions] returns[int outRegisters] +insn_format22c_type[List instructions] returns[int outRegisters] : //e.g. instance-of v0, v1, Ljava/lang/String; ^(I_STATEMENT_FORMAT22c_TYPE INSTRUCTION_FORMAT22c_TYPE registerA=REGISTER registerB=REGISTER nonvoid_type_descriptor) { @@ -1050,12 +1058,11 @@ insn_format22c_type[List instructions] returns[int outRegisters] byte regA = parseRegister_nibble($registerA.text); byte regB = parseRegister_nibble($registerB.text); - ImmutableTypeReference typeReference = new ImmutableTypeReference($nonvoid_type_descriptor.type); - - $instructions.add(new ImmutableInstruction22c(opcode, regA, regB, typeReference)); + $instructions.add(instructionFactory.makeInstruction22c(opcode, regA, regB, + dexBuilder.internTypeReference($nonvoid_type_descriptor.type))); }; -insn_format22s[List instructions] returns[int outRegisters] +insn_format22s[List instructions] returns[int outRegisters] : //e.g. add-int/lit16 v0, v1, 12345 ^(I_STATEMENT_FORMAT22s INSTRUCTION_FORMAT22s registerA=REGISTER registerB=REGISTER short_integral_literal) { @@ -1065,10 +1072,10 @@ insn_format22s[List instructions] returns[int outRegisters] short litC = $short_integral_literal.value; - $instructions.add(new ImmutableInstruction22s(opcode, regA, regB, litC)); + $instructions.add(instructionFactory.makeInstruction22s(opcode, regA, regB, litC)); }; -insn_format22t[List instructions] returns[int outRegisters] +insn_format22t[List instructions] returns[int outRegisters] : //e.g. if-eq v0, v1, endloop: ^(I_STATEMENT_FORMAT22t INSTRUCTION_FORMAT22t registerA=REGISTER registerB=REGISTER offset_or_label) { @@ -1082,10 +1089,10 @@ insn_format22t[List instructions] returns[int outRegisters] throw new SemanticException(input, $offset_or_label.start, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); } - $instructions.add(new ImmutableInstruction22t(opcode, regA, regB, addressOffset)); + $instructions.add(instructionFactory.makeInstruction22t(opcode, regA, regB, addressOffset)); }; -insn_format22x[List instructions] returns[int outRegisters] +insn_format22x[List instructions] returns[int outRegisters] : //e.g. move/from16 v1, v1234 ^(I_STATEMENT_FORMAT22x INSTRUCTION_FORMAT22x registerA=REGISTER registerB=REGISTER) { @@ -1093,10 +1100,10 @@ insn_format22x[List instructions] returns[int outRegisters] short regA = parseRegister_byte($registerA.text); int regB = parseRegister_short($registerB.text); - $instructions.add(new ImmutableInstruction22x(opcode, regA, regB)); + $instructions.add(instructionFactory.makeInstruction22x(opcode, regA, regB)); }; -insn_format23x[List instructions] returns[int outRegisters] +insn_format23x[List instructions] returns[int outRegisters] : //e.g. add-int v1, v2, v3 ^(I_STATEMENT_FORMAT23x INSTRUCTION_FORMAT23x registerA=REGISTER registerB=REGISTER registerC=REGISTER) { @@ -1105,10 +1112,10 @@ insn_format23x[List instructions] returns[int outRegisters] short regB = parseRegister_byte($registerB.text); short regC = parseRegister_byte($registerC.text); - $instructions.add(new ImmutableInstruction23x(opcode, regA, regB, regC)); + $instructions.add(instructionFactory.makeInstruction23x(opcode, regA, regB, regC)); }; -insn_format30t[List instructions] returns[int outRegisters] +insn_format30t[List instructions] returns[int outRegisters] : //e.g. goto/32 endloop: ^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t offset_or_label) { @@ -1116,22 +1123,21 @@ insn_format30t[List instructions] returns[int outRegisters] int addressOffset = $offset_or_label.offsetValue; - $instructions.add(new ImmutableInstruction30t(opcode, addressOffset)); + $instructions.add(instructionFactory.makeInstruction30t(opcode, addressOffset)); }; -insn_format31c[List instructions] returns[int outRegisters] +insn_format31c[List instructions] returns[int outRegisters] : //e.g. const-string/jumbo v1 "Hello World!" ^(I_STATEMENT_FORMAT31c INSTRUCTION_FORMAT31c REGISTER string_literal) { Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT31c.text); short regA = parseRegister_byte($REGISTER.text); - ImmutableStringReference stringReference = new ImmutableStringReference($string_literal.value); - - $instructions.add(new ImmutableInstruction31c(opcode, regA, stringReference)); + $instructions.add(instructionFactory.makeInstruction31c(opcode, regA, + dexBuilder.internStringReference($string_literal.value))); }; -insn_format31i[List instructions] returns[int outRegisters] +insn_format31i[List instructions] returns[int outRegisters] : //e.g. const v0, 123456 ^(I_STATEMENT_FORMAT31i INSTRUCTION_FORMAT31i REGISTER fixed_32bit_literal) { @@ -1140,10 +1146,10 @@ insn_format31i[List instructions] returns[int outRegisters] int litB = $fixed_32bit_literal.value; - $instructions.add(new ImmutableInstruction31i(opcode, regA, litB)); + $instructions.add(instructionFactory.makeInstruction31i(opcode, regA, litB)); }; -insn_format31t[List instructions] returns[int outRegisters] +insn_format31t[List instructions] returns[int outRegisters] : //e.g. fill-array-data v0, ArrayData: ^(I_STATEMENT_FORMAT31t INSTRUCTION_FORMAT31t REGISTER offset_or_label) { @@ -1156,10 +1162,10 @@ insn_format31t[List instructions] returns[int outRegisters] addressOffset++; } - $instructions.add(new ImmutableInstruction31t(opcode, regA, addressOffset)); + $instructions.add(instructionFactory.makeInstruction31t(opcode, regA, addressOffset)); }; -insn_format32x[List instructions] returns[int outRegisters] +insn_format32x[List instructions] returns[int outRegisters] : //e.g. move/16 v5678, v1234 ^(I_STATEMENT_FORMAT32x INSTRUCTION_FORMAT32x registerA=REGISTER registerB=REGISTER) { @@ -1167,10 +1173,10 @@ insn_format32x[List instructions] returns[int outRegisters] int regA = parseRegister_short($registerA.text); int regB = parseRegister_short($registerB.text); - $instructions.add(new ImmutableInstruction32x(opcode, regA, regB)); + $instructions.add(instructionFactory.makeInstruction32x(opcode, regA, regB)); }; -insn_format35c_method[List instructions] returns[int outRegisters] +insn_format35c_method[List instructions] returns[int outRegisters] : //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V ^(I_STATEMENT_FORMAT35c_METHOD INSTRUCTION_FORMAT35c_METHOD register_list fully_qualified_method) { @@ -1183,10 +1189,11 @@ insn_format35c_method[List instructions] returns[int outRegisters] ImmutableMethodReference methodReference = $fully_qualified_method.methodReference; - $instructions.add(new ImmutableInstruction35c(opcode, registerCount, registers[0], registers[1], registers[2], registers[3], registers[4], methodReference)); + $instructions.add(instructionFactory.makeInstruction35c(opcode, registerCount, registers[0], registers[1], + registers[2], registers[3], registers[4], dexBuilder.internMethodReference(methodReference))); }; -insn_format35c_type[List instructions] returns[int outRegisters] +insn_format35c_type[List instructions] returns[int outRegisters] : //e.g. filled-new-array {v0,v1}, I ^(I_STATEMENT_FORMAT35c_TYPE INSTRUCTION_FORMAT35c_TYPE register_list nonvoid_type_descriptor) { @@ -1197,12 +1204,11 @@ insn_format35c_type[List instructions] returns[int outRegisters] byte registerCount = $register_list.registerCount; $outRegisters = registerCount; - ImmutableTypeReference typeReference = new ImmutableTypeReference($nonvoid_type_descriptor.type); - - $instructions.add(new ImmutableInstruction35c(opcode, registerCount, registers[0], registers[1], registers[2], registers[3], registers[4], typeReference)); + $instructions.add(instructionFactory.makeInstruction35c(opcode, registerCount, registers[0], registers[1], + registers[2], registers[3], registers[4], dexBuilder.internTypeReference($nonvoid_type_descriptor.type))); }; -insn_format3rc_method[List instructions] returns[int outRegisters] +insn_format3rc_method[List instructions] returns[int outRegisters] : //e.g. invoke-virtual/range {v25..v26} java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder; ^(I_STATEMENT_FORMAT3rc_METHOD INSTRUCTION_FORMAT3rc_METHOD register_range fully_qualified_method) { @@ -1215,10 +1221,11 @@ insn_format3rc_method[List instructions] returns[int outRegisters] ImmutableMethodReference methodReference = $fully_qualified_method.methodReference; - $instructions.add(new ImmutableInstruction3rc(opcode, startRegister, registerCount, methodReference)); + $instructions.add(instructionFactory.makeInstruction3rc(opcode, startRegister, registerCount, + dexBuilder.internMethodReference(methodReference))); }; -insn_format3rc_type[List instructions] returns[int outRegisters] +insn_format3rc_type[List instructions] returns[int outRegisters] : //e.g. filled-new-array/range {v0..v6} I ^(I_STATEMENT_FORMAT3rc_TYPE INSTRUCTION_FORMAT3rc_TYPE register_range nonvoid_type_descriptor) { @@ -1229,12 +1236,11 @@ insn_format3rc_type[List instructions] returns[int outRegisters] int registerCount = endRegister-startRegister+1; $outRegisters = registerCount; - ImmutableTypeReference typeReference = new ImmutableTypeReference($nonvoid_type_descriptor.type); - - $instructions.add(new ImmutableInstruction3rc(opcode, startRegister, registerCount, typeReference)); + $instructions.add(instructionFactory.makeInstruction3rc(opcode, startRegister, registerCount, + dexBuilder.internTypeReference($nonvoid_type_descriptor.type))); }; -insn_format51l_type[List instructions] returns[int outRegisters] +insn_format51l_type[List instructions] returns[int outRegisters] : //e.g. const-wide v0, 5000000000L ^(I_STATEMENT_FORMAT51l INSTRUCTION_FORMAT51l REGISTER fixed_64bit_literal) { @@ -1243,20 +1249,20 @@ insn_format51l_type[List instructions] returns[int outRegisters] long litB = $fixed_64bit_literal.value; - $instructions.add(new ImmutableInstruction51l(opcode, regA, litB)); + $instructions.add(instructionFactory.makeInstruction51l(opcode, regA, litB)); }; -insn_array_data_directive[List instructions] returns[int outRegisters] +insn_array_data_directive[List instructions] returns[int outRegisters] : //e.g. .array-data 4 1000000 .end array-data ^(I_STATEMENT_ARRAY_DATA ^(I_ARRAY_ELEMENT_SIZE short_integral_literal) array_elements) { int elementWidth = $short_integral_literal.value; List elements = $array_elements.elements; - $instructions.add(new ImmutableArrayPayload(elementWidth, $array_elements.elements)); + $instructions.add(instructionFactory.makeArrayPayload(elementWidth, $array_elements.elements)); }; -insn_packed_switch_directive[List instructions] returns[int outRegisters] +insn_packed_switch_directive[List instructions] returns[int outRegisters] : ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) { @@ -1268,10 +1274,10 @@ insn_packed_switch_directive[List instructions] returns[int outRegi } packed_switch_elements[baseAddress, startKey]) { - $instructions.add(new ImmutablePackedSwitchPayload($packed_switch_elements.elements)); + $instructions.add(instructionFactory.makePackedSwitchPayload($packed_switch_elements.elements)); }; -insn_sparse_switch_directive[List instructions] returns[int outRegisters] +insn_sparse_switch_directive[List instructions] returns[int outRegisters] : { Integer baseAddress = $method::sparseSwitchDeclarations.get($method::currentAddress); @@ -1281,7 +1287,7 @@ insn_sparse_switch_directive[List instructions] returns[int outRegi } ^(I_STATEMENT_SPARSE_SWITCH sparse_switch_elements[baseAddress]) { - $instructions.add(new ImmutableSparseSwitchPayload($sparse_switch_elements.elements)); + $instructions.add(instructionFactory.makeSparseSwitchPayload($sparse_switch_elements.elements)); }; nonvoid_type_descriptor returns [String type] diff --git a/smali/src/main/java/org/jf/smali/main.java b/smali/src/main/java/org/jf/smali/main.java index 61eece38..eeddec4f 100644 --- a/smali/src/main/java/org/jf/smali/main.java +++ b/smali/src/main/java/org/jf/smali/main.java @@ -28,21 +28,21 @@ package org.jf.smali; -import com.google.common.collect.Lists; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.Token; import org.antlr.runtime.TokenSource; import org.antlr.runtime.tree.CommonTree; import org.antlr.runtime.tree.CommonTreeNodeStream; import org.apache.commons.cli.*; -import org.jf.dexlib2.DexFileFactory; -import org.jf.dexlib2.iface.ClassDef; -import org.jf.dexlib2.immutable.ImmutableDexFile; +import org.jf.dexlib2.writer.builder.DexBuilder; import org.jf.util.ConsoleUtil; import org.jf.util.SmaliHelpFormatter; import java.io.*; -import java.util.*; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Properties; +import java.util.Set; /** * Main class for smali. It recognizes enough options to be able to dispatch @@ -184,8 +184,6 @@ public class main { } } - List classes = Lists.newArrayList(); - // TODO: uncomment /*if (apiSet && apiLevel >= 14) { dexFile.HeaderItem.setVersion(36); @@ -193,8 +191,10 @@ public class main { boolean errors = false; + DexBuilder dexBuilder = DexBuilder.makeDexBuilder(); + for (File file: filesToProcess) { - if (!assembleSmaliFile(file, classes, verboseErrors, printTokens, allowOdex, apiLevel)) { + if (!assembleSmaliFile(file, dexBuilder, verboseErrors, printTokens, allowOdex, apiLevel)) { errors = true; } } @@ -203,7 +203,6 @@ public class main { System.exit(1); } - // TODO: uncomment /*if (sort) { dexFile.setSortAllItems(true); @@ -214,8 +213,7 @@ public class main { fixInstructions(dexFile, fixJumbo, fixGoto); }*/ - - DexFileFactory.writeDexFile(outputDexFile, new ImmutableDexFile(classes)); + dexBuilder.writeTo(outputDexFile); } catch (RuntimeException ex) { System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:"); ex.printStackTrace(); @@ -229,13 +227,13 @@ public class main { private static void getSmaliFilesInDir(File dir, Set smaliFiles) { for(File file: dir.listFiles()) { - if (file.isDirectory()) { - getSmaliFilesInDir(file, smaliFiles); - } else if (file.getName().endsWith(".smali")) { - smaliFiles.add(file); + if (file.isDirectory()) { + getSmaliFilesInDir(file, smaliFiles); + } else if (file.getName().endsWith(".smali")) { + smaliFiles.add(file); + } } } - } // TODO: uncomment /*private static void fixInstructions(DexFile dexFile, boolean fixJumbo, boolean fixGoto) { @@ -243,15 +241,14 @@ public class main { for (CodeItem codeItem: dexFile.CodeItemsSection.getItems()) { codeItem.fixInstructions(fixJumbo, fixGoto); - } + } }*/ - private static boolean assembleSmaliFile(File smaliFile, List classes, boolean verboseErrors, + private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, boolean verboseErrors, boolean printTokens, boolean allowOdex, int apiLevel) throws Exception { CommonTokenStream tokens; - boolean lexerErrors = false; LexerErrorInterface lexer; @@ -293,12 +290,8 @@ public class main { smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); dexGen.setVerboseErrors(verboseErrors); - ClassDef classDef = dexGen.smali_file(); - - // TODO: will this ever be null? if so, what should happen? - if (classDef != null) { - classes.add(classDef); - } + dexGen.setDexBuilder(dexBuilder); + dexGen.smali_file(); if (dexGen.getNumberOfSyntaxErrors() > 0) { return false;