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 
17 package com.android.dx.cf.code;
18 
19 import com.android.dx.cf.attrib.AttCode;
20 import com.android.dx.cf.attrib.AttLineNumberTable;
21 import com.android.dx.cf.attrib.AttLocalVariableTable;
22 import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
23 import com.android.dx.cf.iface.AttributeList;
24 import com.android.dx.cf.iface.ClassFile;
25 import com.android.dx.cf.iface.Method;
26 import com.android.dx.rop.code.AccessFlags;
27 import com.android.dx.rop.code.SourcePosition;
28 import com.android.dx.rop.cst.CstNat;
29 import com.android.dx.rop.cst.CstString;
30 import com.android.dx.rop.cst.CstType;
31 import 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  */
39 public 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      */
ConcreteMethod(Method method, ClassFile classFile, boolean keepLines, boolean keepLocals)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      */
getSourceFile()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      */
isDefaultOrStaticInterfaceMethod()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      */
isStaticMethod()149     public final boolean isStaticMethod() {
150         return (getAccessFlags() & AccessFlags.ACC_STATIC) != 0;
151     }
152 
153     /** {@inheritDoc} */
154     @Override
getNat()155     public CstNat getNat() {
156         return method.getNat();
157     }
158 
159     /** {@inheritDoc} */
160     @Override
getName()161     public CstString getName() {
162         return method.getName();
163     }
164 
165     /** {@inheritDoc} */
166     @Override
getDescriptor()167     public CstString getDescriptor() {
168         return method.getDescriptor();
169     }
170 
171     /** {@inheritDoc} */
172     @Override
getAccessFlags()173     public int getAccessFlags() {
174         return method.getAccessFlags();
175     }
176 
177     /** {@inheritDoc} */
178     @Override
getAttributes()179     public AttributeList getAttributes() {
180         return method.getAttributes();
181     }
182 
183     /** {@inheritDoc} */
184     @Override
getDefiningClass()185     public CstType getDefiningClass() {
186         return method.getDefiningClass();
187     }
188 
189     /** {@inheritDoc} */
190     @Override
getEffectiveDescriptor()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      */
getMaxStack()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      */
getMaxLocals()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      */
getCode()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      */
getCatches()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      */
getLineNumbers()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      */
getLocalVariables()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      */
makeSourcePosistion(int offset)256     public SourcePosition makeSourcePosistion(int offset) {
257         return new SourcePosition(getSourceFile(), offset,
258                                   lineNumbers.pcToLine(offset));
259     }
260 }
261