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