blob: da6cff7508e39541fb13a087106599579cada98e [file] [log] [blame]
/*
* 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));
}
}