/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dx.cf.code; import com.android.dx.cf.attrib.AttCode; import com.android.dx.cf.attrib.AttLineNumberTable; import com.android.dx.cf.attrib.AttLocalVariableTable; import com.android.dx.cf.attrib.AttLocalVariableTypeTable; import com.android.dx.cf.attrib.AttSourceFile; import com.android.dx.cf.iface.AttributeList; import com.android.dx.cf.iface.ClassFile; import com.android.dx.cf.iface.Method; import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.code.SourcePosition; import com.android.dx.rop.cst.CstNat; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.cst.CstUtf8; import com.android.dx.rop.type.Prototype; /** * Container for all the giblets that make up a concrete Java bytecode method. * It implements {@link Method}, so it provides all the original access * (by delegation), but it also constructs and keeps useful versions of * stuff extracted from the method's {@code Code} attribute. */ public final class ConcreteMethod implements Method { /** {@code non-null;} method being wrapped */ private final Method method; /** * {@code null-ok;} the class's {@code SourceFile} attribute value, * if any */ private final CstUtf8 sourceFile; /** * whether the class that this method is part of is defined with * {@code ACC_SUPER} */ private final boolean accSuper; /** {@code non-null;} the code attribute */ private final AttCode attCode; /** {@code non-null;} line number list */ private final LineNumberList lineNumbers; /** {@code non-null;} local variable list */ private final LocalVariableList localVariables; /** * Constructs an instance. * * @param method {@code non-null;} the method to be based on * @param cf {@code non-null;} the class file that contains this method * @param keepLines whether to keep the line number information * (if any) * @param keepLocals whether to keep the local variable * information (if any) */ public ConcreteMethod(Method method, ClassFile cf, boolean keepLines, boolean keepLocals) { this.method = method; this.accSuper = (cf.getAccessFlags() & AccessFlags.ACC_SUPER) != 0; this.sourceFile = cf.getSourceFile(); AttributeList attribs = method.getAttributes(); this.attCode = (AttCode) attribs.findFirst(AttCode.ATTRIBUTE_NAME); AttributeList codeAttribs = attCode.getAttributes(); /* * Combine all LineNumberTable attributes into one, with the * combined result saved into the instance. The following code * isn't particularly efficient for doing merges, but as far * as I know, this situation rarely occurs "in the * wild," so there's not much point in optimizing for it. */ LineNumberList lineNumbers = LineNumberList.EMPTY; if (keepLines) { for (AttLineNumberTable lnt = (AttLineNumberTable) codeAttribs.findFirst(AttLineNumberTable.ATTRIBUTE_NAME); lnt != null; lnt = (AttLineNumberTable) codeAttribs.findNext(lnt)) { lineNumbers = LineNumberList.concat(lineNumbers, lnt.getLineNumbers()); } } this.lineNumbers = lineNumbers; LocalVariableList localVariables = LocalVariableList.EMPTY; if (keepLocals) { /* * Do likewise (and with the same caveat) for * LocalVariableTable and LocalVariableTypeTable attributes. * This combines both of these kinds of attribute into a * single LocalVariableList. */ for (AttLocalVariableTable lvt = (AttLocalVariableTable) codeAttribs.findFirst( AttLocalVariableTable.ATTRIBUTE_NAME); lvt != null; lvt = (AttLocalVariableTable) codeAttribs.findNext(lvt)) { localVariables = LocalVariableList.concat(localVariables, lvt.getLocalVariables()); } LocalVariableList typeList = LocalVariableList.EMPTY; for (AttLocalVariableTypeTable lvtt = (AttLocalVariableTypeTable) codeAttribs.findFirst( AttLocalVariableTypeTable.ATTRIBUTE_NAME); lvtt != null; lvtt = (AttLocalVariableTypeTable) codeAttribs.findNext(lvtt)) { typeList = LocalVariableList.concat(typeList, lvtt.getLocalVariables()); } if (typeList.size() != 0) { localVariables = LocalVariableList.mergeDescriptorsAndSignatures( localVariables, typeList); } } this.localVariables = localVariables; } /** {@inheritDoc} */ public CstNat getNat() { return method.getNat(); } /** {@inheritDoc} */ public CstUtf8 getName() { return method.getName(); } /** {@inheritDoc} */ public CstUtf8 getDescriptor() { return method.getDescriptor(); } /** {@inheritDoc} */ public int getAccessFlags() { return method.getAccessFlags(); } /** {@inheritDoc} */ public AttributeList getAttributes() { return method.getAttributes(); } /** {@inheritDoc} */ public CstType getDefiningClass() { return method.getDefiningClass(); } /** {@inheritDoc} */ public Prototype getEffectiveDescriptor() { return method.getEffectiveDescriptor(); } /** * Gets whether the class that this method is part of is defined with * {@code ACC_SUPER}. * * @return the {@code ACC_SUPER} value */ public boolean getAccSuper() { return accSuper; } /** * Gets the maximum stack size. * * @return {@code >= 0;} the maximum stack size */ public int getMaxStack() { return attCode.getMaxStack(); } /** * Gets the number of locals. * * @return {@code >= 0;} the number of locals */ public int getMaxLocals() { return attCode.getMaxLocals(); } /** * Gets the bytecode array. * * @return {@code non-null;} the bytecode array */ public BytecodeArray getCode() { return attCode.getCode(); } /** * Gets the exception table. * * @return {@code non-null;} the exception table */ public ByteCatchList getCatches() { return attCode.getCatches(); } /** * Gets the line number list. * * @return {@code non-null;} the line number list */ public LineNumberList getLineNumbers() { return lineNumbers; } /** * Gets the local variable list. * * @return {@code non-null;} the local variable list */ public LocalVariableList getLocalVariables() { return localVariables; } /** * Returns a {@link SourcePosition} instance corresponding to the * given bytecode offset. * * @param offset {@code >= 0;} the bytecode offset * @return {@code non-null;} an appropriate instance */ public SourcePosition makeSourcePosistion(int offset) { return new SourcePosition(sourceFile, offset, lineNumbers.pcToLine(offset)); } }