WALA/com.ibm.wala.core/src/com/ibm/wala/classLoader/ShrikeCTMethod.java

376 lines
11 KiB
Java

/*******************************************************************************
* Copyright (c) 2002 - 2006 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.classLoader;
import java.util.Collection;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.Decoder;
import com.ibm.wala.shrikeBT.IndirectionData;
import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
import com.ibm.wala.shrikeCT.AnnotationsReader;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
import com.ibm.wala.shrikeCT.CodeReader;
import com.ibm.wala.shrikeCT.ExceptionsReader;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.shrikeCT.LineNumberTableReader;
import com.ibm.wala.shrikeCT.LocalVariableTableReader;
import com.ibm.wala.shrikeCT.RuntimeInvisibleAnnotationsReader;
import com.ibm.wala.shrikeCT.RuntimeVisibleAnnotationsReader;
import com.ibm.wala.shrikeCT.SignatureReader;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.types.annotations.Annotation;
import com.ibm.wala.types.generics.MethodTypeSignature;
import com.ibm.wala.util.debug.Assertions;
/**
* A wrapper around a Shrike object that represents a method
*/
public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMethod {
/**
* The index of this method in the declaring class's method list according to Shrike CT.
*/
final private int shrikeMethodIndex;
/**
* JVM-level modifiers for this method a value of -1 means "uninitialized"
*/
private int modifiers = -1;
private final IClassHierarchy cha;
public ShrikeCTMethod(IClass klass, int index) {
super(klass);
if (klass == null) {
throw new IllegalArgumentException("klass is null");
}
this.shrikeMethodIndex = index;
this.cha = klass.getClassHierarchy();
}
@Override
public byte[] getBytecodes() {
CodeReader code = getCodeReader();
if (code == null) {
return null;
} else {
return code.getBytecode();
}
}
@Override
protected String getMethodName() throws InvalidClassFileException {
ClassReader reader = getClassReader();
return reader.getMethodName(shrikeMethodIndex);
}
@Override
protected String getMethodSignature() throws InvalidClassFileException {
ClassReader reader = getClassReader();
return reader.getMethodType(shrikeMethodIndex);
}
@Override
protected int getModifiers() {
if (modifiers == -1) {
modifiers = getClassReader().getMethodAccessFlags(shrikeMethodIndex);
}
return modifiers;
}
@Override
protected Decoder makeDecoder() {
CodeReader reader = getCodeReader();
if (reader == null) {
return null;
}
final Decoder d = new CTDecoder(reader);
try {
d.decode();
} catch (Decoder.InvalidBytecodeException ex) {
Assertions.UNREACHABLE();
}
return d;
}
@Override
public int getMaxLocals() {
CodeReader reader = getCodeReader();
return reader.getMaxLocals();
}
@Override
public int getMaxStackHeight() {
CodeReader reader = getCodeReader();
// note that Shrike returns the maximum index in the zero-indexed stack
// array.
// Instead, we want the max number of entries on the stack.
// So we add 1.
// Additionally, ShrikeBT may add additional stack entries with
// Constant instructions. We add an additional 1 to account for this,
// which seems to handle all ShrikeBT code generation patterns.
// TODO: ShrikeBT should have a getMaxStack method on Decoder, I think.
return reader.getMaxStack() + 2;
}
@Override
public boolean hasExceptionHandler() {
CodeReader reader = getCodeReader();
if (reader == null)
return false;
int[] handlers = reader.getRawHandlers();
return handlers != null && handlers.length > 0;
}
@Override
protected String[] getDeclaredExceptionTypeNames() throws InvalidClassFileException {
ExceptionsReader reader = getExceptionReader();
if (reader == null) {
return null;
} else {
return reader.getClasses();
}
}
@Override
protected void processDebugInfo(BytecodeInfo bcInfo) throws InvalidClassFileException {
CodeReader cr = getCodeReader();
bcInfo.lineNumberMap = LineNumberTableReader.makeBytecodeToSourceMap(cr);
bcInfo.localVariableMap = LocalVariableTableReader.makeVarMap(cr);
}
@Override
public String getLocalVariableName(int bcIndex, int localNumber){
int[][] map = null;
try {
map = getBCInfo().localVariableMap;
} catch (InvalidClassFileException e1) {
return null;
}
if (localNumber > getMaxLocals()) {
throw new IllegalArgumentException("illegal local number: " + localNumber + ", method " + getName() + " uses at most "
+ getMaxLocals());
}
if (map == null) {
return null;
} else {
int[] localPairs = map[bcIndex];
int localIndex = localNumber * 2;
if (localPairs == null || localIndex >= localPairs.length) {
// no information about the specified local at this program point
return null;
}
int nameIndex = localPairs[localIndex];
if (nameIndex == 0) {
return null;
} else {
try {
return getClassReader().getCP().getCPUtf8(nameIndex);
} catch (InvalidClassFileException e) {
e.printStackTrace();
Assertions.UNREACHABLE();
return null;
}
}
}
}
/*
* TODO: cache for efficiency?
*
* @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
*/
@Override
public boolean hasLocalVariableTable() {
try {
ClassReader.AttrIterator iter = new ClassReader.AttrIterator();
getCodeReader().initAttributeIterator(iter);
for (; iter.isValid(); iter.advance()) {
if (iter.getName().equals("LocalVariableTable")) {
return true;
}
}
return false;
} catch (InvalidClassFileException e) {
e.printStackTrace();
Assertions.UNREACHABLE();
return false;
}
}
private ClassReader getClassReader() {
return ((ShrikeClass) getDeclaringClass()).getReader();
}
private CodeReader getCodeReader() {
ClassReader.AttrIterator iter = new AttrIterator();
getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);
// search for the code attribute
CodeReader code = null;
try {
for (; iter.isValid(); iter.advance()) {
if (iter.getName().equals("Code")) {
code = new CodeReader(iter);
break;
}
}
} catch (InvalidClassFileException e) {
Assertions.UNREACHABLE();
}
return code;
}
private ExceptionsReader getExceptionReader() {
ClassReader.AttrIterator iter = new AttrIterator();
getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);
// search for the desired attribute
ExceptionsReader result = null;
try {
for (; iter.isValid(); iter.advance()) {
if (iter.getName().equals("Exceptions")) {
result = new ExceptionsReader(iter);
break;
}
}
} catch (InvalidClassFileException e) {
Assertions.UNREACHABLE();
}
return result;
}
private SignatureReader getSignatureReader() {
ClassReader.AttrIterator iter = new AttrIterator();
getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);
// search for the desired attribute
SignatureReader result = null;
try {
for (; iter.isValid(); iter.advance()) {
if (iter.getName().equals("Signature")) {
result = new SignatureReader(iter);
break;
}
}
} catch (InvalidClassFileException e) {
Assertions.UNREACHABLE();
}
return result;
}
private AnnotationsReader getAnnotationsReader(boolean runtimeInvisible) {
ClassReader.AttrIterator iter = new AttrIterator();
getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);
// search for the desired attribute
AnnotationsReader result = null;
try {
for (; iter.isValid(); iter.advance()) {
if (runtimeInvisible) {
if (iter.getName().equals(RuntimeInvisibleAnnotationsReader.attrName)) {
result = new RuntimeInvisibleAnnotationsReader(iter);
break;
}
} else {
if (iter.getName().equals(RuntimeVisibleAnnotationsReader.attrName)) {
result = new RuntimeVisibleAnnotationsReader(iter);
break;
}
}
}
} catch (InvalidClassFileException e) {
Assertions.UNREACHABLE();
}
return result;
}
private String computeGenericsSignature() throws InvalidClassFileException {
SignatureReader reader = getSignatureReader();
if (reader == null) {
return null;
} else {
return reader.getSignature();
}
}
public TypeReference getReturnType() {
return getReference().getReturnType();
}
public IClassHierarchy getClassHierarchy() {
return cha;
}
/**
* TODO: cache?
*
* @return raw "Signature" attribute from the bytecode
* @throws InvalidClassFileException
*/
private String getGenericsSignature() throws InvalidClassFileException {
return computeGenericsSignature();
}
/**
* UNDER CONSTRUCTION
*
* @throws InvalidClassFileException
*/
public MethodTypeSignature getMethodTypeSignature() throws InvalidClassFileException {
String sig = getGenericsSignature();
return sig == null ? null : MethodTypeSignature.make(sig);
}
/**
* read the runtime-invisible annotations from the class file
*/
public Collection<Annotation> getRuntimeInvisibleAnnotations() throws InvalidClassFileException {
return getAnnotations(true);
}
/**
* read the runtime-visible annotations from the class file
*/
public Collection<Annotation> getRuntimeVisibleAnnotations() throws InvalidClassFileException {
return getAnnotations(false);
}
public Collection<Annotation> getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException {
AnnotationsReader r = getAnnotationsReader(runtimeInvisible);
return Annotation.getAnnotationsFromReader(r, getDeclaringClass().getClassLoader().getReference());
}
private static final IndirectionData NO_INDIRECTIONS = new IndirectionData() {
private final int[] NOTHING = new int[0];
public int[] indirectlyReadLocals(int instructionIndex) {
return NOTHING;
}
public int[] indirectlyWrittenLocals(int instructionIndex) {
return NOTHING;
}
};
public IndirectionData getIndirectionData() {
return NO_INDIRECTIONS;
}
}