From 5c5e4ae279738f36d2a7cb4dfcd397daa1cb4268 Mon Sep 17 00:00:00 2001 From: Ben Gruver Date: Fri, 2 Feb 2018 13:06:52 -0800 Subject: [PATCH] Implement support for new structures in DexWriter --- .../main/java/org/jf/dexlib2/VersionMap.java | 15 +- .../jf/dexlib2/dexbacked/raw/HeaderItem.java | 6 +- .../reference/DexBackedReference.java | 2 + .../jf/dexlib2/writer/CallSiteSection.java | 39 +++++ .../java/org/jf/dexlib2/writer/DexWriter.java | 110 ++++++++++++-- .../jf/dexlib2/writer/EncodedValueWriter.java | 17 ++- .../jf/dexlib2/writer/InstructionWriter.java | 30 ++-- .../dexlib2/writer/MethodHandleSection.java | 42 ++++++ .../writer/builder/BuilderCallSitePool.java | 98 +++++++++++++ .../builder/BuilderCallSiteReference.java | 88 ++++++++++++ .../writer/builder/BuilderEncodedValues.java | 22 +++ .../builder/BuilderMethodHandlePool.java | 124 ++++++++++++++++ .../builder/BuilderMethodHandleReference.java | 65 +++++++++ .../jf/dexlib2/writer/builder/DexBuilder.java | 51 ++++++- .../jf/dexlib2/writer/pool/CallSitePool.java | 59 ++++++++ .../org/jf/dexlib2/writer/pool/ClassPool.java | 3 + .../org/jf/dexlib2/writer/pool/DexPool.java | 24 +++- .../dexlib2/writer/pool/MethodHandlePool.java | 79 ++++++++++ .../jf/dexlib2/writer/util/CallSiteUtil.java | 76 ++++++++++ .../org/jf/dexlib2/writer/CallSiteTest.java | 135 ++++++++++++++++++ 20 files changed, 1046 insertions(+), 39 deletions(-) create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/CallSiteSection.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/MethodHandleSection.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderCallSitePool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderCallSiteReference.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodHandlePool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodHandleReference.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/pool/CallSitePool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/pool/MethodHandlePool.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/writer/util/CallSiteUtil.java create mode 100644 dexlib2/src/test/java/org/jf/dexlib2/writer/CallSiteTest.java diff --git a/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java b/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java index b3581876..550f7547 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java @@ -35,10 +35,7 @@ public class VersionMap { public static final int NO_VERSION = -1; public static int mapArtVersionToApi(int artVersion) { - // NOTE: Art version 87 and api level 26 do not correspond to any - // particular android release and represent the current (as of - // October 2016) state of aosp/master. - if (artVersion >= 87) { + if (artVersion >= 124) { return 26; } if (artVersion >= 79) { @@ -70,13 +67,15 @@ public class VersionMap { case 24: case 25: return 79; + case 26: + return 124; } - // NOTE: Art version 87 and api level 26 do not correspond to any + // NOTE: Art version 143 and api level 27 do not correspond to any // particular android release and represent the current (as of - // October 2016) state of aosp/master. - if (api > 25) { - return 87; + // May 2018) state of aosp/master. + if (api > 26) { + return 143; } return NO_VERSION; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java index a88f509e..618c7521 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java @@ -43,7 +43,7 @@ public class HeaderItem { public static final int ITEM_SIZE = 0x70; private static final byte[] MAGIC_VALUE = new byte[] { 0x64, 0x65, 0x78, 0x0a, 0x00, 0x00, 0x00, 0x00 }; - private static final int[] SUPPORTED_DEX_VERSIONS = new int[] { 35, 37 }; + private static final int[] SUPPORTED_DEX_VERSIONS = new int[] { 35, 37, 38 }; public static final int LITTLE_ENDIAN_TAG = 0x12345678; public static final int BIG_ENDIAN_TAG = 0x78563412; @@ -232,9 +232,11 @@ public class HeaderItem { if (api < 24) { // Prior to Android N we only support dex version 035. return getMagicForDexVersion(35); - } else { + } if (api < 26) { // On android N and later we support dex version 037. return getMagicForDexVersion(37); + } else { + return getMagicForDexVersion(38); } } 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 99d66ecd..89d00900 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 @@ -51,6 +51,8 @@ public abstract class DexBackedReference { return new DexBackedFieldReference(dexFile, referenceIndex); case ReferenceType.METHOD_PROTO: return new DexBackedMethodProtoReference(dexFile, referenceIndex); + case ReferenceType.CALL_SITE: + return new DexBackedCallSiteReference(dexFile, referenceIndex); default: throw new ExceptionWithContext("Invalid reference type: %d", referenceType); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/CallSiteSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/CallSiteSection.java new file mode 100644 index 00000000..0860de4a --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/CallSiteSection.java @@ -0,0 +1,39 @@ +/* + * Copyright 2018, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import org.jf.dexlib2.iface.reference.CallSiteReference; + +public interface CallSiteSection + extends IndexSection { + EncodedArrayKey getEncodedCallSite(CallSiteKey callSiteReference); +} 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 aad36f71..84f57f2d 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java @@ -35,10 +35,8 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; -import org.jf.dexlib2.AccessFlags; -import org.jf.dexlib2.Opcode; -import org.jf.dexlib2.Opcodes; -import org.jf.dexlib2.ReferenceType; +import com.google.common.primitives.Ints; +import org.jf.dexlib2.*; import org.jf.dexlib2.base.BaseAnnotation; import org.jf.dexlib2.base.BaseAnnotationElement; import org.jf.dexlib2.builder.MutableMethodImplementation; @@ -84,6 +82,8 @@ public abstract class DexWriter< TypeRef extends TypeReference, ProtoRefKey extends MethodProtoReference, FieldRefKey extends FieldReference, MethodRefKey extends MethodReference, ClassKey extends Comparable, + CallSiteKey extends CallSiteReference, + MethodHandleKey extends MethodHandleReference, AnnotationKey extends Annotation, AnnotationSetKey, TypeListKey, FieldKey, MethodKey, @@ -97,6 +97,8 @@ public abstract class DexWriter< MethodSectionType extends MethodSection, ClassSectionType extends ClassSection, + CallSiteSectionType extends CallSiteSection, + MethodHandleSectionType extends MethodHandleSection, TypeListSectionType extends TypeListSection, AnnotationSectionType extends AnnotationSection, @@ -113,6 +115,8 @@ public abstract class DexWriter< protected int fieldSectionOffset = NO_OFFSET; protected int methodSectionOffset = NO_OFFSET; protected int classIndexSectionOffset = NO_OFFSET; + protected int callSiteSectionOffset = NO_OFFSET; + protected int methodHandleSectionOffset = NO_OFFSET; protected int stringDataSectionOffset = NO_OFFSET; protected int classDataSectionOffset = NO_OFFSET; @@ -138,6 +142,8 @@ public abstract class DexWriter< public final FieldSectionType fieldSection; public final MethodSectionType methodSection; public final ClassSectionType classSection; + public final CallSiteSectionType callSiteSection; + public final MethodHandleSectionType methodHandleSection; public final TypeListSectionType typeListSection; public final AnnotationSectionType annotationSection; @@ -154,6 +160,8 @@ public abstract class DexWriter< this.fieldSection = sectionProvider.getFieldSection(); this.methodSection = sectionProvider.getMethodSection(); this.classSection = sectionProvider.getClassSection(); + this.callSiteSection = sectionProvider.getCallSiteSection(); + this.methodHandleSection = sectionProvider.getMethodHandleSection(); this.typeListSection = sectionProvider.getTypeListSection(); this.annotationSection = sectionProvider.getAnnotationSection(); this.annotationSetSection = sectionProvider.getAnnotationSetSection(); @@ -165,6 +173,16 @@ public abstract class DexWriter< protected abstract void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer, @Nonnull EncodedValue encodedValue) throws IOException; + private Comparator> callSiteComparator = + new Comparator>() { + @Override + public int compare(Entry o1, Entry o2) { + int offset1 = encodedArraySection.getItemOffset(callSiteSection.getEncodedCallSite(o1.getKey())); + int offset2 = encodedArraySection.getItemOffset(callSiteSection.getEncodedCallSite(o2.getKey())); + return Ints.compare(offset1, offset2); + } + }; + private static Comparator toStringKeyComparator = new Comparator() { @Override public int compare(Entry o1, Entry o2) { @@ -181,9 +199,10 @@ public abstract class DexWriter< } protected class InternalEncodedValueWriter extends EncodedValueWriter { + AnnotationElement, ProtoRefKey, MethodHandleKey, EncodedValue> { private InternalEncodedValueWriter(@Nonnull DexDataWriter writer) { - super(writer, stringSection, typeSection, fieldSection, methodSection, annotationSection); + super(writer, stringSection, typeSection, fieldSection, methodSection, protoSection, methodHandleSection, + annotationSection); } @Override protected void writeEncodedValue(@Nonnull EncodedValue encodedValue) throws IOException { @@ -198,7 +217,9 @@ public abstract class DexWriter< protoSection.getItemCount() * ProtoIdItem.ITEM_SIZE + fieldSection.getItemCount() * FieldIdItem.ITEM_SIZE + methodSection.getItemCount() * MethodIdItem.ITEM_SIZE + - classSection.getItemCount() * ClassDefItem.ITEM_SIZE; + classSection.getItemCount() * ClassDefItem.ITEM_SIZE + + callSiteSection.getItemCount() * CallSiteIdItem.ITEM_SIZE + + methodHandleSection.getItemCount() * MethodHandleItem.ITEM_SIZE; } @Nonnull @@ -262,14 +283,36 @@ public abstract class DexWriter< writeProtos(indexWriter); writeFields(indexWriter); writeMethods(indexWriter); + + // encoded arrays depend on method handles.. + DexDataWriter methodHandleWriter = outputAt(dest, indexWriter.getPosition() + + classSection.getItemCount() * ClassDefItem.ITEM_SIZE + + callSiteSection.getItemCount() * CallSiteIdItem.ITEM_SIZE); + try { + writeMethodHandles(methodHandleWriter); + } finally { + methodHandleWriter.close(); + } + + // call sites depend on encoded arrays.. writeEncodedArrays(offsetWriter); + + // class defs depend on method handles and call sites.. + DexDataWriter callSiteWriter = outputAt(dest, indexWriter.getPosition() + + classSection.getItemCount() * ClassDefItem.ITEM_SIZE); + try { + writeCallSites(callSiteWriter); + } finally { + callSiteWriter.close(); + } + writeAnnotations(offsetWriter); writeAnnotationSets(offsetWriter); writeAnnotationSetRefs(offsetWriter); writeAnnotationDirectories(offsetWriter); writeDebugAndCodeItems(offsetWriter, tempFactory.makeDeferredOutputStream()); - writeEncodedArrays(offsetWriter); writeClasses(indexWriter, offsetWriter); + writeMapItem(offsetWriter); writeHeader(headerWriter, dataSectionOffset, offsetWriter.getPosition()); } finally { @@ -511,6 +554,55 @@ public abstract class DexWriter< return nextIndex; } + private void writeCallSites(DexDataWriter writer) throws IOException { + callSiteSectionOffset = writer.getPosition(); + + List> callSiteEntries = + Lists.newArrayList(callSiteSection.getItems()); + Collections.sort(callSiteEntries, callSiteComparator); + + int index = 0; + + for (Map.Entry callSite: callSiteEntries) { + callSite.setValue(index++); + writer.writeInt(encodedArraySection.getItemOffset(callSiteSection.getEncodedCallSite(callSite.getKey()))); + } + } + + private void writeMethodHandles(DexDataWriter writer) throws IOException { + methodHandleSectionOffset = writer.getPosition(); + + int index = 0; + + for (Entry entry: methodHandleSection.getItems()) { + entry.setValue(index++); + MethodHandleKey methodHandleReference = entry.getKey(); + writer.writeUshort(methodHandleReference.getMethodHandleType()); + writer.writeUshort(0); + int memberIndex; + switch (methodHandleReference.getMethodHandleType()) { + case MethodHandleType.INSTANCE_GET: + case MethodHandleType.INSTANCE_PUT: + case MethodHandleType.STATIC_GET: + case MethodHandleType.STATIC_PUT: + memberIndex = fieldSection.getItemIndex( + methodHandleSection.getFieldReference(methodHandleReference)); + break; + case MethodHandleType.INVOKE_INSTANCE: + case MethodHandleType.INVOKE_STATIC: + memberIndex = methodSection.getItemIndex( + methodHandleSection.getMethodReference(methodHandleReference)); + break; + default: + throw new ExceptionWithContext("Invalid method handle type: %d", + methodHandleReference.getMethodHandleType()); + } + + writer.writeUshort(memberIndex); + writer.writeUshort(0); + } + } + private void writeEncodedFields(@Nonnull DexDataWriter writer, @Nonnull Collection fields) throws IOException { int prevIndex = 0; @@ -1311,6 +1403,8 @@ public abstract class DexWriter< @Nonnull public abstract FieldSectionType getFieldSection(); @Nonnull public abstract MethodSectionType getMethodSection(); @Nonnull public abstract ClassSectionType getClassSection(); + @Nonnull public abstract CallSiteSectionType getCallSiteSection(); + @Nonnull public abstract MethodHandleSectionType getMethodHandleSection(); @Nonnull public abstract TypeListSectionType getTypeListSection(); @Nonnull public abstract AnnotationSectionType getAnnotationSection(); @Nonnull public abstract AnnotationSetSectionType getAnnotationSetSection(); 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 def326c6..3032acb6 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedValueWriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/EncodedValueWriter.java @@ -35,6 +35,7 @@ import com.google.common.collect.Ordering; import org.jf.dexlib2.ValueType; import org.jf.dexlib2.base.BaseAnnotationElement; import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodHandleReference; import org.jf.dexlib2.iface.reference.MethodReference; import javax.annotation.Nonnull; @@ -43,12 +44,14 @@ import java.util.Collection; public abstract class EncodedValueWriter { + ProtoRefKey, MethodHandleKey extends MethodHandleReference, EncodedValue> { @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 private final MethodHandleSection methodHandleSection; @Nonnull private final AnnotationSection annotationSection; public EncodedValueWriter( @@ -57,12 +60,16 @@ public abstract class EncodedValueWriter typeSection, @Nonnull FieldSection fieldSection, @Nonnull MethodSection methodSection, + ProtoSection protoSection, + MethodHandleSection methodHandleSection, @Nonnull AnnotationSection annotationSection) { this.writer = writer; this.stringSection = stringSection; this.typeSection = typeSection; this.fieldSection = fieldSection; this.methodSection = methodSection; + this.protoSection = protoSection; + this.methodHandleSection = methodHandleSection; this.annotationSection = annotationSection; } @@ -146,4 +153,12 @@ public abstract class EncodedValueWriter { + ProtoRefKey extends MethodProtoReference, CallSiteKey extends CallSiteReference> { @Nonnull private final Opcodes opcodes; @Nonnull private final DexDataWriter writer; @Nonnull private final StringSection stringSection; @@ -63,10 +58,12 @@ public class InstructionWriter fieldSection; @Nonnull private final MethodSection methodSection; @Nonnull private final ProtoSection protoSection; + @Nonnull private final CallSiteSection callSiteSection; - @Nonnull static - InstructionWriter + @Nonnull static + InstructionWriter makeInstructionWriter( @Nonnull Opcodes opcodes, @Nonnull DexDataWriter writer, @@ -74,9 +71,10 @@ public class InstructionWriter typeSection, @Nonnull FieldSection fieldSection, @Nonnull MethodSection methodSection, - @Nonnull ProtoSection protoSection) { - return new InstructionWriter( - opcodes, writer, stringSection, typeSection, fieldSection, methodSection, protoSection); + @Nonnull ProtoSection protoSection, + @Nonnull CallSiteSection callSiteSection) { + return new InstructionWriter( + opcodes, writer, stringSection, typeSection, fieldSection, methodSection, protoSection, callSiteSection); } InstructionWriter(@Nonnull Opcodes opcodes, @@ -85,7 +83,8 @@ public class InstructionWriter typeSection, @Nonnull FieldSection fieldSection, @Nonnull MethodSection methodSection, - @Nonnull ProtoSection protoSection) { + @Nonnull ProtoSection protoSection, + @Nonnull CallSiteSection callSiteSection) { this.opcodes = opcodes; this.writer = writer; this.stringSection = stringSection; @@ -93,6 +92,7 @@ public class InstructionWriter extends IndexSection { + FieldRefKey getFieldReference(MethodHandleKey methodHandleReference); + MethodRefKey getMethodReference(MethodHandleKey methodHandleReference); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderCallSitePool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderCallSitePool.java new file mode 100644 index 00000000..0c5c6aef --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderCallSitePool.java @@ -0,0 +1,98 @@ +/* + * Copyright 2018, 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.CallSiteReference; +import org.jf.dexlib2.writer.CallSiteSection; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderArrayEncodedValue; +import org.jf.dexlib2.writer.util.CallSiteUtil; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +public class BuilderCallSitePool extends BaseBuilderPool + implements CallSiteSection { + @Nonnull private final ConcurrentMap internedItems = + Maps.newConcurrentMap(); + + public BuilderCallSitePool(@Nonnull DexBuilder dexBuilder) { + super(dexBuilder); + } + + @Nonnull public BuilderCallSiteReference internCallSite(@Nonnull CallSiteReference callSiteReference) { + BuilderCallSiteReference internedCallSite = internedItems.get(callSiteReference); + if (internedCallSite != null) { + return internedCallSite; + } + BuilderArrayEncodedValue encodedCallSite = dexBuilder.encodedArraySection.internArrayEncodedValue( + CallSiteUtil.getEncodedCallSite(callSiteReference)); + internedCallSite = new BuilderCallSiteReference(callSiteReference.getName(), encodedCallSite); + BuilderCallSiteReference existing = internedItems.putIfAbsent(internedCallSite, internedCallSite); + return existing == null ? internedCallSite : existing; + } + + @Override + public BuilderArrayEncodedValue getEncodedCallSite(BuilderCallSiteReference callSiteReference) { + return callSiteReference.encodedCallSite; + } + + @Override + public int getItemIndex(@Nonnull BuilderCallSiteReference builderCallSite) { + return builderCallSite.index; + } + + @Nonnull + @Override + public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override + protected int getValue(@Nonnull BuilderCallSiteReference builderCallSiteReference) { + return builderCallSiteReference.index; + } + + @Override + protected int setValue(@Nonnull BuilderCallSiteReference builderCallSiteReference, int value) { + int prev = builderCallSiteReference.index; + builderCallSiteReference.index = value; + return prev; + } + }; + } + + @Override + public int getItemCount() { + return internedItems.size(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderCallSiteReference.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderCallSiteReference.java new file mode 100644 index 00000000..76e5b38f --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderCallSiteReference.java @@ -0,0 +1,88 @@ +/* + * Copyright 2018, 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.base.reference.BaseCallSiteReference; +import org.jf.dexlib2.iface.value.StringEncodedValue; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderArrayEncodedValue; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderMethodHandleEncodedValue; +import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderMethodTypeEncodedValue; + +import javax.annotation.Nonnull; +import java.util.List; + +import static org.jf.dexlib2.writer.DexWriter.NO_INDEX; + +public class BuilderCallSiteReference extends BaseCallSiteReference implements BuilderReference { + @Nonnull final String name; + @Nonnull final BuilderArrayEncodedValue encodedCallSite; + int index = NO_INDEX; + + public BuilderCallSiteReference(@Nonnull String name, + @Nonnull BuilderArrayEncodedValue encodedCallSite) { + this.name = name; + this.encodedCallSite = encodedCallSite; + } + + @Nonnull @Override public String getName() { + return name; + } + + @Nonnull @Override public BuilderMethodHandleReference getMethodHandle() { + return ((BuilderMethodHandleEncodedValue) encodedCallSite.elements.get(0)).getValue(); + } + + @Nonnull @Override public String getMethodName() { + return ((StringEncodedValue) encodedCallSite.elements.get(1)).getValue(); + } + + @Nonnull @Override public BuilderMethodProtoReference getMethodProto() { + return ((BuilderMethodTypeEncodedValue) encodedCallSite.elements.get(2)).getValue(); + } + + @Nonnull @Override public List getExtraArguments() { + if (encodedCallSite.elements.size() <= 3) { + return ImmutableList.of(); + } + return encodedCallSite.elements.subList(3, encodedCallSite.elements.size()); + } + + @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/BuilderEncodedValues.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderEncodedValues.java index 1cee858b..5fae60c2 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderEncodedValues.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderEncodedValues.java @@ -241,4 +241,26 @@ public abstract class BuilderEncodedValues { return typeReference.getType(); } } + + public static class BuilderMethodTypeEncodedValue extends BaseMethodTypeEncodedValue + implements BuilderEncodedValue { + @Nonnull final BuilderMethodProtoReference methodProtoReference; + + public BuilderMethodTypeEncodedValue(@Nonnull BuilderMethodProtoReference methodProtoReference) { + this.methodProtoReference = methodProtoReference; + } + + @Nonnull @Override public BuilderMethodProtoReference getValue() { return methodProtoReference; } + } + + public static class BuilderMethodHandleEncodedValue extends BaseMethodHandleEncodedValue + implements BuilderEncodedValue { + @Nonnull final BuilderMethodHandleReference methodHandleReference; + + public BuilderMethodHandleEncodedValue(@Nonnull BuilderMethodHandleReference methodHandleReference) { + this.methodHandleReference = methodHandleReference; + } + + @Nonnull @Override public BuilderMethodHandleReference getValue() { return methodHandleReference; } + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodHandlePool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodHandlePool.java new file mode 100644 index 00000000..17f6c01e --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodHandlePool.java @@ -0,0 +1,124 @@ +/* + * Copyright 2018, 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.MethodHandleType; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodHandleReference; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.writer.MethodHandleSection; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +public class BuilderMethodHandlePool extends BaseBuilderPool + implements MethodHandleSection { + @Nonnull private final ConcurrentMap internedItems = + Maps.newConcurrentMap(); + + public BuilderMethodHandlePool(@Nonnull DexBuilder dexBuilder) { + super(dexBuilder); + } + + public BuilderMethodHandleReference internMethodHandle(MethodHandleReference methodHandleReference) { + BuilderMethodHandleReference internedMethodHandle = internedItems.get(methodHandleReference); + if (internedMethodHandle != null) { + return internedMethodHandle; + } + + BuilderReference memberReference; + switch (methodHandleReference.getMethodHandleType()) { + case MethodHandleType.INSTANCE_GET: + case MethodHandleType.INSTANCE_PUT: + case MethodHandleType.STATIC_GET: + case MethodHandleType.STATIC_PUT: + memberReference = dexBuilder.internFieldReference( + (FieldReference) methodHandleReference.getMemberReference()); + break; + case MethodHandleType.INVOKE_INSTANCE: + case MethodHandleType.INVOKE_STATIC: + memberReference = dexBuilder.internMethodReference( + (MethodReference) methodHandleReference.getMemberReference()); + break; + default: + throw new ExceptionWithContext("Invalid method handle type: %d", + methodHandleReference.getMethodHandleType()); + } + + internedMethodHandle = new BuilderMethodHandleReference(methodHandleReference.getMethodHandleType(), + memberReference); + BuilderMethodHandleReference prev = internedItems.putIfAbsent(internedMethodHandle, internedMethodHandle); + return prev == null ? internedMethodHandle : prev; + } + + @Override + public BuilderFieldReference getFieldReference(BuilderMethodHandleReference methodHandleReference) { + return (BuilderFieldReference) methodHandleReference.getMemberReference(); + } + + @Override + public BuilderMethodReference getMethodReference(BuilderMethodHandleReference methodHandleReference) { + return (BuilderMethodReference) methodHandleReference.getMemberReference(); + } + + @Override + public int getItemIndex(@Nonnull BuilderMethodHandleReference builderMethodHandleReference) { + return builderMethodHandleReference.index; + } + + @Nonnull + @Override + public Collection> getItems() { + return new BuilderMapEntryCollection(internedItems.values()) { + @Override + protected int getValue(@Nonnull BuilderMethodHandleReference builderMethodHandleReference) { + return builderMethodHandleReference.index; + } + + @Override + protected int setValue(@Nonnull BuilderMethodHandleReference builderMethodHandleReference, int value) { + int prev = builderMethodHandleReference.index; + builderMethodHandleReference.index = value; + return prev; + } + }; + } + + @Override + public int getItemCount() { + return internedItems.size(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodHandleReference.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodHandleReference.java new file mode 100644 index 00000000..4fe0e300 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderMethodHandleReference.java @@ -0,0 +1,65 @@ +/* + * Copyright 2018, 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.BaseMethodHandleReference; + +import javax.annotation.Nonnull; + +import static org.jf.dexlib2.writer.DexWriter.NO_INDEX; + +public class BuilderMethodHandleReference extends BaseMethodHandleReference implements BuilderReference { + final int methodHandleType; + @Nonnull final BuilderReference memberReference; + int index = NO_INDEX; + + public BuilderMethodHandleReference(int methodHandleType, @Nonnull BuilderReference memberReference) { + this.methodHandleType = methodHandleType; + this.memberReference = memberReference; + } + + @Override public int getMethodHandleType() { + return methodHandleType; + } + + @Nonnull @Override public BuilderReference getMemberReference() { + return memberReference; + } + + @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 index ec25be1f..60377882 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 @@ -56,10 +56,11 @@ import java.util.Set; public class DexBuilder extends DexWriter { + BuilderClassDef, BuilderCallSiteReference, BuilderMethodHandleReference, BuilderAnnotation, BuilderAnnotationSet, BuilderTypeList, + BuilderField, BuilderMethod, BuilderArrayEncodedValue, BuilderEncodedValue, BuilderAnnotationElement, + BuilderStringPool, BuilderTypePool, BuilderProtoPool, BuilderFieldPool, BuilderMethodPool, BuilderClassPool, + BuilderCallSitePool, BuilderMethodHandlePool, BuilderTypeListPool, BuilderAnnotationPool, + BuilderAnnotationSetPool, BuilderEncodedArrayPool> { public DexBuilder(@Nonnull Opcodes opcodes) { super(opcodes); @@ -145,6 +146,14 @@ public class DexBuilder extends DexWriter + implements CallSiteSection { + + public CallSitePool(@Nonnull DexPool dexPool) { + super(dexPool); + } + + public void intern(CallSiteReference callSiteReference) { + Integer prev = internedItems.put(callSiteReference, 0); + if (prev == null) { + dexPool.encodedArraySection.intern(getEncodedCallSite(callSiteReference)); + } + } + + @Override + public ArrayEncodedValue getEncodedCallSite(CallSiteReference callSiteReference) { + return CallSiteUtil.getEncodedCallSite(callSiteReference); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java index c1886e78..0befc5c1 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java @@ -146,6 +146,9 @@ public class ClassPool extends BasePool implements ClassSe case ReferenceType.METHOD: dexPool.methodSection.intern((MethodReference)reference); break; + case ReferenceType.CALL_SITE: + dexPool.callSiteSection.intern((CallSiteReference) reference); + break; default: throw new ExceptionWithContext("Unrecognized reference type: %d", instruction.getOpcode().referenceType); 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 93561412..e5afd4f9 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 @@ -52,10 +52,10 @@ import java.util.Set; public class DexPool extends DexWriter, + CallSiteReference, MethodHandleReference, Annotation, Set, TypeListPool.Key>, Field, PoolMethod, ArrayEncodedValue, EncodedValue, AnnotationElement, StringPool, TypePool, ProtoPool, FieldPool, MethodPool, - ClassPool, TypeListPool, AnnotationPool, AnnotationSetPool, EncodedArrayPool> { + ClassPool, CallSitePool, MethodHandlePool, TypeListPool, AnnotationPool, AnnotationSetPool, EncodedArrayPool> { private final Markable[] sections = new Markable[] { stringSection, typeSection, protoSection, fieldSection, methodSection, classSection, typeListSection, @@ -170,6 +170,12 @@ public class DexPool extends DexWriter + implements MethodHandleSection { + public MethodHandlePool(@Nonnull DexPool dexPool) { + super(dexPool); + } + + public void intern(MethodHandleReference methodHandleReference) { + Integer prev = internedItems.put(methodHandleReference, 0); + if (prev == null) { + switch (methodHandleReference.getMethodHandleType()) { + case MethodHandleType.INSTANCE_GET: + case MethodHandleType.INSTANCE_PUT: + case MethodHandleType.STATIC_GET: + case MethodHandleType.STATIC_PUT: + dexPool.fieldSection.intern((FieldReference) methodHandleReference.getMemberReference()); + break; + case MethodHandleType.INVOKE_INSTANCE: + case MethodHandleType.INVOKE_STATIC: + dexPool.methodSection.intern((MethodReference) methodHandleReference.getMemberReference()); + break; + default: + throw new ExceptionWithContext( + "Invalid method handle type: %d", methodHandleReference.getMethodHandleType()); + } + } + } + + @Override + public FieldReference getFieldReference(MethodHandleReference methodHandleReference) { + return (FieldReference) methodHandleReference.getMemberReference(); + } + + @Override + public MethodReference getMethodReference(MethodHandleReference methodHandleReference) { + return (MethodReference) methodHandleReference.getMemberReference(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/util/CallSiteUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/util/CallSiteUtil.java new file mode 100644 index 00000000..73dbc409 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/util/CallSiteUtil.java @@ -0,0 +1,76 @@ +/* + * Copyright 2018, 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.util; + +import com.google.common.collect.Lists; +import org.jf.dexlib2.base.value.BaseArrayEncodedValue; +import org.jf.dexlib2.base.value.BaseMethodHandleEncodedValue; +import org.jf.dexlib2.base.value.BaseMethodTypeEncodedValue; +import org.jf.dexlib2.iface.reference.CallSiteReference; +import org.jf.dexlib2.iface.reference.MethodHandleReference; +import org.jf.dexlib2.iface.reference.MethodProtoReference; +import org.jf.dexlib2.iface.value.ArrayEncodedValue; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.immutable.value.ImmutableStringEncodedValue; + +import javax.annotation.Nonnull; +import java.util.List; + +public class CallSiteUtil { + public static ArrayEncodedValue getEncodedCallSite(CallSiteReference callSiteReference) { + return new BaseArrayEncodedValue() { + @Nonnull + @Override + public List getValue() { + List encodedCallSite = Lists.newArrayList(); + + encodedCallSite.add(new BaseMethodHandleEncodedValue() { + @Nonnull + @Override + public MethodHandleReference getValue() { + return callSiteReference.getMethodHandle(); + } + }); + encodedCallSite.add(new ImmutableStringEncodedValue(callSiteReference.getMethodName())); + encodedCallSite.add(new BaseMethodTypeEncodedValue() { + @Nonnull + @Override + public MethodProtoReference getValue() { + return callSiteReference.getMethodProto(); + } + }); + encodedCallSite.addAll(callSiteReference.getExtraArguments()); + return encodedCallSite; + } + }; + } +} diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/CallSiteTest.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/CallSiteTest.java new file mode 100644 index 00000000..c909fb28 --- /dev/null +++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/CallSiteTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2018, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.writer; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import org.jf.dexlib2.*; +import org.jf.dexlib2.builder.MethodImplementationBuilder; +import org.jf.dexlib2.builder.instruction.BuilderInstruction35c; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.DexFile; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.formats.Instruction35c; +import org.jf.dexlib2.iface.reference.CallSiteReference; +import org.jf.dexlib2.immutable.ImmutableClassDef; +import org.jf.dexlib2.immutable.ImmutableDexFile; +import org.jf.dexlib2.immutable.ImmutableMethod; +import org.jf.dexlib2.immutable.ImmutableMethodImplementation; +import org.jf.dexlib2.immutable.instruction.ImmutableInstruction35c; +import org.jf.dexlib2.immutable.reference.ImmutableCallSiteReference; +import org.jf.dexlib2.immutable.reference.ImmutableMethodHandleReference; +import org.jf.dexlib2.immutable.reference.ImmutableMethodProtoReference; +import org.jf.dexlib2.immutable.reference.ImmutableMethodReference; +import org.jf.dexlib2.writer.builder.BuilderCallSiteReference; +import org.jf.dexlib2.writer.builder.BuilderMethod; +import org.jf.dexlib2.writer.builder.DexBuilder; +import org.jf.dexlib2.writer.io.FileDataStore; +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +public class CallSiteTest { + @Test + public void testPoolCallSite() throws IOException { + ClassDef class1 = new ImmutableClassDef("Lcls1;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null, null, + null, null, + Lists.newArrayList( + new ImmutableMethod("Lcls1", "method1", + ImmutableList.of(), "V", AccessFlags.PUBLIC.getValue(), null, + new ImmutableMethodImplementation(10, ImmutableList.of( + new ImmutableInstruction35c(Opcode.INVOKE_CUSTOM, 0, 0, 0, 0, 0, 0, + new ImmutableCallSiteReference("call_site_1", + new ImmutableMethodHandleReference(MethodHandleType.INVOKE_STATIC, + new ImmutableMethodReference("Lcls1", "loader", + ImmutableList.of("Ljava/lang/invoke/Lookup;", + "Ljava/lang/String;", + "Ljava/lang/invoke/MethodType;"), + "Ljava/lang/invoke/CallSite;")), + "someMethod", new ImmutableMethodProtoReference(ImmutableList.of(), "V"), ImmutableList.of())) + ), null, null)))); + + File tempFile = File.createTempFile("dex", ".dex"); + DexFileFactory.writeDexFile(tempFile.getPath(), + new ImmutableDexFile(Opcodes.forArtVersion(111), ImmutableList.of(class1))); + + verifyDexFile(DexFileFactory.loadDexFile(tempFile, Opcodes.forArtVersion(111))); + } + + @Test + public void testBuilderCallSite() throws IOException { + DexBuilder dexBuilder = new DexBuilder(Opcodes.forArtVersion(111)); + + BuilderCallSiteReference callSite = dexBuilder.internCallSite(new ImmutableCallSiteReference("call_site_1", + new ImmutableMethodHandleReference( + MethodHandleType.INVOKE_STATIC, + new ImmutableMethodReference("Lcls1", "loader", ImmutableList.of("Ljava/lang/invoke/Lookup;", + "Ljava/lang/String;", + "Ljava/lang/invoke/MethodType;"), + "Ljava/lang/invoke/CallSite;")), + "someMethod", + new ImmutableMethodProtoReference(ImmutableList.of(), "V"), ImmutableList.of())); + + MethodImplementationBuilder methodImplementationBuilder = new MethodImplementationBuilder(10); + methodImplementationBuilder.addInstruction(new BuilderInstruction35c(Opcode.INVOKE_CUSTOM, 0, 0, 0, 0, 0, 0, + callSite)); + + BuilderMethod method = dexBuilder.internMethod("Lcls1", "method1", null, "V", 0, ImmutableSet.of(), + methodImplementationBuilder.getMethodImplementation()); + dexBuilder.internClassDef("Lcls1;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null, null, + ImmutableSet.of(), null, + ImmutableList.of(method)); + + File tempFile = File.createTempFile("dex", ".dex"); + dexBuilder.writeTo(new FileDataStore(tempFile)); + + verifyDexFile(DexFileFactory.loadDexFile(tempFile, Opcodes.forArtVersion(111))); + } + + private void verifyDexFile(DexFile dexFile) { + Assert.assertEquals(1, dexFile.getClasses().size()); + ClassDef cls = Lists.newArrayList(dexFile.getClasses()).get(0); + Assert.assertEquals("Lcls1;", cls.getType()); + Assert.assertEquals(1, Lists.newArrayList(cls.getMethods()).size()); + Method method = Iterators.getNext(cls.getMethods().iterator(), null); + Assert.assertEquals("method1", method.getName()); + Assert.assertEquals(1, Lists.newArrayList(method.getImplementation().getInstructions()).size()); + Instruction instruction = Lists.newArrayList(method.getImplementation().getInstructions().iterator()).get(0); + Assert.assertEquals(Opcode.INVOKE_CUSTOM, instruction.getOpcode()); + Assert.assertTrue(((Instruction35c) instruction).getReference() instanceof CallSiteReference); + } +}