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.cf.code;
18
19import com.android.dx.cf.attrib.AttCode;
20import com.android.dx.cf.attrib.AttLineNumberTable;
21import com.android.dx.cf.attrib.AttLocalVariableTable;
22import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
23import com.android.dx.cf.iface.AttributeList;
24import com.android.dx.cf.iface.ClassFile;
25import com.android.dx.cf.iface.Method;
26import com.android.dx.rop.code.AccessFlags;
27import com.android.dx.rop.code.SourcePosition;
28import com.android.dx.rop.cst.CstNat;
29import com.android.dx.rop.cst.CstString;
30import com.android.dx.rop.cst.CstType;
31import com.android.dx.rop.type.Prototype;
32
33/**
34 * Container for all the giblets that make up a concrete Java bytecode method.
35 * It implements {@link Method}, so it provides all the original access
36 * (by delegation), but it also constructs and keeps useful versions of
37 * stuff extracted from the method's {@code Code} attribute.
38 */
39public final class ConcreteMethod implements Method {
40    /** {@code non-null;} method being wrapped */
41    private final Method method;
42
43    /** {@code non-null;} the {@code ClassFile} the method belongs to. */
44    private final ClassFile classFile;
45
46    /** {@code non-null;} the code attribute */
47    private final AttCode attCode;
48
49    /** {@code non-null;} line number list */
50    private final LineNumberList lineNumbers;
51
52    /** {@code non-null;} local variable list */
53    private final LocalVariableList localVariables;
54
55    /**
56     * Constructs an instance.
57     *
58     * @param method {@code non-null;} the method to be based on
59     * @param classFile {@code non-null;} the class file that contains this method
60     * @param keepLines whether to keep the line number information
61     * (if any)
62     * @param keepLocals whether to keep the local variable
63     * information (if any)
64     */
65    public ConcreteMethod(Method method, ClassFile classFile,
66            boolean keepLines, boolean keepLocals) {
67        this.method = method;
68        this.classFile = classFile;
69
70        AttributeList attribs = method.getAttributes();
71        this.attCode = (AttCode) attribs.findFirst(AttCode.ATTRIBUTE_NAME);
72
73        AttributeList codeAttribs = attCode.getAttributes();
74
75        /*
76         * Combine all LineNumberTable attributes into one, with the
77         * combined result saved into the instance. The following code
78         * isn't particularly efficient for doing merges, but as far
79         * as I know, this situation rarely occurs "in the
80         * wild," so there's not much point in optimizing for it.
81         */
82        LineNumberList lnl = LineNumberList.EMPTY;
83        if (keepLines) {
84            for (AttLineNumberTable lnt = (AttLineNumberTable)
85                     codeAttribs.findFirst(AttLineNumberTable.ATTRIBUTE_NAME);
86                 lnt != null;
87                 lnt = (AttLineNumberTable) codeAttribs.findNext(lnt)) {
88                lnl = LineNumberList.concat(lnl, lnt.getLineNumbers());
89            }
90        }
91        this.lineNumbers = lnl;
92
93        LocalVariableList lvl = LocalVariableList.EMPTY;
94        if (keepLocals) {
95            /*
96             * Do likewise (and with the same caveat) for
97             * LocalVariableTable and LocalVariableTypeTable attributes.
98             * This combines both of these kinds of attribute into a
99             * single LocalVariableList.
100             */
101            for (AttLocalVariableTable lvt = (AttLocalVariableTable)
102                     codeAttribs.findFirst(
103                             AttLocalVariableTable.ATTRIBUTE_NAME);
104                 lvt != null;
105                 lvt = (AttLocalVariableTable) codeAttribs.findNext(lvt)) {
106
107                lvl = LocalVariableList.concat(lvl, lvt.getLocalVariables());
108            }
109
110            LocalVariableList typeList = LocalVariableList.EMPTY;
111            for (AttLocalVariableTypeTable lvtt = (AttLocalVariableTypeTable)
112                     codeAttribs.findFirst(
113                             AttLocalVariableTypeTable.ATTRIBUTE_NAME);
114                 lvtt != null;
115                 lvtt = (AttLocalVariableTypeTable) codeAttribs.findNext(lvtt)) {
116                typeList = LocalVariableList.concat(typeList, lvtt.getLocalVariables());
117            }
118
119            if (typeList.size() != 0) {
120
121                lvl = LocalVariableList.mergeDescriptorsAndSignatures(lvl, typeList);
122            }
123        }
124        this.localVariables = lvl;
125    }
126
127
128    /**
129     * Gets the source file associated with the method if known.
130     * @return {null-ok;} the source file defining the method if known, null otherwise.
131     */
132    public CstString getSourceFile() {
133        return classFile.getSourceFile();
134    }
135
136    /**
137     * Tests whether the method is being defined on an interface.
138     * @return true if the method is being defined on an interface.
139     */
140    public final boolean isDefaultOrStaticInterfaceMethod() {
141        return (classFile.getAccessFlags() & AccessFlags.ACC_INTERFACE) != 0
142            && !getNat().isClassInit();
143    }
144
145    /**
146     * Tests whether the method is being defined is declared as static.
147     * @return true if the method is being defined is declared as static.
148     */
149    public final boolean isStaticMethod() {
150        return (getAccessFlags() & AccessFlags.ACC_STATIC) != 0;
151    }
152
153    /** {@inheritDoc} */
154    @Override
155    public CstNat getNat() {
156        return method.getNat();
157    }
158
159    /** {@inheritDoc} */
160    @Override
161    public CstString getName() {
162        return method.getName();
163    }
164
165    /** {@inheritDoc} */
166    @Override
167    public CstString getDescriptor() {
168        return method.getDescriptor();
169    }
170
171    /** {@inheritDoc} */
172    @Override
173    public int getAccessFlags() {
174        return method.getAccessFlags();
175    }
176
177    /** {@inheritDoc} */
178    @Override
179    public AttributeList getAttributes() {
180        return method.getAttributes();
181    }
182
183    /** {@inheritDoc} */
184    @Override
185    public CstType getDefiningClass() {
186        return method.getDefiningClass();
187    }
188
189    /** {@inheritDoc} */
190    @Override
191    public Prototype getEffectiveDescriptor() {
192        return method.getEffectiveDescriptor();
193    }
194
195    /**
196     * Gets the maximum stack size.
197     *
198     * @return {@code >= 0;} the maximum stack size
199     */
200    public int getMaxStack() {
201        return attCode.getMaxStack();
202    }
203
204    /**
205     * Gets the number of locals.
206     *
207     * @return {@code >= 0;} the number of locals
208     */
209    public int getMaxLocals() {
210        return attCode.getMaxLocals();
211    }
212
213    /**
214     * Gets the bytecode array.
215     *
216     * @return {@code non-null;} the bytecode array
217     */
218    public BytecodeArray getCode() {
219        return attCode.getCode();
220    }
221
222    /**
223     * Gets the exception table.
224     *
225     * @return {@code non-null;} the exception table
226     */
227    public ByteCatchList getCatches() {
228        return attCode.getCatches();
229    }
230
231    /**
232     * Gets the line number list.
233     *
234     * @return {@code non-null;} the line number list
235     */
236    public LineNumberList getLineNumbers() {
237        return lineNumbers;
238    }
239
240    /**
241     * Gets the local variable list.
242     *
243     * @return {@code non-null;} the local variable list
244     */
245    public LocalVariableList getLocalVariables() {
246        return localVariables;
247    }
248
249    /**
250     * Returns a {@link SourcePosition} instance corresponding to the
251     * given bytecode offset.
252     *
253     * @param offset {@code >= 0;} the bytecode offset
254     * @return {@code non-null;} an appropriate instance
255     */
256    public SourcePosition makeSourcePosistion(int offset) {
257        return new SourcePosition(getSourceFile(), offset,
258                                  lineNumbers.pcToLine(offset));
259    }
260}
261