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    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     */
88    public int compareTo(EncodedMethod other) {
89        return method.compareTo(other.method);
90    }
91
92    /** {@inheritDoc} */
93    @Override
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
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} */
127    public final String toHuman() {
128        return method.toHuman();
129    }
130
131    /** {@inheritDoc} */
132    @Override
133    public final CstString getName() {
134        return method.getNat().getName();
135    }
136
137    /** {@inheritDoc} */
138    @Override
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     */
152    public final CstMethodRef getRef() {
153        return method;
154    }
155
156    /** {@inheritDoc} */
157    @Override
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