diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java index fc43d6f1..39485c90 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java @@ -79,11 +79,20 @@ public class InstructionMethodItem extends MethodItem { return opcode.isVolatileFieldAccessor() || opcode == Opcode.THROW_VERIFICATION_ERROR; } + private String writeInvalidItemIndex(InvalidItemIndex ex, int type, IndentingWriter writer) + throws IOException { + writer.write("#"); + writer.write(ex.getMessage()); + writer.write("\n"); + return String.format("%s@%d", ReferenceType.toString(type), ex.getInvalidIndex()); + } + @Override public boolean writeTo(IndentingWriter writer) throws IOException { Opcode opcode = instruction.getOpcode(); String verificationErrorName = null; String referenceString = null; + String referenceString2 = null; boolean commentOutInstruction = false; @@ -100,25 +109,19 @@ public class InstructionMethodItem extends MethodItem { if (instruction instanceof ReferenceInstruction) { ReferenceInstruction referenceInstruction = (ReferenceInstruction)instruction; + String classContext = null; + if (methodDef.classDef.options.useImplicitReferences) { + classContext = methodDef.method.getDefiningClass(); + } + try { Reference reference = referenceInstruction.getReference(); - - String classContext = null; - if (methodDef.classDef.options.useImplicitReferences) { - classContext = methodDef.method.getDefiningClass(); - } - referenceString = ReferenceUtil.getReferenceString(reference, classContext); assert referenceString != null; } catch (InvalidItemIndex ex) { - writer.write("#"); - writer.write(ex.getMessage()); - writer.write("\n"); commentOutInstruction = true; - - referenceString = String.format("%s@%d", - ReferenceType.toString(referenceInstruction.getReferenceType()), - ex.getInvalidIndex()); + referenceString = writeInvalidItemIndex(ex, referenceInstruction.getReferenceType(), + writer); } catch (ReferenceType.InvalidReferenceTypeException ex) { writer.write("#invalid reference type: "); writer.printSignedIntAsDec(ex.getReferenceType()); @@ -126,6 +129,25 @@ public class InstructionMethodItem extends MethodItem { referenceString = "invalid_reference"; } + + if (instruction instanceof DualReferenceInstruction) { + DualReferenceInstruction dualReferenceInstruction = + (DualReferenceInstruction) instruction; + try { + Reference reference2 = dualReferenceInstruction.getReference2(); + referenceString2 = ReferenceUtil.getReferenceString(reference2, classContext); + } catch (InvalidItemIndex ex) { + commentOutInstruction = true; + referenceString2 = writeInvalidItemIndex(ex, + dualReferenceInstruction.getReferenceType2(), writer); + } catch (ReferenceType.InvalidReferenceTypeException ex) { + writer.write("#invalid reference type: "); + writer.printSignedIntAsDec(ex.getReferenceType()); + commentOutInstruction = true; + + referenceString2 = "invalid_reference"; + } + } } if (instruction instanceof Instruction31t) { @@ -355,6 +377,24 @@ public class InstructionMethodItem extends MethodItem { writer.write(", "); writeVtableIndex(writer); break; + case Format45cc: + writeOpcode(writer); + writer.write(' '); + writeInvokeRegisters(writer); + writer.write(", "); + writer.write(referenceString); + writer.write(", "); + writer.write(referenceString2); + break; + case Format4rcc: + writeOpcode(writer); + writer.write(' '); + writeInvokeRangeRegisters(writer); + writer.write(", "); + writer.write(referenceString); + writer.write(", "); + writer.write(referenceString2); + break; default: assert false; return false; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/Format.java b/dexlib2/src/main/java/org/jf/dexlib2/Format.java index ee34aa50..42e8e144 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/Format.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/Format.java @@ -63,6 +63,8 @@ public enum Format { Format3rc(6), Format3rmi(6), Format3rms(6), + Format45cc(8), + Format4rcc(8), Format51l(10), ArrayPayload(-1, true), PackedSwitchPayload(-1, true), diff --git a/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java b/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java index 3a642358..9559760a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java @@ -294,8 +294,8 @@ public enum Opcode INVOKE_VIRTUAL_QUICK(combine(allApis(0xf8), allArtVersions(0xe9)), "invoke-virtual-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), INVOKE_VIRTUAL_QUICK_RANGE(combine(allApis(0xf9), allArtVersions(0xea)), "invoke-virtual-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), - INVOKE_SUPER_QUICK(allApis(0xfa), "invoke-super-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), - INVOKE_SUPER_QUICK_RANGE(allApis(0xfb), "invoke-super-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_SUPER_QUICK(lastApi(0xfa, 25), "invoke-super-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_SUPER_QUICK_RANGE(lastApi(0xfb, 25), "invoke-super-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), IPUT_OBJECT_VOLATILE(firstApi(0xfc, 9), "iput-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), SGET_OBJECT_VOLATILE(firstApi(0xfd, 9), "sget-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR), @@ -313,7 +313,9 @@ public enum Opcode // TODO: do we need a capture/liberate wide? LIBERATE_VARIABLE(allArtVersions(0xf7), "liberate-variable", ReferenceType.STRING, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL), BOX_LAMBDA(allArtVersions(0xf8), "box-lambda", ReferenceType.NONE, Format.Format22x, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL), - UNBOX_LAMBDA(allArtVersions(0xf9), "unbox-lambda", ReferenceType.TYPE, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL); + UNBOX_LAMBDA(allArtVersions(0xf9), "unbox-lambda", ReferenceType.TYPE, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL), + INVOKE_POLYMORPHIC(firstApi(0xfa, 26), "invoke-polymorphic", ReferenceType.METHOD, ReferenceType.METHOD_PROTO, Format.Format45cc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_POLYMORPHIC_RANGE(firstApi(0xfb, 26), "invoke-polymorphic/range", ReferenceType.METHOD, ReferenceType.METHOD_PROTO, Format.Format4rcc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT); //if the instruction can throw an exception public static final int CAN_THROW = 0x1; @@ -359,6 +361,7 @@ public enum Opcode public final int referenceType; public final Format format; public final int flags; + public final int referenceType2; Opcode(int opcodeValue, String opcodeName, int referenceType, Format format) { this(opcodeValue, opcodeName, referenceType, format, 0); @@ -369,6 +372,11 @@ public enum Opcode } Opcode(List versionConstraints, String opcodeName, int referenceType, Format format, int flags) { + this(versionConstraints, opcodeName, referenceType, -1, format, flags); + } + + Opcode(List versionConstraints, String opcodeName, int referenceType, int referenceType2, + Format format, int flags) { ImmutableRangeMap.Builder apiToValueBuilder = ImmutableRangeMap.builder(); ImmutableRangeMap.Builder artVersionToValueBuilder = ImmutableRangeMap.builder(); @@ -385,6 +393,7 @@ public enum Opcode this.artVersionToValueMap = artVersionToValueBuilder.build(); this.name = opcodeName; this.referenceType = referenceType; + this.referenceType2 = referenceType2; this.format = format; this.flags = flags; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java b/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java index 3371f818..fa8b9687 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java @@ -39,7 +39,8 @@ public final class ReferenceType { public static final int TYPE = 1; public static final int FIELD = 2; public static final int METHOD = 3; - public static final int NONE = 4; + public static final int METHOD_PROTO = 4; + public static final int NONE = 5; public static String toString(int referenceType) { switch (referenceType) { @@ -51,6 +52,8 @@ public final class ReferenceType { return "field"; case METHOD: return "method"; + case METHOD_PROTO: + return "method_proto"; default: throw new InvalidReferenceTypeException(referenceType); } @@ -65,6 +68,8 @@ public final class ReferenceType { return FIELD; } else if (reference instanceof MethodReference) { return METHOD; + } else if (reference instanceof MethodProtoReference) { + return METHOD_PROTO; } else { throw new IllegalStateException("Invalid reference"); } @@ -76,7 +81,7 @@ public final class ReferenceType { * @throws InvalidReferenceTypeException */ public static void validateReferenceType(int referenceType) { - if (referenceType < 0 || referenceType > 3) { + if (referenceType < 0 || referenceType > 4) { throw new InvalidReferenceTypeException(referenceType); } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodProtoReference.java b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodProtoReference.java new file mode 100644 index 00000000..c0d38b0b --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodProtoReference.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016, 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.base.reference; + +import com.google.common.collect.Ordering; +import org.jf.dexlib2.iface.reference.MethodProtoReference; +import org.jf.util.CharSequenceUtils; +import org.jf.util.CollectionUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + + +public abstract class BaseMethodProtoReference implements MethodProtoReference { + @Override + public int hashCode() { + int hashCode = getReturnType().hashCode(); + return hashCode*31 + getParameterTypes().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof MethodProtoReference) { + MethodProtoReference other = (MethodProtoReference)o; + return getReturnType().equals(other.getReturnType()) && + CharSequenceUtils.listEquals(getParameterTypes(), other.getParameterTypes()); + } + return false; + } + + @Override + public int compareTo(@Nonnull MethodProtoReference o) { + int res = getReturnType().compareTo(o.getReturnType()); + if (res != 0) return res; + return CollectionUtils.compareAsIterable(Ordering.usingToString(), getParameterTypes(), o.getParameterTypes()); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/builder/instruction/BuilderInstruction35c.java b/dexlib2/src/main/java/org/jf/dexlib2/builder/instruction/BuilderInstruction35c.java index 283464fe..5ec58a92 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/builder/instruction/BuilderInstruction35c.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/builder/instruction/BuilderInstruction35c.java @@ -60,7 +60,7 @@ public class BuilderInstruction35c extends BuilderInstruction implements Instruc int registerG, @Nonnull Reference reference) { super(opcode); - this.registerCount = Preconditions.check35cRegisterCount(registerCount); + this.registerCount = Preconditions.check35cAnd45ccRegisterCount(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; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/builder/instruction/BuilderInstruction45cc.java b/dexlib2/src/main/java/org/jf/dexlib2/builder/instruction/BuilderInstruction45cc.java new file mode 100644 index 00000000..b0e3f427 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/builder/instruction/BuilderInstruction45cc.java @@ -0,0 +1,87 @@ +/* + * Copyright 2016, 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.builder.instruction; + +import org.jf.dexlib2.Format; +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.builder.BuilderInstruction; +import org.jf.dexlib2.iface.instruction.formats.Instruction45cc; +import org.jf.dexlib2.iface.reference.Reference; +import org.jf.dexlib2.util.Preconditions; + +import javax.annotation.Nonnull; + +public class BuilderInstruction45cc extends BuilderInstruction implements Instruction45cc { + public static final Format FORMAT = Format.Format45cc; + + 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 Reference reference; + @Nonnull protected final Reference reference2; + + public BuilderInstruction45cc(@Nonnull Opcode opcode, + int registerCount, + int registerC, + int registerD, + int registerE, + int registerF, + int registerG, + @Nonnull Reference reference, + @Nonnull Reference reference2) { + super(opcode); + this.registerCount = Preconditions.check35cAnd45ccRegisterCount(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 = reference; + this.reference2 = reference2; + } + + @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 Reference getReference() { return reference; } + @Override public int getReferenceType() { return opcode.referenceType; } + @Nonnull @Override public Reference getReference2() { return reference2; } + @Override public int getReferenceType2() { return opcode.referenceType2; } + + @Override public Format getFormat() { return FORMAT; } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/builder/instruction/BuilderInstruction4rcc.java b/dexlib2/src/main/java/org/jf/dexlib2/builder/instruction/BuilderInstruction4rcc.java new file mode 100644 index 00000000..3ee7ef9d --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/builder/instruction/BuilderInstruction4rcc.java @@ -0,0 +1,72 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder.instruction; + +import org.jf.dexlib2.Format; +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.builder.BuilderInstruction; +import org.jf.dexlib2.iface.instruction.formats.Instruction4rcc; +import org.jf.dexlib2.iface.reference.Reference; +import org.jf.dexlib2.util.Preconditions; + +import javax.annotation.Nonnull; + +public class BuilderInstruction4rcc extends BuilderInstruction implements Instruction4rcc { + public static final Format FORMAT = Format.Format4rcc; + + protected final int startRegister; + protected final int registerCount; + + @Nonnull protected final Reference reference; + @Nonnull protected final Reference reference2; + + public BuilderInstruction4rcc(@Nonnull Opcode opcode, + int startRegister, + int registerCount, + @Nonnull Reference reference, + @Nonnull Reference reference2) { + super(opcode); + this.startRegister = Preconditions.checkShortRegister(startRegister); + this.registerCount = Preconditions.checkRegisterRangeCount(registerCount); + this.reference = reference; + this.reference2 = reference2; + } + + @Override public int getStartRegister() { return startRegister; } + @Override public int getRegisterCount() { return registerCount; } + @Nonnull @Override public Reference getReference() { return reference; } + @Override public int getReferenceType() { return opcode.referenceType; } + @Nonnull @Override public Reference getReference2() { return reference2; } + @Override public int getReferenceType2() { return opcode.referenceType2; } + + @Override public Format getFormat() { return FORMAT; } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction.java index ac82f4b4..2973398a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction.java @@ -139,6 +139,10 @@ public abstract class DexBackedInstruction implements Instruction { return new DexBackedInstruction3rmi(dexFile, opcode, instructionStartOffset); case Format3rms: return new DexBackedInstruction3rms(dexFile, opcode, instructionStartOffset); + case Format45cc: + return new DexBackedInstruction45cc(dexFile, opcode, instructionStartOffset); + case Format4rcc: + return new DexBackedInstruction4rcc(dexFile, opcode, instructionStartOffset); case Format51l: return new DexBackedInstruction51l(dexFile, opcode, instructionStartOffset); case PackedSwitchPayload: diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction45cc.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction45cc.java new file mode 100644 index 00000000..bbdc229f --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction45cc.java @@ -0,0 +1,101 @@ +/* + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.dexbacked.instruction; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; +import org.jf.dexlib2.dexbacked.reference.DexBackedReference; +import org.jf.dexlib2.iface.instruction.formats.Instruction45cc; +import org.jf.dexlib2.iface.reference.Reference; +import org.jf.util.NibbleUtils; + +import javax.annotation.Nonnull; + +public class DexBackedInstruction45cc extends DexBackedInstruction implements Instruction45cc { + public DexBackedInstruction45cc(@Nonnull DexBackedDexFile dexFile, + @Nonnull Opcode opcode, + int instructionStart) { + super(dexFile, opcode, instructionStart); + } + + @Override public int getRegisterCount() { + return NibbleUtils.extractHighUnsignedNibble(dexFile.readUbyte(instructionStart + 1)); + } + + @Override + public int getRegisterC() { + return NibbleUtils.extractLowUnsignedNibble(dexFile.readUbyte(instructionStart + 4)); + } + + @Override + public int getRegisterD() { + return NibbleUtils.extractHighUnsignedNibble(dexFile.readUbyte(instructionStart + 4)); + } + + @Override + public int getRegisterE() { + return NibbleUtils.extractLowUnsignedNibble(dexFile.readUbyte(instructionStart + 5)); + } + + @Override + public int getRegisterF() { + return NibbleUtils.extractHighUnsignedNibble(dexFile.readUbyte(instructionStart + 5)); + } + + @Override + public int getRegisterG() { + return NibbleUtils.extractLowUnsignedNibble(dexFile.readUbyte(instructionStart + 1)); + } + + @Nonnull + @Override + public Reference getReference() { + return DexBackedReference.makeReference(dexFile, opcode.referenceType, + dexFile.readUshort(instructionStart + 2)); + } + + @Override + public int getReferenceType() { + return opcode.referenceType; + } + + @Override + public Reference getReference2() { + return DexBackedReference.makeReference(dexFile, opcode.referenceType2, + dexFile.readUshort(instructionStart + 3)); + } + + @Override + public int getReferenceType2() { + return opcode.referenceType2; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction4rcc.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction4rcc.java new file mode 100644 index 00000000..629e753b --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction4rcc.java @@ -0,0 +1,80 @@ +/* + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.dexbacked.instruction; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; +import org.jf.dexlib2.dexbacked.reference.DexBackedReference; +import org.jf.dexlib2.iface.instruction.formats.Instruction4rcc; +import org.jf.dexlib2.iface.reference.Reference; + +import javax.annotation.Nonnull; + +public class DexBackedInstruction4rcc extends DexBackedInstruction implements Instruction4rcc { + public DexBackedInstruction4rcc(@Nonnull DexBackedDexFile dexFile, + @Nonnull Opcode opcode, + int instructionStart) { + super(dexFile, opcode, instructionStart); + } + + @Override public int getRegisterCount() { + return dexFile.readUbyte(instructionStart + 1); + } + + @Override + public int getStartRegister() { + return dexFile.readUshort(instructionStart + 4); + } + + @Nonnull + @Override + public Reference getReference() { + return DexBackedReference.makeReference(dexFile, opcode.referenceType, + dexFile.readUshort(instructionStart + 2)); + } + + @Override + public int getReferenceType() { + return opcode.referenceType; + } + + @Override + public Reference getReference2() { + return DexBackedReference.makeReference(dexFile, opcode.referenceType2, + dexFile.readUshort(instructionStart + 3)); + } + + @Override + public int getReferenceType2() { + return opcode.referenceType2; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodProtoReference.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodProtoReference.java new file mode 100644 index 00000000..12875b7b --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodProtoReference.java @@ -0,0 +1,77 @@ +/* + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.dexbacked.reference; + +import com.google.common.collect.ImmutableList; +import org.jf.dexlib2.base.reference.BaseMethodProtoReference; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; +import org.jf.dexlib2.dexbacked.raw.ProtoIdItem; +import org.jf.dexlib2.dexbacked.raw.TypeListItem; +import org.jf.dexlib2.dexbacked.util.FixedSizeList; + +import java.util.List; +import javax.annotation.Nonnull; + +public class DexBackedMethodProtoReference extends BaseMethodProtoReference { + @Nonnull public final DexBackedDexFile dexFile; + private final int protoIdItemOffset; + + public DexBackedMethodProtoReference(@Nonnull DexBackedDexFile dexFile, int protoIndex) { + this.dexFile = dexFile; + this.protoIdItemOffset = dexFile.getProtoIdItemOffset(protoIndex); + } + + @Nonnull + @Override + public List getParameterTypes() { + final int parametersOffset = dexFile.readSmallUint(protoIdItemOffset + ProtoIdItem.PARAMETERS_OFFSET); + if (parametersOffset > 0) { + final int parameterCount = dexFile.readSmallUint(parametersOffset + TypeListItem.SIZE_OFFSET); + final int paramListStart = parametersOffset + TypeListItem.LIST_OFFSET; + return new FixedSizeList() { + @Nonnull + @Override + public String readItem(final int index) { + return dexFile.getType(dexFile.readUshort(paramListStart + 2*index)); + } + @Override public int size() { return parameterCount; } + }; + } + return ImmutableList.of(); + } + + @Nonnull + @Override + public String getReturnType() { + return dexFile.getType(dexFile.readSmallUint(protoIdItemOffset + ProtoIdItem.RETURN_TYPE_OFFSET)); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedReference.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedReference.java index b63c37c2..99d66ecd 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedReference.java @@ -49,6 +49,8 @@ public abstract class DexBackedReference { return new DexBackedMethodReference(dexFile, referenceIndex); case ReferenceType.FIELD: return new DexBackedFieldReference(dexFile, referenceIndex); + case ReferenceType.METHOD_PROTO: + return new DexBackedMethodProtoReference(dexFile, referenceIndex); default: throw new ExceptionWithContext("Invalid reference type: %d", referenceType); } diff --git a/baksmali/src/test/java/org/jf/baksmali/LambdaTest.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/instruction/DualReferenceInstruction.java similarity index 73% rename from baksmali/src/test/java/org/jf/baksmali/LambdaTest.java rename to dexlib2/src/main/java/org/jf/dexlib2/iface/instruction/DualReferenceInstruction.java index 5431df54..0f1f81b2 100644 --- a/baksmali/src/test/java/org/jf/baksmali/LambdaTest.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/instruction/DualReferenceInstruction.java @@ -1,5 +1,5 @@ /* - * Copyright 2015, Google Inc. + * Copyright 2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,21 +29,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.jf.baksmali; +package org.jf.dexlib2.iface.instruction; -import org.junit.Test; +import org.jf.dexlib2.iface.reference.Reference; -public class LambdaTest extends IdenticalRoundtripTest { +import javax.annotation.Nonnull; - private baksmaliOptions createOptions() { - baksmaliOptions options = new baksmaliOptions(); - options.apiLevel = 23; // since we need at least level 23 for lambda opcodes - options.experimental = true; // since these opcodes aren't implemented in runtime yet); - return options; - } - - @Test - public void testHelloWorldLambda() { - runTest("HelloWorldLambda", createOptions()); - } +public interface DualReferenceInstruction extends ReferenceInstruction { + @Nonnull Reference getReference2(); + int getReferenceType2(); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/instruction/formats/Instruction45cc.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/instruction/formats/Instruction45cc.java new file mode 100644 index 00000000..221fd623 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/instruction/formats/Instruction45cc.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016, 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.iface.instruction.formats; + +import org.jf.dexlib2.iface.instruction.DualReferenceInstruction; +import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction; + +public interface Instruction45cc extends FiveRegisterInstruction, DualReferenceInstruction { +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/instruction/formats/Instruction4rcc.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/instruction/formats/Instruction4rcc.java new file mode 100644 index 00000000..d0b930b1 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/instruction/formats/Instruction4rcc.java @@ -0,0 +1,38 @@ +/* + * Copyright 2016, 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.iface.instruction.formats; + +import org.jf.dexlib2.iface.instruction.DualReferenceInstruction; +import org.jf.dexlib2.iface.instruction.RegisterRangeInstruction; + +public interface Instruction4rcc extends RegisterRangeInstruction, DualReferenceInstruction { +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/reference/MethodProtoReference.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/reference/MethodProtoReference.java new file mode 100644 index 00000000..e150c214 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/reference/MethodProtoReference.java @@ -0,0 +1,96 @@ +/* + * Copyright 20116, 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.iface.reference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +/** + * This class represents a reference to a method prototype. + */ +public interface MethodProtoReference extends Reference, Comparable { + /** + * Gets a list of the types of the parameters of this method prototype. + * + * @return A list of the parameter types of this method prototype, as strings. + */ + @Nonnull List getParameterTypes(); + + /** + * Gets the return type of the referenced method prototype. + * + * @return The return type of the referenced method prototype. + */ + @Nonnull String getReturnType(); + + /** + * Returns a hashcode for this MethodProtoReference. + * + * This hashCode is defined to be the following: + * + *
+     * {@code
+     * int hashCode =  getReturnType().hashCode();
+     * hashCode = hashCode*31 + CharSequenceUtils.listHashCode(getParameters());
+     * }
+ * + * @return The hash code value for this ProtoReference + */ + @Override int hashCode(); + + /** + * Compares this MethodTypeReference to another MethodProtoReference for equality. + * + * This MethodTypeReference is equal to another MethodProtoReference if all of it's "fields" are equal. That is, if + * the return values of getReturnType() and getParameterTypes() are all equal. + * + * Equality for getParameters() should be tested by comparing the string representation of each element. I.e. + * CharSequenceUtils.listEquals(this.getParameterTypes(), other.getParameterTypes()) + * + * @param o The object to be compared for equality with this MethodProtoReference + * @return true if the specified object is equal to this MethodProtoReference + */ + @Override boolean equals(@Nullable Object o); + + /** + * Compare this MethodTypeReference to another MethodProtoReference. + * + * The comparison is based on the comparison of the return values of getReturnType() and getParameters(), + * in that order. getParameters() should be compared using the semantics of + * org.jf.util.CollectionUtils.compareAsList() + * + * @param o The MethodReference to compare with this MethodProtoReference + * @return An integer representing the result of the comparison + */ + @Override int compareTo(@Nonnull MethodProtoReference o); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35c.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35c.java index 20976f80..7ac1a8f6 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35c.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35c.java @@ -61,7 +61,7 @@ public class ImmutableInstruction35c extends ImmutableInstruction implements Ins int registerG, @Nonnull Reference reference) { super(opcode); - this.registerCount = Preconditions.check35cRegisterCount(registerCount); + this.registerCount = Preconditions.check35cAnd45ccRegisterCount(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; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35mi.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35mi.java index def7eb60..5e58cae9 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35mi.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35mi.java @@ -58,7 +58,7 @@ public class ImmutableInstruction35mi extends ImmutableInstruction implements In int registerG, int inlineIndex) { super(opcode); - this.registerCount = Preconditions.check35cRegisterCount(registerCount); + this.registerCount = Preconditions.check35cAnd45ccRegisterCount(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; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35ms.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35ms.java index 16d7e913..0130f474 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35ms.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction35ms.java @@ -58,7 +58,7 @@ public class ImmutableInstruction35ms extends ImmutableInstruction implements In int registerG, int vtableIndex) { super(opcode); - this.registerCount = Preconditions.check35cRegisterCount(registerCount); + this.registerCount = Preconditions.check35cAnd45ccRegisterCount(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; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/reference/ImmutableMethodProtoReference.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/reference/ImmutableMethodProtoReference.java new file mode 100644 index 00000000..8c2afe59 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/reference/ImmutableMethodProtoReference.java @@ -0,0 +1,72 @@ +/* + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.immutable.reference; + +import com.google.common.collect.ImmutableList; +import org.jf.dexlib2.base.reference.BaseMethodProtoReference; +import org.jf.dexlib2.iface.reference.MethodProtoReference; +import org.jf.dexlib2.immutable.util.CharSequenceConverter; + +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ImmutableMethodProtoReference extends BaseMethodProtoReference implements ImmutableReference { + @Nonnull protected final ImmutableList parameters; + @Nonnull protected final String returnType; + + public ImmutableMethodProtoReference(@Nullable Iterable parameters, + @Nonnull String returnType) { + this.parameters = CharSequenceConverter.immutableStringList(parameters); + this.returnType = returnType; + } + + @Nonnull public static ImmutableMethodProtoReference of(@Nonnull MethodProtoReference methodProtoReference) { + if (methodProtoReference instanceof ImmutableMethodProtoReference) { + return (ImmutableMethodProtoReference) methodProtoReference; + } + return new ImmutableMethodProtoReference( + methodProtoReference.getParameterTypes(), + methodProtoReference.getReturnType()); + } + + @Override + public List getParameterTypes() { + return parameters; + } + + @Override + public String getReturnType() { + return returnType; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/reference/ImmutableReferenceFactory.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/reference/ImmutableReferenceFactory.java index 0d27e47f..d0007479 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/reference/ImmutableReferenceFactory.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/reference/ImmutableReferenceFactory.java @@ -52,6 +52,9 @@ public class ImmutableReferenceFactory { if (reference instanceof MethodReference) { return ImmutableMethodReference.of((MethodReference)reference); } + if (reference instanceof MethodProtoReference) { + return ImmutableMethodProtoReference.of((MethodProtoReference) reference); + } throw new ExceptionWithContext("Invalid reference type"); } @@ -66,6 +69,8 @@ public class ImmutableReferenceFactory { return ImmutableFieldReference.of((FieldReference)reference); case ReferenceType.METHOD: return ImmutableMethodReference.of((MethodReference)reference); + case ReferenceType.METHOD_PROTO: + return ImmutableMethodProtoReference.of((MethodProtoReference)reference); } throw new ExceptionWithContext("Invalid reference type: %d", referenceType); } 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 51c083ca..3b30d075 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/Preconditions.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/Preconditions.java @@ -125,7 +125,7 @@ public class Preconditions { return offset; } - public static int check35cRegisterCount(int registerCount) { + public static int check35cAnd45ccRegisterCount(int registerCount) { if (registerCount < 0 || registerCount > 5) { throw new IllegalArgumentException( String.format("Invalid register count: %d. Must be between 0 and 5, inclusive.", registerCount)); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java index 81b042ec..4e46a0e9 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java @@ -60,6 +60,17 @@ public final class ReferenceUtil { return sb.toString(); } + public static String getMethodProtoDescriptor(MethodProtoReference methodProtoReference) { + StringBuilder sb = new StringBuilder(); + sb.append('('); + for (CharSequence paramType : methodProtoReference.getParameterTypes()) { + sb.append(paramType); + } + sb.append(')'); + sb.append(methodProtoReference.getReturnType()); + return sb.toString(); + } + public static void writeMethodDescriptor(Writer writer, MethodReference methodReference) throws IOException { writeMethodDescriptor(writer, methodReference, false); } @@ -134,12 +145,16 @@ public final class ReferenceUtil { if (reference instanceof FieldReference) { FieldReference fieldReference = (FieldReference)reference; boolean useImplicitReference = fieldReference.getDefiningClass().equals(containingClass); - return getFieldDescriptor((FieldReference)reference, useImplicitReference); + return getFieldDescriptor(fieldReference, useImplicitReference); } if (reference instanceof MethodReference) { MethodReference methodReference = (MethodReference)reference; boolean useImplicitReference = methodReference.getDefiningClass().equals(containingClass); - return getMethodDescriptor((MethodReference)reference, useImplicitReference); + return getMethodDescriptor(methodReference, useImplicitReference); + } + if (reference instanceof MethodProtoReference) { + MethodProtoReference methodProtoReference = (MethodProtoReference)reference; + return getMethodProtoDescriptor(methodProtoReference); } return null; } 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 4e81f7fa..5329351e 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java @@ -54,6 +54,7 @@ import org.jf.dexlib2.iface.instruction.OneRegisterInstruction; import org.jf.dexlib2.iface.instruction.ReferenceInstruction; import org.jf.dexlib2.iface.instruction.formats.*; import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodProtoReference; import org.jf.dexlib2.iface.reference.MethodReference; import org.jf.dexlib2.iface.reference.StringReference; import org.jf.dexlib2.iface.reference.TypeReference; @@ -84,7 +85,7 @@ import java.util.zip.Adler32; public abstract class DexWriter< StringKey extends CharSequence, StringRef extends StringReference, TypeKey extends CharSequence, - TypeRef extends TypeReference, ProtoKey extends Comparable, + TypeRef extends TypeReference, ProtoRefKey extends MethodProtoReference, FieldRefKey extends FieldReference, MethodRefKey extends MethodReference, ClassKey extends Comparable, AnnotationKey extends Annotation, AnnotationSetKey, @@ -125,9 +126,9 @@ public abstract class DexWriter< protected final StringSection stringSection; protected final TypeSection typeSection; - protected final ProtoSection protoSection; + protected final ProtoSection protoSection; protected final FieldSection fieldSection; - protected final MethodSection methodSection; + protected final MethodSection methodSection; protected final ClassSection classSection; @@ -138,9 +139,9 @@ public abstract class DexWriter< protected DexWriter(Opcodes opcodes, StringSection stringSection, TypeSection typeSection, - ProtoSection protoSection, + ProtoSection protoSection, FieldSection fieldSection, - MethodSection methodSection, + MethodSection methodSection, ClassSection classSection, TypeListSection typeListSection, @@ -347,12 +348,12 @@ public abstract class DexWriter< protoSectionOffset = writer.getPosition(); int index = 0; - List> protoEntries = Lists.newArrayList(protoSection.getItems()); - Collections.sort(protoEntries, DexWriter.comparableKeyComparator()); + List> protoEntries = Lists.newArrayList(protoSection.getItems()); + Collections.sort(protoEntries, DexWriter.comparableKeyComparator()); - for (Map.Entry entry: protoEntries) { + for (Map.Entry entry: protoEntries) { entry.setValue(index++); - ProtoKey key = entry.getKey(); + ProtoRefKey key = entry.getKey(); writer.writeInt(stringSection.getItemIndex(protoSection.getShorty(key))); writer.writeInt(typeSection.getItemIndex(protoSection.getReturnType(key))); writer.writeInt(typeListSection.getNullableItemOffset(protoSection.getParameters(key))); @@ -946,7 +947,7 @@ public abstract class DexWriter< InstructionWriter instructionWriter = InstructionWriter.makeInstructionWriter(opcodes, writer, stringSection, typeSection, fieldSection, - methodSection); + methodSection, protoSection); writer.writeInt(codeUnitCount); for (Instruction instruction: instructions) { @@ -1029,6 +1030,12 @@ public abstract class DexWriter< case Format3rc: instructionWriter.write((Instruction3rc)instruction); break; + case Format45cc: + instructionWriter.write((Instruction45cc) instruction); + break; + case Format4rcc: + instructionWriter.write((Instruction4rcc) instruction); + break; case Format51l: instructionWriter.write((Instruction51l)instruction); break; 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 f16256c5..c240c515 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java @@ -36,11 +36,14 @@ import com.google.common.primitives.Ints; import org.jf.dexlib2.Opcode; import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.ReferenceType; +import org.jf.dexlib2.iface.instruction.DualReferenceInstruction; import org.jf.dexlib2.iface.instruction.ReferenceInstruction; import org.jf.dexlib2.iface.instruction.SwitchElement; import org.jf.dexlib2.iface.instruction.formats.*; import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodProtoReference; import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.iface.reference.Reference; import org.jf.dexlib2.iface.reference.StringReference; import org.jf.dexlib2.iface.reference.TypeReference; import org.jf.util.ExceptionWithContext; @@ -51,25 +54,29 @@ import java.util.Comparator; import java.util.List; public class InstructionWriter { + FieldRefKey extends FieldReference, MethodRefKey extends MethodReference, + ProtoRefKey extends MethodProtoReference> { @Nonnull private final Opcodes opcodes; @Nonnull private final DexDataWriter writer; @Nonnull private final StringSection stringSection; @Nonnull private final TypeSection typeSection; @Nonnull private final FieldSection fieldSection; @Nonnull private final MethodSection methodSection; + @Nonnull private final ProtoSection protoSection; - @Nonnull static - InstructionWriter + @Nonnull static + InstructionWriter makeInstructionWriter( @Nonnull Opcodes opcodes, @Nonnull DexDataWriter writer, @Nonnull StringSection stringSection, @Nonnull TypeSection typeSection, @Nonnull FieldSection fieldSection, - @Nonnull MethodSection methodSection) { - return new InstructionWriter( - opcodes, writer, stringSection, typeSection, fieldSection, methodSection); + @Nonnull MethodSection methodSection, + @Nonnull ProtoSection protoSection) { + return new InstructionWriter( + opcodes, writer, stringSection, typeSection, fieldSection, methodSection, protoSection); } InstructionWriter(@Nonnull Opcodes opcodes, @@ -77,13 +84,15 @@ public class InstructionWriter stringSection, @Nonnull TypeSection typeSection, @Nonnull FieldSection fieldSection, - @Nonnull MethodSection methodSection) { + @Nonnull MethodSection methodSection, + @Nonnull ProtoSection protoSection) { this.opcodes = opcodes; this.writer = writer; this.stringSection = stringSection; this.typeSection = typeSection; this.fieldSection = fieldSection; this.methodSection = methodSection; + this.protoSection = protoSection; } private short getOpcodeValue(Opcode opcode) { @@ -347,6 +356,7 @@ public class InstructionWriter +public interface MethodSection extends IndexSection { @Nonnull TypeKey getDefiningClass(@Nonnull MethodRefKey key); - @Nonnull ProtoKey getPrototype(@Nonnull MethodRefKey key); - @Nonnull ProtoKey getPrototype(@Nonnull MethodKey key); + @Nonnull ProtoRefKey getPrototype(@Nonnull MethodRefKey key); + @Nonnull ProtoRefKey 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/builder/BuilderMethodPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodPool.java index 7dc924e7..2c5dd816 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodPool.java @@ -43,7 +43,7 @@ import java.util.Map.Entry; import java.util.concurrent.ConcurrentMap; class BuilderMethodPool implements MethodSection{ + BuilderMethodProtoReference, BuilderMethodReference, BuilderMethod>{ @Nonnull private final BuilderContext context; @Nonnull private final ConcurrentMap internedItems = Maps.newConcurrentMap(); @@ -61,7 +61,7 @@ class BuilderMethodPool implements MethodSection { +public class BuilderMethodProtoReference extends BaseMethodProtoReference implements + MethodProtoReference, BuilderReference { @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) { + public BuilderMethodProtoReference(@Nonnull BuilderStringReference shorty, @Nonnull BuilderTypeList parameterTypes, + @Nonnull BuilderTypeReference returnType) { this.shorty = shorty; this.parameterTypes = parameterTypes; this.returnType = returnType; @@ -62,25 +65,12 @@ public class BuilderProtoReference implements BuilderProtoPool.ProtoKey, Compara } @Override - public int hashCode() { - int hashCode = getReturnType().hashCode(); - return hashCode*31 + getParameterTypes().hashCode(); + public int getIndex() { + return index; } @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); + public void setIndex(int index) { + this.index = index; } } 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 index c913efa5..96708444 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodReference.java @@ -39,12 +39,12 @@ import javax.annotation.Nonnull; public class BuilderMethodReference extends BaseMethodReference implements BuilderReference { @Nonnull final BuilderTypeReference definingClass; @Nonnull final BuilderStringReference name; - @Nonnull final BuilderProtoReference proto; + @Nonnull final BuilderMethodProtoReference proto; int index = DexWriter.NO_INDEX; BuilderMethodReference(@Nonnull BuilderTypeReference definingClass, @Nonnull BuilderStringReference name, - @Nonnull BuilderProtoReference proto) { + @Nonnull BuilderMethodProtoReference proto) { this.definingClass = definingClass; this.name = name; this.proto = proto; 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 index 6ed18fe8..de19fa30 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderProtoPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderProtoPool.java @@ -32,114 +32,75 @@ package org.jf.dexlib2.writer.builder; import com.google.common.collect.Maps; +import org.jf.dexlib2.iface.reference.MethodProtoReference; import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.immutable.reference.ImmutableMethodProtoReference; 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 { + implements ProtoSection { @Nonnull private final BuilderContext context; - @Nonnull private final ConcurrentMap internedItems = + @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); + @Nonnull public BuilderMethodProtoReference internMethodProto(@Nonnull MethodProtoReference methodProto) { + BuilderMethodProtoReference ret = internedItems.get(methodProto); if (ret != null) { return ret; } - BuilderProtoReference protoReference = new BuilderProtoReference( - context.stringPool.internString(MethodUtil.getShorty(parameters, returnType)), - context.typeListPool.internTypeList(parameters), - context.typePool.internType(returnType)); + BuilderMethodProtoReference protoReference = new BuilderMethodProtoReference( + context.stringPool.internString(MethodUtil.getShorty( + methodProto.getParameterTypes(), methodProto.getReturnType())), + context.typeListPool.internTypeList(methodProto.getParameterTypes()), + context.typePool.internType(methodProto.getReturnType())); ret = internedItems.putIfAbsent(protoReference, protoReference); return ret==null?protoReference:ret; } - @Nonnull public BuilderProtoReference internProto(@Nonnull MethodReference methodReference) { - return internProto(methodReference.getParameterTypes(), methodReference.getReturnType()); + @Nonnull public BuilderMethodProtoReference internMethodProto(@Nonnull MethodReference methodReference) { + return internMethodProto(new ImmutableMethodProtoReference( + methodReference.getParameterTypes(), methodReference.getReturnType())); } - @Nonnull @Override public BuilderStringReference getShorty(@Nonnull BuilderProtoReference key) { - return key.shorty; + @Nonnull @Override public BuilderStringReference getShorty(@Nonnull BuilderMethodProtoReference proto) { + return proto.shorty; } - @Nonnull @Override public BuilderTypeReference getReturnType(@Nonnull BuilderProtoReference key) { - return key.returnType; + @Nonnull @Override public BuilderTypeReference getReturnType(@Nonnull BuilderMethodProtoReference proto) { + return proto.returnType; } - @Nullable @Override public BuilderTypeList getParameters(@Nonnull BuilderProtoReference key) { - return key.parameterTypes; + @Nullable @Override public BuilderTypeList getParameters(@Nonnull BuilderMethodProtoReference proto) { + return proto.parameterTypes; } - @Override public int getItemIndex(@Nonnull BuilderProtoReference key) { - return key.index; + @Override public int getItemIndex(@Nonnull BuilderMethodProtoReference proto) { + return proto.getIndex(); } - @Nonnull @Override public Collection> getItems() { - return new BuilderMapEntryCollection(internedItems.values()) { - @Override protected int getValue(@Nonnull BuilderProtoReference key) { + @Nonnull @Override public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override protected int getValue(@Nonnull BuilderMethodProtoReference key) { return key.index; } - @Override protected int setValue(@Nonnull BuilderProtoReference key, int value) { + @Override protected int setValue(@Nonnull BuilderMethodProtoReference 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/DexBuilder.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java index d1190249..b7507fa6 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java @@ -54,7 +54,7 @@ import java.util.List; import java.util.Set; public class DexBuilder extends DexWriter { @@ -176,6 +176,10 @@ public class DexBuilder extends DexWriter, TypeListPool.Key>, Field, PoolMethod, EncodedValue, AnnotationElement> { 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 7ae42fb6..8103d319 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 @@ -31,13 +31,14 @@ package org.jf.dexlib2.writer.pool; +import org.jf.dexlib2.iface.reference.MethodProtoReference; import org.jf.dexlib2.iface.reference.MethodReference; 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; @@ -53,7 +54,7 @@ public class MethodPool extends BaseIndexPool Integer prev = internedItems.put(method, 0); if (prev == null) { typePool.intern(method.getDefiningClass()); - protoPool.intern(method); + protoPool.intern(new PoolMethodProto(method)); stringPool.intern(method.getName()); } } @@ -62,12 +63,12 @@ public class MethodPool extends BaseIndexPool return methodReference.getDefiningClass(); } - @Nonnull @Override public ProtoPool.Key getPrototype(@Nonnull MethodReference methodReference) { - return new ProtoPool.Key(methodReference); + @Nonnull @Override public MethodProtoReference getPrototype(@Nonnull MethodReference methodReference) { + return new PoolMethodProto(methodReference); } - @Nonnull @Override public ProtoPool.Key getPrototype(@Nonnull PoolMethod poolMethod) { - return new ProtoPool.Key(poolMethod); + @Nonnull @Override public MethodProtoReference getPrototype(@Nonnull PoolMethod poolMethod) { + return new PoolMethodProto(poolMethod); } @Nonnull @Override public CharSequence getName(@Nonnull MethodReference methodReference) { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolMethodProto.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolMethodProto.java new file mode 100644 index 00000000..d180be1d --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolMethodProto.java @@ -0,0 +1,56 @@ +/* + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer.pool; + +import org.jf.dexlib2.base.reference.BaseMethodProtoReference; +import org.jf.dexlib2.iface.reference.MethodProtoReference; +import org.jf.dexlib2.iface.reference.MethodReference; + +import java.util.List; + +public class PoolMethodProto extends BaseMethodProtoReference implements MethodProtoReference { + private final MethodReference methodReference; + + public PoolMethodProto(MethodReference methodReference) { + this.methodReference = methodReference; + } + + @Override + public List getParameterTypes() { + return methodReference.getParameterTypes(); + } + + @Override + public String getReturnType() { + return methodReference.getReturnType(); + } +} 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 eeabdf4a..523e5f4d 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 @@ -31,21 +31,18 @@ package org.jf.dexlib2.writer.pool; -import com.google.common.collect.Ordering; -import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.iface.reference.MethodProtoReference; 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; -import org.jf.util.CollectionUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collection; import java.util.List; -public class ProtoPool extends BaseIndexPool - implements ProtoSection>> { +public class ProtoPool extends BaseIndexPool + implements ProtoSection>> { @Nonnull private final StringPool stringPool; @Nonnull private final TypePool typePool; @Nonnull private final TypeListPool typeListPool; @@ -57,78 +54,25 @@ public class ProtoPool extends BaseIndexPool this.typeListPool = typeListPool; } - public void intern(@Nonnull MethodReference method) { - // We can't use method directly, because it is likely a full MethodReference. We use a wrapper that computes - // hashCode and equals based only on the prototype fields - Key key = new Key(method); - Integer prev = internedItems.put(key, 0); + public void intern(@Nonnull MethodProtoReference reference) { + Integer prev = internedItems.put(reference, 0); if (prev == null) { - stringPool.intern(key.getShorty()); - typePool.intern(method.getReturnType()); - typeListPool.intern(method.getParameterTypes()); + stringPool.intern(getShorty(reference)); + typePool.intern(reference.getReturnType()); + typeListPool.intern(reference.getParameterTypes()); } } - @Nonnull @Override public CharSequence getShorty(@Nonnull Key key) { - return key.getShorty(); + @Nonnull @Override public CharSequence getShorty(@Nonnull MethodProtoReference reference) { + return MethodUtil.getShorty(reference.getParameterTypes(), reference.getReturnType()); } - @Nonnull @Override public CharSequence getReturnType(@Nonnull Key key) { - return key.getReturnType(); + @Nonnull @Override public CharSequence getReturnType(@Nonnull MethodProtoReference protoReference) { + return protoReference.getReturnType(); } - @Nullable @Override public TypeListPool.Key> getParameters(@Nonnull Key key) { - return new TypeListPool.Key>(key.getParameters()); - } - - public static class Key implements Comparable { - @Nonnull private final MethodReference method; - - public Key(@Nonnull MethodReference method) { - this.method = method; - } - - @Nonnull public String getReturnType() { return method.getReturnType(); } - @Nonnull public List getParameters() { - return method.getParameterTypes(); - } - - public String getShorty() { - return MethodUtil.getShorty(method.getParameterTypes(), method.getReturnType()); - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append('('); - for (CharSequence paramType: getParameters()) { - sb.append(paramType); - } - sb.append(')'); - sb.append(getReturnType()); - return sb.toString(); - } - - @Override - public int hashCode() { - int hashCode = getReturnType().hashCode(); - return hashCode*31 + CharSequenceUtils.listHashCode(getParameters()); - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof Key) { - Key other = (Key)o; - return getReturnType().equals(other.getReturnType()) && - CharSequenceUtils.listEquals(getParameters(), other.getParameters()); - } - return false; - } - - @Override - public int compareTo(@Nonnull Key o) { - int res = getReturnType().compareTo(o.getReturnType()); - if (res != 0) return res; - return CollectionUtils.compareAsIterable(Ordering.usingToString(), getParameters(), o.getParameters()); - } + @Nullable @Override public TypeListPool.Key> getParameters( + @Nonnull MethodProtoReference methodProto) { + return new TypeListPool.Key>(methodProto.getParameterTypes()); } } diff --git a/smali/src/main/antlr/smaliParser.g b/smali/src/main/antlr/smaliParser.g index fcccbe80..29148f7b 100644 --- a/smali/src/main/antlr/smaliParser.g +++ b/smali/src/main/antlr/smaliParser.g @@ -120,6 +120,8 @@ tokens { INSTRUCTION_FORMAT3rc_TYPE; INSTRUCTION_FORMAT3rmi_METHOD; INSTRUCTION_FORMAT3rms_METHOD; + INSTRUCTION_FORMAT45cc_METHOD; + INSTRUCTION_FORMAT4rcc_METHOD; INSTRUCTION_FORMAT51l; LINE_COMMENT; LINE_DIRECTIVE; @@ -234,6 +236,8 @@ tokens { I_STATEMENT_FORMAT35c_TYPE; I_STATEMENT_FORMAT3rc_METHOD; I_STATEMENT_FORMAT3rc_TYPE; + I_STATEMENT_FORMAT45cc_METHOD; + I_STATEMENT_FORMAT4rcc_METHOD; I_STATEMENT_FORMAT51l; I_STATEMENT_ARRAY_DATA; I_STATEMENT_PACKED_SWITCH; @@ -581,6 +585,8 @@ simple_name | INSTRUCTION_FORMAT35c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_TYPE] | INSTRUCTION_FORMAT35mi_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35mi_METHOD] | INSTRUCTION_FORMAT35ms_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35ms_METHOD] + | INSTRUCTION_FORMAT45cc_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT45cc_METHOD] + | INSTRUCTION_FORMAT4rcc_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT4rcc_METHOD] | INSTRUCTION_FORMAT51l -> SIMPLE_NAME[$INSTRUCTION_FORMAT51l]; member_name @@ -849,6 +855,8 @@ instruction | insn_format3rc_type | insn_format3rmi_method | insn_format3rms_method + | insn_format45cc_method + | insn_format4rcc_method | insn_format51l | insn_array_data_directive | insn_packed_switch_directive @@ -1106,6 +1114,16 @@ insn_format3rms_method throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rms_METHOD.text); }; +insn_format45cc_method + : //e.g. invoke-polymorphic {v0..v1}, java/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (I)J + INSTRUCTION_FORMAT45cc_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA method_reference COMMA method_prototype + -> ^(I_STATEMENT_FORMAT45cc_METHOD[$start, "I_STATEMENT_FORMAT45cc_METHOD"] INSTRUCTION_FORMAT45cc_METHOD register_list method_reference method_prototype); + +insn_format4rcc_method + : //e.g. invoke-polymorphic/range {v0,v1}, java/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (I)J + INSTRUCTION_FORMAT4rcc_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA method_reference COMMA method_prototype + -> ^(I_STATEMENT_FORMAT4rcc_METHOD[$start, "I_STATEMENT_FORMAT4rcc_METHOD"] INSTRUCTION_FORMAT4rcc_METHOD register_range method_reference method_prototype); + insn_format51l : //e.g. const-wide v0, 5000000000L INSTRUCTION_FORMAT51l REGISTER COMMA fixed_literal @@ -1141,4 +1159,4 @@ insn_sparse_switch_directive (fixed_32bit_literal ARROW label_ref)* END_SPARSE_SWITCH_DIRECTIVE -> ^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"] - ^(I_SPARSE_SWITCH_ELEMENTS[$start, "I_SPARSE_SWITCH_ELEMENTS"] (fixed_32bit_literal label_ref)*)); \ No newline at end of file + ^(I_SPARSE_SWITCH_ELEMENTS[$start, "I_SPARSE_SWITCH_ELEMENTS"] (fixed_32bit_literal label_ref)*)); diff --git a/smali/src/main/antlr/smaliTreeWalker.g b/smali/src/main/antlr/smaliTreeWalker.g index c3a50994..7f5657a5 100644 --- a/smali/src/main/antlr/smaliTreeWalker.g +++ b/smali/src/main/antlr/smaliTreeWalker.g @@ -62,6 +62,7 @@ import org.jf.dexlib2.immutable.ImmutableAnnotation; import org.jf.dexlib2.immutable.ImmutableAnnotationElement; import org.jf.dexlib2.immutable.reference.ImmutableFieldReference; import org.jf.dexlib2.immutable.reference.ImmutableMethodReference; +import org.jf.dexlib2.immutable.reference.ImmutableMethodProtoReference; import org.jf.dexlib2.immutable.reference.ImmutableReference; import org.jf.dexlib2.immutable.reference.ImmutableTypeReference; import org.jf.dexlib2.immutable.value.*; @@ -766,6 +767,8 @@ instruction | insn_format35c_type | insn_format3rc_method | insn_format3rc_type + | insn_format45cc_method + | insn_format4rcc_method | insn_format51l_type | insn_array_data_directive | insn_packed_switch_directive @@ -1181,6 +1184,47 @@ insn_format3rc_type dexBuilder.internTypeReference($nonvoid_type_descriptor.type))); }; +insn_format45cc_method + : //e.g. invoke-polymorphic {v0, v1}, java/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (I)J + ^(I_STATEMENT_FORMAT45cc_METHOD INSTRUCTION_FORMAT45cc_METHOD register_list method_reference method_prototype) + { + Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT45cc_METHOD.text); + + //this depends on the fact that register_list returns a byte[5] + byte[] registers = $register_list.registers; + byte registerCount = $register_list.registerCount; + + ImmutableMethodReference methodReference = $method_reference.methodReference; + ImmutableMethodProtoReference methodProtoReference = new ImmutableMethodProtoReference( + $method_prototype.parameters, + $method_prototype.returnType); + + $method::methodBuilder.addInstruction(new BuilderInstruction45cc(opcode, registerCount, registers[0], registers[1], + registers[2], registers[3], registers[4], + dexBuilder.internMethodReference(methodReference), + dexBuilder.internMethodProtoReference(methodProtoReference))); + }; + +insn_format4rcc_method + : //e.g. invoke-polymorphic {v0..v1}, java/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (I)J + ^(I_STATEMENT_FORMAT4rcc_METHOD INSTRUCTION_FORMAT4rcc_METHOD register_range method_reference method_prototype) + { + Opcode opcode = opcodes.getOpcodeByName($INSTRUCTION_FORMAT4rcc_METHOD.text); + int startRegister = $register_range.startRegister; + int endRegister = $register_range.endRegister; + + int registerCount = endRegister-startRegister+1; + + ImmutableMethodReference methodReference = $method_reference.methodReference; + ImmutableMethodProtoReference methodProtoReference = new ImmutableMethodProtoReference( + $method_prototype.parameters, + $method_prototype.returnType); + + $method::methodBuilder.addInstruction(new BuilderInstruction4rcc(opcode, startRegister, registerCount, + dexBuilder.internMethodReference(methodReference), + dexBuilder.internMethodProtoReference(methodProtoReference))); + }; + insn_format51l_type : //e.g. const-wide v0, 5000000000L ^(I_STATEMENT_FORMAT51l INSTRUCTION_FORMAT51l REGISTER fixed_64bit_literal) diff --git a/smali/src/main/jflex/smaliLexer.jflex b/smali/src/main/jflex/smaliLexer.jflex index 2f57a438..e0010a93 100644 --- a/smali/src/main/jflex/smaliLexer.jflex +++ b/smali/src/main/jflex/smaliLexer.jflex @@ -624,6 +624,14 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayPrefix} ({ClassDescriptor} | return newToken(INSTRUCTION_FORMAT3rms_METHOD); } + "invoke-polymorphic" { + return newToken(INSTRUCTION_FORMAT45cc_METHOD); + } + + "invoke-polymorphic/range" { + return newToken(INSTRUCTION_FORMAT4rcc_METHOD); + } + "const-wide" { return newToken(INSTRUCTION_FORMAT51l); }