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
32package org.jf.dexlib2.dexbacked;
33
34import com.google.common.collect.ImmutableList;
35import com.google.common.collect.Iterators;
36import org.jf.dexlib2.base.reference.BaseMethodReference;
37import org.jf.dexlib2.dexbacked.raw.MethodIdItem;
38import org.jf.dexlib2.dexbacked.raw.ProtoIdItem;
39import org.jf.dexlib2.dexbacked.raw.TypeListItem;
40import org.jf.dexlib2.dexbacked.util.AnnotationsDirectory;
41import org.jf.dexlib2.dexbacked.util.FixedSizeList;
42import org.jf.dexlib2.dexbacked.util.ParameterIterator;
43import org.jf.dexlib2.iface.Annotation;
44import org.jf.dexlib2.iface.Method;
45import org.jf.dexlib2.iface.MethodParameter;
46import org.jf.util.AbstractForwardSequentialList;
47
48import javax.annotation.Nonnull;
49import javax.annotation.Nullable;
50import java.util.Iterator;
51import java.util.List;
52import java.util.Set;
53
54public class DexBackedMethod extends BaseMethodReference implements Method {
55    @Nonnull public final DexBackedDexFile dexFile;
56    @Nonnull public final DexBackedClassDef classDef;
57
58    public final int accessFlags;
59
60    private final int codeOffset;
61    private final int parameterAnnotationSetListOffset;
62    private final int methodAnnotationSetOffset;
63
64    public final int methodIndex;
65
66    private int methodIdItemOffset;
67    private int protoIdItemOffset;
68    private int parametersOffset = -1;
69
70    public DexBackedMethod(@Nonnull DexReader reader,
71                           @Nonnull DexBackedClassDef classDef,
72                           int previousMethodIndex) {
73        this.dexFile = reader.dexBuf;
74        this.classDef = classDef;
75
76        // large values may be used for the index delta, which cause the cumulative index to overflow upon
77        // addition, effectively allowing out of order entries.
78        int methodIndexDiff = reader.readLargeUleb128();
79        this.methodIndex = methodIndexDiff + previousMethodIndex;
80        this.accessFlags = reader.readSmallUleb128();
81        this.codeOffset = reader.readSmallUleb128();
82
83        this.methodAnnotationSetOffset = 0;
84        this.parameterAnnotationSetListOffset = 0;
85    }
86
87    public DexBackedMethod(@Nonnull DexReader reader,
88                           @Nonnull DexBackedClassDef classDef,
89                           int previousMethodIndex,
90                           @Nonnull AnnotationsDirectory.AnnotationIterator methodAnnotationIterator,
91                           @Nonnull AnnotationsDirectory.AnnotationIterator paramaterAnnotationIterator) {
92        this.dexFile = reader.dexBuf;
93        this.classDef = classDef;
94
95        // large values may be used for the index delta, which cause the cumulative index to overflow upon
96        // addition, effectively allowing out of order entries.
97        int methodIndexDiff = reader.readLargeUleb128();
98        this.methodIndex = methodIndexDiff + previousMethodIndex;
99        this.accessFlags = reader.readSmallUleb128();
100        this.codeOffset = reader.readSmallUleb128();
101
102        this.methodAnnotationSetOffset = methodAnnotationIterator.seekTo(methodIndex);
103        this.parameterAnnotationSetListOffset = paramaterAnnotationIterator.seekTo(methodIndex);
104    }
105
106    public int getMethodIndex() { return methodIndex; }
107    @Nonnull @Override public String getDefiningClass() { return classDef.getType(); }
108    @Override public int getAccessFlags() { return accessFlags; }
109
110    @Nonnull
111    @Override
112    public String getName() {
113        return dexFile.getString(dexFile.readSmallUint(getMethodIdItemOffset() + MethodIdItem.NAME_OFFSET));
114    }
115
116    @Nonnull
117    @Override
118    public String getReturnType() {
119        return dexFile.getType(dexFile.readSmallUint(getProtoIdItemOffset() + ProtoIdItem.RETURN_TYPE_OFFSET));
120    }
121
122    @Nonnull
123    @Override
124    public List<? extends MethodParameter> getParameters() {
125        int parametersOffset = getParametersOffset();
126        if (parametersOffset > 0) {
127            final List<String> parameterTypes = getParameterTypes();
128
129            return new AbstractForwardSequentialList<MethodParameter>() {
130                @Nonnull @Override public Iterator<MethodParameter> iterator() {
131                    return new ParameterIterator(parameterTypes,
132                            getParameterAnnotations(),
133                            getParameterNames());
134                }
135
136                @Override public int size() {
137                    return parameterTypes.size();
138                }
139            };
140        }
141        return ImmutableList.of();
142    }
143
144    @Nonnull
145    public List<? extends Set<? extends DexBackedAnnotation>> getParameterAnnotations() {
146        return AnnotationsDirectory.getParameterAnnotations(dexFile, parameterAnnotationSetListOffset);
147    }
148
149    @Nonnull
150    public Iterator<String> getParameterNames() {
151        DexBackedMethodImplementation methodImpl = getImplementation();
152        if (methodImpl != null) {
153            return methodImpl.getParameterNames(null);
154        }
155        return Iterators.emptyIterator();
156    }
157
158    @Nonnull
159    @Override
160    public List<String> getParameterTypes() {
161        final int parametersOffset = getParametersOffset();
162        if (parametersOffset > 0) {
163            final int parameterCount = dexFile.readSmallUint(parametersOffset + TypeListItem.SIZE_OFFSET);
164            final int paramListStart = parametersOffset + TypeListItem.LIST_OFFSET;
165            return new FixedSizeList<String>() {
166                @Nonnull
167                @Override
168                public String readItem(final int index) {
169                    return dexFile.getType(dexFile.readUshort(paramListStart + 2*index));
170                }
171                @Override public int size() { return parameterCount; }
172            };
173        }
174        return ImmutableList.of();
175    }
176
177    @Nonnull
178    @Override
179    public Set<? extends Annotation> getAnnotations() {
180        return AnnotationsDirectory.getAnnotations(dexFile, methodAnnotationSetOffset);
181    }
182
183    @Nullable
184    @Override
185    public DexBackedMethodImplementation getImplementation() {
186        if (codeOffset > 0) {
187            return new DexBackedMethodImplementation(dexFile, this, codeOffset);
188        }
189        return null;
190    }
191
192    private int getMethodIdItemOffset() {
193        if (methodIdItemOffset == 0) {
194            methodIdItemOffset = dexFile.getMethodIdItemOffset(methodIndex);
195        }
196        return methodIdItemOffset;
197    }
198
199    private int getProtoIdItemOffset() {
200        if (protoIdItemOffset == 0) {
201            int protoIndex = dexFile.readUshort(getMethodIdItemOffset() + MethodIdItem.PROTO_OFFSET);
202            protoIdItemOffset = dexFile.getProtoIdItemOffset(protoIndex);
203        }
204        return protoIdItemOffset;
205    }
206
207    private int getParametersOffset() {
208        if (parametersOffset == -1) {
209            parametersOffset = dexFile.readSmallUint(getProtoIdItemOffset() + ProtoIdItem.PARAMETERS_OFFSET);
210        }
211        return parametersOffset;
212    }
213
214    /**
215     * Skips the reader over the specified number of encoded_method structures
216     *
217     * @param reader The reader to skip
218     * @param count The number of encoded_method structures to skip over
219     */
220    public static void skipMethods(@Nonnull DexReader reader, int count) {
221        for (int i=0; i<count; i++) {
222            reader.skipUleb128();
223            reader.skipUleb128();
224            reader.skipUleb128();
225        }
226    }
227}
228