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