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.dexgen.dex.file;
18
19import com.android.dexgen.dex.code.DalvCode;
20import com.android.dexgen.rop.code.AccessFlags;
21import com.android.dexgen.rop.cst.CstMethodRef;
22import com.android.dexgen.rop.cst.CstUtf8;
23import com.android.dexgen.rop.type.TypeList;
24import com.android.dexgen.util.AnnotatedOutput;
25import com.android.dexgen.util.Hex;
26import com.android.dexgen.util.Leb128Utils;
27
28import java.io.PrintWriter;
29
30/**
31 * Class that representats a method of a class.
32 */
33public final class EncodedMethod extends EncodedMember
34        implements Comparable<EncodedMethod> {
35    /** {@code non-null;} constant for the method */
36    private final CstMethodRef method;
37
38    /**
39     * {@code null-ok;} code for the method, if the method is neither
40     * {@code abstract} nor {@code native}
41     */
42    private final CodeItem code;
43
44    /**
45     * Constructs an instance.
46     *
47     * @param method {@code non-null;} constant for the method
48     * @param accessFlags access flags
49     * @param code {@code null-ok;} code for the method, if it is neither
50     * {@code abstract} nor {@code native}
51     * @param throwsList {@code non-null;} list of possibly-thrown exceptions,
52     * just used in generating debugging output (listings)
53     */
54    public EncodedMethod(CstMethodRef method, int accessFlags,
55            DalvCode code, TypeList throwsList) {
56        super(accessFlags);
57
58        if (method == null) {
59            throw new NullPointerException("method == null");
60        }
61
62        this.method = method;
63
64        if (code == null) {
65            this.code = null;
66        } else {
67            boolean isStatic = (accessFlags & AccessFlags.ACC_STATIC) != 0;
68            this.code = new CodeItem(method, code, isStatic, throwsList);
69        }
70    }
71
72    /** {@inheritDoc} */
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    public int compareTo(EncodedMethod other) {
90        return method.compareTo(other.method);
91    }
92
93    /** {@inheritDoc} */
94    @Override
95    public String toString() {
96        StringBuffer sb = new StringBuffer(100);
97
98        sb.append(getClass().getName());
99        sb.append('{');
100        sb.append(Hex.u2(getAccessFlags()));
101        sb.append(' ');
102        sb.append(method);
103
104        if (code != null) {
105            sb.append(' ');
106            sb.append(code);
107        }
108
109        sb.append('}');
110
111        return sb.toString();
112    }
113
114    /** {@inheritDoc} */
115    @Override
116    public void addContents(DexFile file) {
117        MethodIdsSection methodIds = file.getMethodIds();
118        MixedItemSection wordData = file.getWordData();
119
120        methodIds.intern(method);
121
122        if (code != null) {
123            wordData.add(code);
124        }
125    }
126
127    /** {@inheritDoc} */
128    public final String toHuman() {
129        return method.toHuman();
130    }
131
132    /** {@inheritDoc} */
133    @Override
134    public final CstUtf8 getName() {
135        return method.getNat().getName();
136    }
137
138    /** {@inheritDoc} */
139    @Override
140    public void debugPrint(PrintWriter out, boolean verbose) {
141        if (code == null) {
142            out.println(getRef().toHuman() + ": abstract or native");
143        } else {
144            code.debugPrint(out, "  ", verbose);
145        }
146    }
147
148    /**
149     * Gets the constant for the method.
150     *
151     * @return {@code non-null;} the constant
152     */
153    public final CstMethodRef getRef() {
154        return method;
155    }
156
157    /** {@inheritDoc} */
158    @Override
159    public int encode(DexFile file, AnnotatedOutput out,
160            int lastIndex, int dumpSeq) {
161        int methodIdx = file.getMethodIds().indexOf(method);
162        int diff = methodIdx - lastIndex;
163        int accessFlags = getAccessFlags();
164        int codeOff = OffsettedItem.getAbsoluteOffsetOr0(code);
165        boolean hasCode = (codeOff != 0);
166        boolean shouldHaveCode = (accessFlags &
167                (AccessFlags.ACC_ABSTRACT | AccessFlags.ACC_NATIVE)) == 0;
168
169        /*
170         * Verify that code appears if and only if a method is
171         * declared to have it.
172         */
173        if (hasCode != shouldHaveCode) {
174            throw new UnsupportedOperationException(
175                    "code vs. access_flags mismatch");
176        }
177
178        if (out.annotates()) {
179            out.annotate(0, String.format("  [%x] %s", dumpSeq,
180                            method.toHuman()));
181            out.annotate(Leb128Utils.unsignedLeb128Size(diff),
182                    "    method_idx:   " + Hex.u4(methodIdx));
183            out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags),
184                    "    access_flags: " +
185                    AccessFlags.methodString(accessFlags));
186            out.annotate(Leb128Utils.unsignedLeb128Size(codeOff),
187                    "    code_off:     " + Hex.u4(codeOff));
188        }
189
190        out.writeUnsignedLeb128(diff);
191        out.writeUnsignedLeb128(accessFlags);
192        out.writeUnsignedLeb128(codeOff);
193
194        return methodIdx;
195    }
196}
197