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