| /* |
| * 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)); |
| } |
| } |