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