Home | History | Annotate | Download | only in dexbacked
      1 /*
      2  * Copyright 2012, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.dexlib2.dexbacked;
     33 
     34 import com.google.common.collect.ImmutableList;
     35 import com.google.common.collect.ImmutableSet;
     36 import org.jf.dexlib2.base.reference.BaseMethodReference;
     37 import org.jf.dexlib2.dexbacked.raw.MethodIdItem;
     38 import org.jf.dexlib2.dexbacked.raw.ProtoIdItem;
     39 import org.jf.dexlib2.dexbacked.raw.TypeListItem;
     40 import org.jf.dexlib2.dexbacked.reference.DexBackedMethodReference;
     41 import org.jf.dexlib2.dexbacked.util.AnnotationsDirectory;
     42 import org.jf.dexlib2.dexbacked.util.FixedSizeList;
     43 import org.jf.dexlib2.dexbacked.util.ParameterIterator;
     44 import org.jf.dexlib2.iface.Annotation;
     45 import org.jf.dexlib2.iface.Method;
     46 import org.jf.dexlib2.iface.MethodImplementation;
     47 import org.jf.dexlib2.iface.MethodParameter;
     48 import org.jf.util.AbstractForwardSequentialList;
     49 
     50 import javax.annotation.Nonnull;
     51 import javax.annotation.Nullable;
     52 import java.util.Iterator;
     53 import java.util.List;
     54 import java.util.Set;
     55 
     56 public class DexBackedMethod extends BaseMethodReference implements Method {
     57     @Nonnull public final DexBackedDexFile dexFile;
     58     @Nonnull public final DexBackedClassDef classDef;
     59 
     60     public final int accessFlags;
     61 
     62     private final int codeOffset;
     63     private final int parameterAnnotationSetListOffset;
     64     private final int methodAnnotationSetOffset;
     65 
     66     public final int methodIndex;
     67     private final int startOffset;
     68 
     69     private int methodIdItemOffset;
     70     private int protoIdItemOffset;
     71     private int parametersOffset = -1;
     72 
     73     public DexBackedMethod(@Nonnull DexReader reader,
     74                            @Nonnull DexBackedClassDef classDef,
     75                            int previousMethodIndex) {
     76         this.dexFile = reader.dexBuf;
     77         this.classDef = classDef;
     78         startOffset = reader.getOffset();
     79 
     80         // large values may be used for the index delta, which cause the cumulative index to overflow upon
     81         // addition, effectively allowing out of order entries.
     82         int methodIndexDiff = reader.readLargeUleb128();
     83         this.methodIndex = methodIndexDiff + previousMethodIndex;
     84         this.accessFlags = reader.readSmallUleb128();
     85         this.codeOffset = reader.readSmallUleb128();
     86 
     87         this.methodAnnotationSetOffset = 0;
     88         this.parameterAnnotationSetListOffset = 0;
     89     }
     90 
     91     public DexBackedMethod(@Nonnull DexReader reader,
     92                            @Nonnull DexBackedClassDef classDef,
     93                            int previousMethodIndex,
     94                            @Nonnull AnnotationsDirectory.AnnotationIterator methodAnnotationIterator,
     95                            @Nonnull AnnotationsDirectory.AnnotationIterator paramaterAnnotationIterator) {
     96         this.dexFile = reader.dexBuf;
     97         this.classDef = classDef;
     98         startOffset = reader.getOffset();
     99 
    100         // large values may be used for the index delta, which cause the cumulative index to overflow upon
    101         // addition, effectively allowing out of order entries.
    102         int methodIndexDiff = reader.readLargeUleb128();
    103         this.methodIndex = methodIndexDiff + previousMethodIndex;
    104         this.accessFlags = reader.readSmallUleb128();
    105         this.codeOffset = reader.readSmallUleb128();
    106 
    107         this.methodAnnotationSetOffset = methodAnnotationIterator.seekTo(methodIndex);
    108         this.parameterAnnotationSetListOffset = paramaterAnnotationIterator.seekTo(methodIndex);
    109     }
    110 
    111     public int getMethodIndex() { return methodIndex; }
    112     @Nonnull @Override public String getDefiningClass() { return classDef.getType(); }
    113     @Override public int getAccessFlags() { return accessFlags; }
    114 
    115     @Nonnull
    116     @Override
    117     public String getName() {
    118         return dexFile.getString(dexFile.readSmallUint(getMethodIdItemOffset() + MethodIdItem.NAME_OFFSET));
    119     }
    120 
    121     @Nonnull
    122     @Override
    123     public String getReturnType() {
    124         return dexFile.getType(dexFile.readSmallUint(getProtoIdItemOffset() + ProtoIdItem.RETURN_TYPE_OFFSET));
    125     }
    126 
    127     @Nonnull
    128     @Override
    129     public List<? extends MethodParameter> getParameters() {
    130         int parametersOffset = getParametersOffset();
    131         if (parametersOffset > 0) {
    132             final List<String> parameterTypes = getParameterTypes();
    133 
    134             return new AbstractForwardSequentialList<MethodParameter>() {
    135                 @Nonnull @Override public Iterator<MethodParameter> iterator() {
    136                     return new ParameterIterator(parameterTypes,
    137                             getParameterAnnotations(),
    138                             getParameterNames());
    139                 }
    140 
    141                 @Override public int size() {
    142                     return parameterTypes.size();
    143                 }
    144             };
    145         }
    146         return ImmutableList.of();
    147     }
    148 
    149     @Nonnull
    150     public List<? extends Set<? extends DexBackedAnnotation>> getParameterAnnotations() {
    151         return AnnotationsDirectory.getParameterAnnotations(dexFile, parameterAnnotationSetListOffset);
    152     }
    153 
    154     @Nonnull
    155     public Iterator<String> getParameterNames() {
    156         DexBackedMethodImplementation methodImpl = getImplementation();
    157         if (methodImpl != null) {
    158             return methodImpl.getParameterNames(null);
    159         }
    160         return ImmutableSet.<String>of().iterator();
    161     }
    162 
    163     @Nonnull
    164     @Override
    165     public List<String> getParameterTypes() {
    166         final int parametersOffset = getParametersOffset();
    167         if (parametersOffset > 0) {
    168             final int parameterCount = dexFile.readSmallUint(parametersOffset + TypeListItem.SIZE_OFFSET);
    169             final int paramListStart = parametersOffset + TypeListItem.LIST_OFFSET;
    170             return new FixedSizeList<String>() {
    171                 @Nonnull
    172                 @Override
    173                 public String readItem(final int index) {
    174                     return dexFile.getType(dexFile.readUshort(paramListStart + 2*index));
    175                 }
    176                 @Override public int size() { return parameterCount; }
    177             };
    178         }
    179         return ImmutableList.of();
    180     }
    181 
    182     @Nonnull
    183     @Override
    184     public Set<? extends Annotation> getAnnotations() {
    185         return AnnotationsDirectory.getAnnotations(dexFile, methodAnnotationSetOffset);
    186     }
    187 
    188     @Nullable
    189     @Override
    190     public DexBackedMethodImplementation getImplementation() {
    191         if (codeOffset > 0) {
    192             return new DexBackedMethodImplementation(dexFile, this, codeOffset);
    193         }
    194         return null;
    195     }
    196 
    197     private int getMethodIdItemOffset() {
    198         if (methodIdItemOffset == 0) {
    199             methodIdItemOffset = dexFile.getMethodIdItemOffset(methodIndex);
    200         }
    201         return methodIdItemOffset;
    202     }
    203 
    204     private int getProtoIdItemOffset() {
    205         if (protoIdItemOffset == 0) {
    206             int protoIndex = dexFile.readUshort(getMethodIdItemOffset() + MethodIdItem.PROTO_OFFSET);
    207             protoIdItemOffset = dexFile.getProtoIdItemOffset(protoIndex);
    208         }
    209         return protoIdItemOffset;
    210     }
    211 
    212     private int getParametersOffset() {
    213         if (parametersOffset == -1) {
    214             parametersOffset = dexFile.readSmallUint(getProtoIdItemOffset() + ProtoIdItem.PARAMETERS_OFFSET);
    215         }
    216         return parametersOffset;
    217     }
    218 
    219     /**
    220      * Skips the reader over the specified number of encoded_method structures
    221      *
    222      * @param reader The reader to skip
    223      * @param count The number of encoded_method structures to skip over
    224      */
    225     public static void skipMethods(@Nonnull DexReader reader, int count) {
    226         for (int i=0; i<count; i++) {
    227             reader.skipUleb128();
    228             reader.skipUleb128();
    229             reader.skipUleb128();
    230         }
    231     }
    232 
    233     /**
    234      * Calculate and return the private size of a method definition.
    235      *
    236      * Calculated as: method_idx_diff + access_flags + code_off +
    237      * implementation size + reference size
    238      *
    239      * @return size in bytes
    240      */
    241     public int getSize() {
    242         int size = 0;
    243 
    244         DexReader reader = dexFile.readerAt(startOffset);
    245         reader.readLargeUleb128(); //method_idx_diff
    246         reader.readSmallUleb128(); //access_flags
    247         reader.readSmallUleb128(); //code_off
    248         size += reader.getOffset() - startOffset;
    249 
    250         DexBackedMethodImplementation impl = getImplementation();
    251         if (impl != null) {
    252             size += impl.getSize();
    253         }
    254 
    255         DexBackedMethodReference methodRef = new DexBackedMethodReference(dexFile, methodIndex);
    256         size += methodRef.getSize();
    257 
    258         return size;
    259     }
    260 }
    261