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