1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dx.dex.file;
18 
19 import com.android.dex.Leb128;
20 import com.android.dx.dex.code.DalvCode;
21 import com.android.dx.rop.code.AccessFlags;
22 import com.android.dx.rop.cst.CstMethodRef;
23 import com.android.dx.rop.cst.CstString;
24 import com.android.dx.rop.type.TypeList;
25 import com.android.dx.util.AnnotatedOutput;
26 import com.android.dx.util.Hex;
27 import java.io.PrintWriter;
28 
29 /**
30  * Class that representats a method of a class.
31  */
32 public final class EncodedMethod extends EncodedMember
33         implements Comparable<EncodedMethod> {
34     /** {@code non-null;} constant for the method */
35     private final CstMethodRef method;
36 
37     /**
38      * {@code null-ok;} code for the method, if the method is neither
39      * {@code abstract} nor {@code native}
40      */
41     private final CodeItem code;
42 
43     /**
44      * Constructs an instance.
45      *
46      * @param method {@code non-null;} constant for the method
47      * @param accessFlags access flags
48      * @param code {@code null-ok;} code for the method, if it is neither
49      * {@code abstract} nor {@code native}
50      * @param throwsList {@code non-null;} list of possibly-thrown exceptions,
51      * just used in generating debugging output (listings)
52      */
EncodedMethod(CstMethodRef method, int accessFlags, DalvCode code, TypeList throwsList)53     public EncodedMethod(CstMethodRef method, int accessFlags,
54             DalvCode code, TypeList throwsList) {
55         super(accessFlags);
56 
57         if (method == null) {
58             throw new NullPointerException("method == null");
59         }
60 
61         this.method = method;
62 
63         if (code == null) {
64             this.code = null;
65         } else {
66             boolean isStatic = (accessFlags & AccessFlags.ACC_STATIC) != 0;
67             this.code = new CodeItem(method, code, isStatic, throwsList);
68         }
69     }
70 
71     /** {@inheritDoc} */
equals(Object other)72     public boolean equals(Object other) {
73         if (! (other instanceof EncodedMethod)) {
74             return false;
75         }
76 
77         return compareTo((EncodedMethod) other) == 0;
78     }
79 
80     /**
81      * {@inheritDoc}
82      *
83      * <p><b>Note:</b> This compares the method constants only,
84      * ignoring any associated code, because it should never be the
85      * case that two different items with the same method constant
86      * ever appear in the same list (or same file, even).</p>
87      */
compareTo(EncodedMethod other)88     public int compareTo(EncodedMethod other) {
89         return method.compareTo(other.method);
90     }
91 
92     /** {@inheritDoc} */
93     @Override
toString()94     public String toString() {
95         StringBuffer sb = new StringBuffer(100);
96 
97         sb.append(getClass().getName());
98         sb.append('{');
99         sb.append(Hex.u2(getAccessFlags()));
100         sb.append(' ');
101         sb.append(method);
102 
103         if (code != null) {
104             sb.append(' ');
105             sb.append(code);
106         }
107 
108         sb.append('}');
109 
110         return sb.toString();
111     }
112 
113     /** {@inheritDoc} */
114     @Override
addContents(DexFile file)115     public void addContents(DexFile file) {
116         MethodIdsSection methodIds = file.getMethodIds();
117         MixedItemSection wordData = file.getWordData();
118 
119         methodIds.intern(method);
120 
121         if (code != null) {
122             wordData.add(code);
123         }
124     }
125 
126     /** {@inheritDoc} */
toHuman()127     public final String toHuman() {
128         return method.toHuman();
129     }
130 
131     /** {@inheritDoc} */
132     @Override
getName()133     public final CstString getName() {
134         return method.getNat().getName();
135     }
136 
137     /** {@inheritDoc} */
138     @Override
debugPrint(PrintWriter out, boolean verbose)139     public void debugPrint(PrintWriter out, boolean verbose) {
140         if (code == null) {
141             out.println(getRef().toHuman() + ": abstract or native");
142         } else {
143             code.debugPrint(out, "  ", verbose);
144         }
145     }
146 
147     /**
148      * Gets the constant for the method.
149      *
150      * @return {@code non-null;} the constant
151      */
getRef()152     public final CstMethodRef getRef() {
153         return method;
154     }
155 
156     /** {@inheritDoc} */
157     @Override
encode(DexFile file, AnnotatedOutput out, int lastIndex, int dumpSeq)158     public int encode(DexFile file, AnnotatedOutput out,
159             int lastIndex, int dumpSeq) {
160         int methodIdx = file.getMethodIds().indexOf(method);
161         int diff = methodIdx - lastIndex;
162         int accessFlags = getAccessFlags();
163         int codeOff = OffsettedItem.getAbsoluteOffsetOr0(code);
164         boolean hasCode = (codeOff != 0);
165         boolean shouldHaveCode = (accessFlags &
166                 (AccessFlags.ACC_ABSTRACT | AccessFlags.ACC_NATIVE)) == 0;
167 
168         /*
169          * Verify that code appears if and only if a method is
170          * declared to have it.
171          */
172         if (hasCode != shouldHaveCode) {
173             throw new UnsupportedOperationException(
174                     "code vs. access_flags mismatch");
175         }
176 
177         if (out.annotates()) {
178             out.annotate(0, String.format("  [%x] %s", dumpSeq,
179                             method.toHuman()));
180             out.annotate(Leb128.unsignedLeb128Size(diff),
181                     "    method_idx:   " + Hex.u4(methodIdx));
182             out.annotate(Leb128.unsignedLeb128Size(accessFlags),
183                     "    access_flags: " +
184                     AccessFlags.methodString(accessFlags));
185             out.annotate(Leb128.unsignedLeb128Size(codeOff),
186                     "    code_off:     " + Hex.u4(codeOff));
187         }
188 
189         out.writeUleb128(diff);
190         out.writeUleb128(accessFlags);
191         out.writeUleb128(codeOff);
192 
193         return methodIdx;
194     }
195 }
196