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
17package com.android.dx.dex.file;
18
19import com.android.dex.Leb128;
20import com.android.dx.dex.code.DalvCode;
21import com.android.dx.rop.code.AccessFlags;
22import com.android.dx.rop.cst.CstMethodRef;
23import com.android.dx.rop.cst.CstString;
24import com.android.dx.rop.type.TypeList;
25import com.android.dx.util.AnnotatedOutput;
26import com.android.dx.util.Hex;
27import java.io.PrintWriter;
28
29/**
30 * Class that representats a method of a class.
31 */
32public 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     */
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} */
72    @Override
73    public boolean equals(Object other) {
74        if (! (other instanceof EncodedMethod)) {
75            return false;
76        }
77
78        return compareTo((EncodedMethod) other) == 0;
79    }
80
81    /**
82     * {@inheritDoc}
83     *
84     * <p><b>Note:</b> This compares the method constants only,
85     * ignoring any associated code, because it should never be the
86     * case that two different items with the same method constant
87     * ever appear in the same list (or same file, even).</p>
88     */
89    @Override
90    public int compareTo(EncodedMethod other) {
91        return method.compareTo(other.method);
92    }
93
94    /** {@inheritDoc} */
95    @Override
96    public String toString() {
97        StringBuilder sb = new StringBuilder(100);
98
99        sb.append(getClass().getName());
100        sb.append('{');
101        sb.append(Hex.u2(getAccessFlags()));
102        sb.append(' ');
103        sb.append(method);
104
105        if (code != null) {
106            sb.append(' ');
107            sb.append(code);
108        }
109
110        sb.append('}');
111
112        return sb.toString();
113    }
114
115    /** {@inheritDoc} */
116    @Override
117    public void addContents(DexFile file) {
118        MethodIdsSection methodIds = file.getMethodIds();
119        MixedItemSection wordData = file.getWordData();
120
121        methodIds.intern(method);
122
123        if (code != null) {
124            wordData.add(code);
125        }
126    }
127
128    /** {@inheritDoc} */
129    @Override
130    public final String toHuman() {
131        return method.toHuman();
132    }
133
134    /** {@inheritDoc} */
135    @Override
136    public final CstString getName() {
137        return method.getNat().getName();
138    }
139
140    /** {@inheritDoc} */
141    @Override
142    public void debugPrint(PrintWriter out, boolean verbose) {
143        if (code == null) {
144            out.println(getRef().toHuman() + ": abstract or native");
145        } else {
146            code.debugPrint(out, "  ", verbose);
147        }
148    }
149
150    /**
151     * Gets the constant for the method.
152     *
153     * @return {@code non-null;} the constant
154     */
155    public final CstMethodRef getRef() {
156        return method;
157    }
158
159    /** {@inheritDoc} */
160    @Override
161    public int encode(DexFile file, AnnotatedOutput out,
162            int lastIndex, int dumpSeq) {
163        int methodIdx = file.getMethodIds().indexOf(method);
164        int diff = methodIdx - lastIndex;
165        int accessFlags = getAccessFlags();
166        int codeOff = OffsettedItem.getAbsoluteOffsetOr0(code);
167        boolean hasCode = (codeOff != 0);
168        boolean shouldHaveCode = (accessFlags &
169                (AccessFlags.ACC_ABSTRACT | AccessFlags.ACC_NATIVE)) == 0;
170
171        /*
172         * Verify that code appears if and only if a method is
173         * declared to have it.
174         */
175        if (hasCode != shouldHaveCode) {
176            throw new UnsupportedOperationException(
177                    "code vs. access_flags mismatch");
178        }
179
180        if (out.annotates()) {
181            out.annotate(0, String.format("  [%x] %s", dumpSeq,
182                            method.toHuman()));
183            out.annotate(Leb128.unsignedLeb128Size(diff),
184                    "    method_idx:   " + Hex.u4(methodIdx));
185            out.annotate(Leb128.unsignedLeb128Size(accessFlags),
186                    "    access_flags: " +
187                    AccessFlags.methodString(accessFlags));
188            out.annotate(Leb128.unsignedLeb128Size(codeOff),
189                    "    code_off:     " + Hex.u4(codeOff));
190        }
191
192        out.writeUleb128(diff);
193        out.writeUleb128(accessFlags);
194        out.writeUleb128(codeOff);
195
196        return methodIdx;
197    }
198}
199