rename Shrike*Wrapper to just Shrike*
git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@552 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
parent
979451f05c
commit
429403c321
|
@ -0,0 +1,925 @@
|
|||
/*******************************************************************************
|
||||
* 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.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.ibm.wala.shrikeBT.ArrayLoadInstruction;
|
||||
import com.ibm.wala.shrikeBT.ArrayStoreInstruction;
|
||||
import com.ibm.wala.shrikeBT.BytecodeConstants;
|
||||
import com.ibm.wala.shrikeBT.CheckCastInstruction;
|
||||
import com.ibm.wala.shrikeBT.Constants;
|
||||
import com.ibm.wala.shrikeBT.Decoder;
|
||||
import com.ibm.wala.shrikeBT.ExceptionHandler;
|
||||
import com.ibm.wala.shrikeBT.GetInstruction;
|
||||
import com.ibm.wala.shrikeBT.IInvokeInstruction;
|
||||
import com.ibm.wala.shrikeBT.Instruction;
|
||||
import com.ibm.wala.shrikeBT.InvokeInstruction;
|
||||
import com.ibm.wala.shrikeBT.MonitorInstruction;
|
||||
import com.ibm.wala.shrikeBT.NewInstruction;
|
||||
import com.ibm.wala.shrikeBT.PutInstruction;
|
||||
import com.ibm.wala.shrikeCT.InvalidClassFileException;
|
||||
import com.ibm.wala.types.ClassLoaderReference;
|
||||
import com.ibm.wala.types.Descriptor;
|
||||
import com.ibm.wala.types.FieldReference;
|
||||
import com.ibm.wala.types.MethodReference;
|
||||
import com.ibm.wala.types.Selector;
|
||||
import com.ibm.wala.types.TypeName;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.Atom;
|
||||
import com.ibm.wala.util.Exceptions;
|
||||
import com.ibm.wala.util.ImmutableByteArray;
|
||||
import com.ibm.wala.util.ShrikeUtil;
|
||||
import com.ibm.wala.util.bytecode.BytecodeStream;
|
||||
import com.ibm.wala.util.collections.EmptyIterator;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
import com.ibm.wala.util.debug.Assertions;
|
||||
|
||||
/**
|
||||
*
|
||||
* A wrapper around a Shrike object that represents a method
|
||||
*
|
||||
* @author sfink
|
||||
*/
|
||||
public abstract class ShrikeBTMethod implements IMethod, BytecodeConstants {
|
||||
|
||||
/**
|
||||
* Some verbose progress output?
|
||||
*/
|
||||
private final static boolean verbose = false;
|
||||
|
||||
private static int methodsParsed = 0;
|
||||
|
||||
/**
|
||||
* A wrapper around the declaring class.
|
||||
*/
|
||||
protected final IClass declaringClass;
|
||||
|
||||
/**
|
||||
* Canonical reference for this method
|
||||
*/
|
||||
private MethodReference methodReference;
|
||||
|
||||
// break these out to save some space; they're computed lazily.
|
||||
protected static class BytecodeInfo {
|
||||
Decoder decoder;
|
||||
|
||||
CallSiteReference[] callSites;
|
||||
|
||||
FieldReference[] fieldsWritten;
|
||||
|
||||
FieldReference[] fieldsRead;
|
||||
|
||||
NewSiteReference[] newSites;
|
||||
|
||||
TypeReference[] arraysRead;
|
||||
|
||||
TypeReference[] arraysWritten;
|
||||
|
||||
TypeReference[] implicitExceptions;
|
||||
|
||||
TypeReference[] castTypes;
|
||||
|
||||
boolean hasMonitorOp;
|
||||
|
||||
/**
|
||||
* Mapping from instruction index to program counter.
|
||||
*/
|
||||
private int[] pcMap;
|
||||
|
||||
/**
|
||||
* Cached map representing line number information in ShrikeCT format TODO:
|
||||
* do more careful caching than just soft references
|
||||
*/
|
||||
protected int[] lineNumberMap;
|
||||
|
||||
/**
|
||||
* an array mapping bytecode offsets to arrays representing the local
|
||||
* variable maps for each offset; a local variable map is represented as an
|
||||
* array of localVars*2 elements, containing a pair (nameIndex, typeIndex)
|
||||
* for each local variable; a pair (0,0) indicates there is no information
|
||||
* for that local variable at that offset
|
||||
*/
|
||||
protected int[][] localVariableMap;
|
||||
|
||||
/**
|
||||
* Exception types this method might throw. Computed on demand.
|
||||
*/
|
||||
private TypeReference[] exceptionTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache the information about the method statements.
|
||||
*/
|
||||
protected BytecodeInfo bcInfo;
|
||||
|
||||
public ShrikeBTMethod(IClass klass) {
|
||||
this.declaringClass = klass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the program counter (bytecode index) for a particular Shrike
|
||||
* instruction index.
|
||||
*
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public int getBytecodeIndex(int instructionIndex) throws InvalidClassFileException {
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
return bcInfo.pcMap[instructionIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an Iterator of CallSiteReferences from this method.
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
Iterator<CallSiteReference> getCallSites() throws InvalidClassFileException {
|
||||
if (isNative()) {
|
||||
return EmptyIterator.instance();
|
||||
}
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
Iterator<CallSiteReference> empty = EmptyIterator.instance();
|
||||
return (bcInfo.callSites == null) ? empty : Arrays.asList(bcInfo.callSites).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an Iterator of NewlSiteReferences from this method.
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
Iterator<NewSiteReference> getNewSites() throws InvalidClassFileException {
|
||||
if (isNative()) {
|
||||
return EmptyIterator.instance();
|
||||
}
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
Iterator<NewSiteReference> empty = EmptyIterator.instance();
|
||||
return (bcInfo.newSites == null) ? empty : Arrays.asList(bcInfo.newSites).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Set <TypeReference>, the exceptions that statements in this method
|
||||
* may throw,
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public Collection getImplicitExceptionTypes() throws InvalidClassFileException {
|
||||
if (isNative()) {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
return (bcInfo.implicitExceptions == null) ? Arrays.asList(new TypeReference[0]) : Arrays.asList(bcInfo.implicitExceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method processBytecodes. Do a cheap pass over the bytecodes to collect some
|
||||
* mapping information. Some methods require this as a pre-req to accessing
|
||||
* ShrikeCT information.
|
||||
*
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public void processBytecodes() throws InvalidClassFileException {
|
||||
if (bcInfo != null) {
|
||||
// already done.
|
||||
return;
|
||||
}
|
||||
bcInfo = new BytecodeInfo();
|
||||
bcInfo.exceptionTypes = computeDeclaredExceptions();
|
||||
|
||||
if (isNative()) {
|
||||
return;
|
||||
}
|
||||
if (verbose) {
|
||||
methodsParsed += 1;
|
||||
if (methodsParsed % 100 == 0) {
|
||||
System.out.println(methodsParsed + " methods processed...");
|
||||
}
|
||||
}
|
||||
|
||||
processBytecodesWithShrikeBT();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff this method has a monitorenter or monitorexit
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public boolean hasMonitorOp() throws InvalidClassFileException {
|
||||
if (isNative()) {
|
||||
return false;
|
||||
}
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
return bcInfo.hasMonitorOp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Set of FieldReference
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public Iterator<FieldReference> getFieldsWritten() throws InvalidClassFileException {
|
||||
if (isNative()) {
|
||||
return EmptyIterator.instance();
|
||||
}
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
if (bcInfo.fieldsWritten == null) {
|
||||
return EmptyIterator.instance();
|
||||
} else {
|
||||
List<FieldReference> l = Arrays.asList(bcInfo.fieldsWritten);
|
||||
return l.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator of FieldReference
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public Iterator<FieldReference> getFieldsRead() throws InvalidClassFileException {
|
||||
if (isNative()) {
|
||||
return EmptyIterator.instance();
|
||||
}
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
if (bcInfo.fieldsRead == null) {
|
||||
return EmptyIterator.instance();
|
||||
} else {
|
||||
List<FieldReference> l = Arrays.asList(bcInfo.fieldsRead);
|
||||
return l.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator of TypeReference
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public Iterator getArraysRead() throws InvalidClassFileException {
|
||||
if (isNative()) {
|
||||
return EmptyIterator.instance();
|
||||
}
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
|
||||
return (bcInfo.arraysRead == null) ? EmptyIterator.instance() : Arrays.asList(bcInfo.arraysRead).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator of TypeReference
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public Iterator getArraysWritten() throws InvalidClassFileException {
|
||||
if (isNative()) {
|
||||
return EmptyIterator.instance();
|
||||
}
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
|
||||
return (bcInfo.arraysWritten == null) ? EmptyIterator.instance() : Arrays.asList(bcInfo.arraysWritten).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator of TypeReference
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public Iterator getCastTypes() throws InvalidClassFileException {
|
||||
if (isNative()) {
|
||||
return EmptyIterator.instance();
|
||||
}
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
|
||||
return (bcInfo.castTypes == null) ? EmptyIterator.instance() : Arrays.asList(bcInfo.castTypes).iterator();
|
||||
}
|
||||
|
||||
protected abstract byte[] getBytecodes();
|
||||
|
||||
/**
|
||||
* Method getBytecodeStream.
|
||||
*
|
||||
* @return the bytecode stream for this method, or null if no bytecodes.
|
||||
*/
|
||||
public BytecodeStream getBytecodeStream() {
|
||||
byte[] bytecodes = getBytecodes();
|
||||
if (bytecodes == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new BytecodeStream(this, bytecodes);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String getMethodName() throws InvalidClassFileException;
|
||||
|
||||
protected abstract String getMethodSignature() throws InvalidClassFileException;
|
||||
|
||||
private MethodReference computeMethodReference() {
|
||||
try {
|
||||
Atom name = Atom.findOrCreateUnicodeAtom(getMethodName());
|
||||
ImmutableByteArray desc = ImmutableByteArray.make(getMethodSignature());
|
||||
Descriptor D = Descriptor.findOrCreate(desc);
|
||||
return
|
||||
MethodReference.findOrCreate(declaringClass.getReference(), name, D);
|
||||
} catch (InvalidClassFileException e) {
|
||||
Assertions.UNREACHABLE();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public MethodReference getReference() {
|
||||
if (methodReference == null) {
|
||||
methodReference = computeMethodReference();
|
||||
}
|
||||
return methodReference;
|
||||
}
|
||||
|
||||
public boolean isClinit() {
|
||||
return getReference().getSelector().equals(MethodReference.clinitSelector);
|
||||
}
|
||||
|
||||
public boolean isInit() {
|
||||
return getReference().getName().equals(MethodReference.initAtom);
|
||||
}
|
||||
|
||||
protected abstract int getModifiers();
|
||||
|
||||
public boolean isNative() {
|
||||
return ((getModifiers() & Constants.ACC_NATIVE) != 0);
|
||||
}
|
||||
|
||||
public boolean isAbstract() {
|
||||
return ((getModifiers() & Constants.ACC_ABSTRACT) != 0);
|
||||
}
|
||||
|
||||
public boolean isPrivate() {
|
||||
return ((getModifiers() & Constants.ACC_PRIVATE) != 0);
|
||||
}
|
||||
|
||||
public boolean isProtected() {
|
||||
return ((getModifiers() & Constants.ACC_PROTECTED) != 0);
|
||||
}
|
||||
|
||||
public boolean isPublic() {
|
||||
return ((getModifiers() & Constants.ACC_PUBLIC) != 0);
|
||||
}
|
||||
|
||||
public boolean isFinal() {
|
||||
return ((getModifiers() & Constants.ACC_FINAL) != 0);
|
||||
}
|
||||
|
||||
public boolean isSynchronized() {
|
||||
return ((getModifiers() & Constants.ACC_SYNCHRONIZED) != 0);
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
return ((getModifiers() & Constants.ACC_STATIC) != 0);
|
||||
}
|
||||
|
||||
public boolean isSynthetic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public IClass getDeclaringClass() {
|
||||
return declaringClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the decoder object for this method, or create one if necessary.
|
||||
*
|
||||
* @return null if the method has no code.
|
||||
*/
|
||||
protected abstract Decoder makeDecoder();
|
||||
|
||||
/**
|
||||
* Walk through the bytecodes and collect trivial information.
|
||||
*
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
protected abstract void processDebugInfo(BytecodeInfo bcInfo) throws InvalidClassFileException;
|
||||
|
||||
|
||||
private void processBytecodesWithShrikeBT() throws InvalidClassFileException {
|
||||
bcInfo.decoder = makeDecoder();
|
||||
if (Assertions.verifyAssertions) {
|
||||
if (!isAbstract() && bcInfo.decoder == null) {
|
||||
Assertions.UNREACHABLE("bad method " + getReference());
|
||||
}
|
||||
}
|
||||
if (bcInfo.decoder == null) {
|
||||
return;
|
||||
}
|
||||
bcInfo.pcMap = bcInfo.decoder.getInstructionsToBytecodes();
|
||||
|
||||
processDebugInfo(bcInfo);
|
||||
|
||||
SimpleVisitor simpleVisitor = new SimpleVisitor();
|
||||
|
||||
Instruction[] instructions = bcInfo.decoder.getInstructions();
|
||||
for (int i = 0; i < instructions.length; i++) {
|
||||
simpleVisitor.setInstructionIndex(i);
|
||||
instructions[i].visit(simpleVisitor);
|
||||
if (Exceptions.isPEI(instructions[i])) {
|
||||
Collection<TypeReference> t = Exceptions.getIndependentExceptionTypes(instructions[i]);
|
||||
simpleVisitor.implicitExceptions.addAll(t);
|
||||
}
|
||||
}
|
||||
|
||||
// copy the Set results into arrays; will use less
|
||||
// storage
|
||||
copyVisitorSetsToArrays(simpleVisitor);
|
||||
}
|
||||
|
||||
private void copyVisitorSetsToArrays(SimpleVisitor simpleVisitor) {
|
||||
bcInfo.newSites = new NewSiteReference[simpleVisitor.newSites.size()];
|
||||
int i = 0;
|
||||
for (Iterator<NewSiteReference> it = simpleVisitor.newSites.iterator(); it.hasNext();) {
|
||||
bcInfo.newSites[i++] = it.next();
|
||||
}
|
||||
|
||||
bcInfo.fieldsRead = new FieldReference[simpleVisitor.fieldsRead.size()];
|
||||
i = 0;
|
||||
for (Iterator<FieldReference> it = simpleVisitor.fieldsRead.iterator(); it.hasNext();) {
|
||||
bcInfo.fieldsRead[i++] = it.next();
|
||||
}
|
||||
|
||||
bcInfo.fieldsRead = new FieldReference[simpleVisitor.fieldsRead.size()];
|
||||
i = 0;
|
||||
for (Iterator<FieldReference> it = simpleVisitor.fieldsRead.iterator(); it.hasNext();) {
|
||||
bcInfo.fieldsRead[i++] = it.next();
|
||||
}
|
||||
|
||||
bcInfo.fieldsWritten = new FieldReference[simpleVisitor.fieldsWritten.size()];
|
||||
i = 0;
|
||||
for (Iterator<FieldReference> it = simpleVisitor.fieldsWritten.iterator(); it.hasNext();) {
|
||||
bcInfo.fieldsWritten[i++] = it.next();
|
||||
}
|
||||
|
||||
bcInfo.callSites = new CallSiteReference[simpleVisitor.callSites.size()];
|
||||
i = 0;
|
||||
for (Iterator<CallSiteReference> it = simpleVisitor.callSites.iterator(); it.hasNext();) {
|
||||
bcInfo.callSites[i++] = it.next();
|
||||
}
|
||||
|
||||
bcInfo.arraysRead = new TypeReference[simpleVisitor.arraysRead.size()];
|
||||
i = 0;
|
||||
for (Iterator<TypeReference> it = simpleVisitor.arraysRead.iterator(); it.hasNext();) {
|
||||
bcInfo.arraysRead[i++] = it.next();
|
||||
}
|
||||
|
||||
bcInfo.arraysWritten = new TypeReference[simpleVisitor.arraysWritten.size()];
|
||||
i = 0;
|
||||
for (Iterator<TypeReference> it = simpleVisitor.arraysWritten.iterator(); it.hasNext();) {
|
||||
bcInfo.arraysWritten[i++] = it.next();
|
||||
}
|
||||
|
||||
bcInfo.implicitExceptions = new TypeReference[simpleVisitor.implicitExceptions.size()];
|
||||
i = 0;
|
||||
for (Iterator it = simpleVisitor.implicitExceptions.iterator(); it.hasNext();) {
|
||||
bcInfo.implicitExceptions[i++] = (TypeReference) it.next();
|
||||
}
|
||||
|
||||
bcInfo.castTypes = new TypeReference[simpleVisitor.castTypes.size()];
|
||||
i = 0;
|
||||
for (Iterator<TypeReference> it = simpleVisitor.castTypes.iterator(); it.hasNext();) {
|
||||
bcInfo.castTypes[i++] = it.next();
|
||||
}
|
||||
|
||||
bcInfo.hasMonitorOp = simpleVisitor.hasMonitorOp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return getReference().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#equals(Object)
|
||||
*/
|
||||
public boolean equals(Object obj) {
|
||||
// instanceof is OK because this class is final.
|
||||
// if (this.getClass().equals(obj.getClass())) {
|
||||
if (obj instanceof ShrikeBTMethod) {
|
||||
ShrikeBTMethod that = (ShrikeBTMethod) obj;
|
||||
return (getDeclaringClass().equals(that.getDeclaringClass()) && getReference().equals(that.getReference()));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return 9661 * getReference().hashCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IMethod#getMaxLocals()
|
||||
*/
|
||||
public abstract int getMaxLocals();
|
||||
|
||||
// TODO: ShrikeBT should have a getMaxStack method on Decoder, I think.
|
||||
public abstract int getMaxStackHeight();
|
||||
|
||||
public Atom getName() {
|
||||
return getReference().getName();
|
||||
}
|
||||
|
||||
public Descriptor getDescriptor() {
|
||||
return getReference().getDescriptor();
|
||||
}
|
||||
|
||||
private class SimpleVisitor extends Instruction.Visitor {
|
||||
|
||||
// TODO: make a better Set implementation for these.
|
||||
Set<CallSiteReference> callSites = HashSetFactory.make(5);
|
||||
|
||||
Set<FieldReference> fieldsWritten = HashSetFactory.make(5);
|
||||
|
||||
Set<FieldReference> fieldsRead = HashSetFactory.make(5);
|
||||
|
||||
Set<NewSiteReference> newSites = HashSetFactory.make(5);
|
||||
|
||||
Set<TypeReference> arraysRead = HashSetFactory.make(5);
|
||||
|
||||
Set<TypeReference> arraysWritten = HashSetFactory.make(5);
|
||||
|
||||
Set<TypeReference> implicitExceptions = HashSetFactory.make(5);
|
||||
|
||||
Set<TypeReference> castTypes = HashSetFactory.make(5);
|
||||
|
||||
boolean hasMonitorOp;
|
||||
|
||||
private int instructionIndex;
|
||||
|
||||
public void setInstructionIndex(int i) {
|
||||
instructionIndex = i;
|
||||
}
|
||||
|
||||
public int getProgramCounter() throws InvalidClassFileException {
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
return bcInfo.pcMap[instructionIndex];
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitMonitor(com.ibm.wala.shrikeBT.MonitorInstruction)
|
||||
*/
|
||||
public void visitMonitor(MonitorInstruction instruction) {
|
||||
hasMonitorOp = true;
|
||||
}
|
||||
|
||||
public void visitNew(NewInstruction instruction) {
|
||||
ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
|
||||
TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType());
|
||||
try {
|
||||
newSites.add(NewSiteReference.make(getProgramCounter(), t));
|
||||
} catch (InvalidClassFileException e) {
|
||||
e.printStackTrace();
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
public void visitGet(GetInstruction instruction) {
|
||||
ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
|
||||
FieldReference f = ShrikeUtil.makeFieldReference(loader, instruction.getClassType(), instruction.getFieldName(), instruction
|
||||
.getFieldType());
|
||||
fieldsRead.add(f);
|
||||
}
|
||||
|
||||
public void visitPut(PutInstruction instruction) {
|
||||
ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
|
||||
FieldReference f = ShrikeUtil.makeFieldReference(loader, instruction.getClassType(), instruction.getFieldName(), instruction
|
||||
.getFieldType());
|
||||
fieldsWritten.add(f);
|
||||
}
|
||||
|
||||
public void visitInvoke(InvokeInstruction instruction) {
|
||||
ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
|
||||
MethodReference m = ShrikeUtil.makeMethodReference(loader, instruction.getClassType(), instruction.getMethodName(),
|
||||
instruction.getMethodSignature());
|
||||
int programCounter = 0;
|
||||
try {
|
||||
programCounter = getProgramCounter();
|
||||
} catch (InvalidClassFileException e) {
|
||||
e.printStackTrace();
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
CallSiteReference site = null;
|
||||
int nParams = m.getNumberOfParameters();
|
||||
switch (instruction.getInvocationMode()) {
|
||||
case Constants.OP_invokestatic:
|
||||
site = CallSiteReference.make(programCounter, m, IInvokeInstruction.Dispatch.STATIC);
|
||||
break;
|
||||
case Constants.OP_invokeinterface:
|
||||
site = CallSiteReference.make(programCounter, m, IInvokeInstruction.Dispatch.INTERFACE);
|
||||
nParams++;
|
||||
break;
|
||||
case Constants.OP_invokespecial:
|
||||
site = CallSiteReference.make(programCounter, m, IInvokeInstruction.Dispatch.SPECIAL);
|
||||
nParams++;
|
||||
break;
|
||||
case Constants.OP_invokevirtual:
|
||||
site = CallSiteReference.make(programCounter, m, IInvokeInstruction.Dispatch.VIRTUAL);
|
||||
nParams++;
|
||||
break;
|
||||
default:
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
callSites.add(site);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayLoad(com.ibm.wala.shrikeBT.ArrayLoadInstruction)
|
||||
*/
|
||||
public void visitArrayLoad(ArrayLoadInstruction instruction) {
|
||||
arraysRead.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), instruction.getType()));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.shrikeBT.Instruction.Visitor#visitArrayStore(com.ibm.wala.shrikeBT.ArrayStoreInstruction)
|
||||
*/
|
||||
public void visitArrayStore(ArrayStoreInstruction instruction) {
|
||||
arraysWritten.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), instruction.getType()));
|
||||
}
|
||||
|
||||
public void visitCheckCast(CheckCastInstruction instruction) {
|
||||
castTypes.add(ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), instruction.getType()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method getInstructions.
|
||||
*
|
||||
* @return Instruction[]
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public Instruction[] getInstructions() throws InvalidClassFileException {
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
if (bcInfo.decoder == null) {
|
||||
return null;
|
||||
} else {
|
||||
return bcInfo.decoder.getInstructions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method getHandlers.
|
||||
*
|
||||
* @return ExceptionHandler[][]
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public ExceptionHandler[][] getHandlers() throws InvalidClassFileException {
|
||||
if (bcInfo == null) {
|
||||
processBytecodes();
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
if (bcInfo.decoder == null) {
|
||||
return null;
|
||||
} else {
|
||||
return bcInfo.decoder.getHandlers();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method getParameterType. By convention, for a non-static method,
|
||||
* getParameterType(0) is the this pointer
|
||||
*
|
||||
* @param i
|
||||
* @return TypeReference
|
||||
*/
|
||||
public TypeReference getParameterType(int i) {
|
||||
if (!isStatic()) {
|
||||
if (i == 0)
|
||||
return declaringClass.getReference();
|
||||
else
|
||||
return getReference().getParameterType(i - 1);
|
||||
} else {
|
||||
return getReference().getParameterType(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method getNumberOfParameters. This result includes the "this" pointer if
|
||||
* applicable
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getNumberOfParameters() {
|
||||
if (isStatic() || isClinit()) {
|
||||
return getReference().getNumberOfParameters();
|
||||
} else {
|
||||
return getReference().getNumberOfParameters() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IMethod#hasExceptionHandler()
|
||||
*/
|
||||
public abstract boolean hasExceptionHandler();
|
||||
|
||||
/**
|
||||
* Clients should not modify the returned array. TODO: clone to avoid the
|
||||
* problem?
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
|
||||
*/
|
||||
public TypeReference[] getDeclaredExceptions() {
|
||||
|
||||
if (bcInfo == null) {
|
||||
try {
|
||||
processBytecodes();
|
||||
} catch (InvalidClassFileException e) {
|
||||
e.printStackTrace();
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
return (bcInfo.exceptionTypes == null) ? new TypeReference[0] : bcInfo.exceptionTypes;
|
||||
}
|
||||
|
||||
protected abstract String[] getDeclaredExceptionTypeNames() throws InvalidClassFileException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IMethod#getDeclaredExceptions()
|
||||
*/
|
||||
private TypeReference[] computeDeclaredExceptions() {
|
||||
try {
|
||||
String[] strings = getDeclaredExceptionTypeNames();
|
||||
if (strings == null) return null;
|
||||
|
||||
ClassLoaderReference loader = getDeclaringClass().getClassLoader().getReference();
|
||||
|
||||
TypeReference[] result = new TypeReference[strings.length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = TypeReference.findOrCreate(loader, TypeName.findOrCreate(ImmutableByteArray.make("L" + strings[i])));
|
||||
}
|
||||
return result;
|
||||
} catch (InvalidClassFileException e) {
|
||||
Assertions.UNREACHABLE();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IMethod#getLineNumber(int)
|
||||
*/
|
||||
public int getLineNumber(int bcIndex) {
|
||||
if (bcInfo == null) {
|
||||
try {
|
||||
processBytecodes();
|
||||
} catch (InvalidClassFileException e) {
|
||||
e.printStackTrace();
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
return (bcInfo.lineNumberMap == null) ? -1 : bcInfo.lineNumberMap[bcIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Set <TypeReference>
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public Set<TypeReference> getCaughtExceptionTypes() throws InvalidClassFileException {
|
||||
|
||||
ExceptionHandler[][] handlers = getHandlers();
|
||||
if (handlers == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
HashSet<TypeReference> result = HashSetFactory.make(10);
|
||||
ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader();
|
||||
for (int i = 0; i < handlers.length; i++) {
|
||||
for (int j = 0; j < handlers[i].length; j++) {
|
||||
TypeReference t = ShrikeUtil.makeTypeReference(loader, handlers[i][j].getCatchClass());
|
||||
if (t == null) {
|
||||
t = TypeReference.JavaLangThrowable;
|
||||
}
|
||||
result.add(t);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IMethod#getSignature()
|
||||
*/
|
||||
public String getSignature() {
|
||||
return getReference().getSignature();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IMethod#getSelector()
|
||||
*/
|
||||
public Selector getSelector() {
|
||||
return getReference().getSelector();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IMethod#getLocalVariableName(int, int)
|
||||
*/
|
||||
|
||||
public abstract String getLocalVariableName(int bcIndex, int localNumber);
|
||||
|
||||
/*
|
||||
* TODO: cache for efficiency? (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
|
||||
*/
|
||||
public abstract boolean hasLocalVariableTable();
|
||||
|
||||
/**
|
||||
* Clear all optional cached data associated with this class
|
||||
*/
|
||||
public void clearCaches() {
|
||||
bcInfo = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
/*******************************************************************************
|
||||
* 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 com.ibm.wala.ipa.cha.ClassHierarchy;
|
||||
import com.ibm.wala.shrikeBT.Decoder;
|
||||
import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
|
||||
import com.ibm.wala.shrikeCT.ClassReader;
|
||||
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.ClassReader.AttrIterator;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.debug.Assertions;
|
||||
|
||||
/**
|
||||
*
|
||||
* A wrapper around a Shrike object that represents a method
|
||||
*
|
||||
* @author sfink
|
||||
*/
|
||||
public final class ShrikeCTMethod extends ShrikeBTMethod {
|
||||
|
||||
/**
|
||||
* The index of this method in the declaring class's method list according to
|
||||
* Shrike CT.
|
||||
*/
|
||||
private int shrikeMethodIndex;
|
||||
|
||||
/**
|
||||
* JVM-level modifiers for this method a value of -1 means "uninitialized"
|
||||
*/
|
||||
private int modifiers = -1;
|
||||
|
||||
private final ClassHierarchy cha;
|
||||
|
||||
public ShrikeCTMethod(IClass klass, int index) {
|
||||
super(klass);
|
||||
this.shrikeMethodIndex = index;
|
||||
this.cha = klass.getClassHierarchy();
|
||||
}
|
||||
|
||||
public byte[] getBytecodes() {
|
||||
CodeReader code = getCodeReader();
|
||||
if (code == null) {
|
||||
return null;
|
||||
} else {
|
||||
return code.getBytecode();
|
||||
}
|
||||
}
|
||||
|
||||
protected String getMethodName() throws InvalidClassFileException {
|
||||
ClassReader reader = getClassReader();
|
||||
return reader.getMethodName(shrikeMethodIndex);
|
||||
}
|
||||
|
||||
protected String getMethodSignature() throws InvalidClassFileException {
|
||||
ClassReader reader = getClassReader();
|
||||
return reader.getMethodType(shrikeMethodIndex);
|
||||
}
|
||||
|
||||
protected int getModifiers() {
|
||||
if (modifiers == -1) {
|
||||
modifiers = getClassReader().getMethodAccessFlags(shrikeMethodIndex);
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public int getMaxLocals() {
|
||||
CodeReader reader = getCodeReader();
|
||||
return reader.getMaxLocals();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public boolean hasExceptionHandler() {
|
||||
CodeReader reader = getCodeReader();
|
||||
if (reader == null)
|
||||
return false;
|
||||
int[] handlers = reader.getRawHandlers();
|
||||
return handlers != null && handlers.length > 0;
|
||||
}
|
||||
|
||||
protected String[] getDeclaredExceptionTypeNames() throws InvalidClassFileException {
|
||||
ExceptionsReader reader = getExceptionReader();
|
||||
if (reader == null) {
|
||||
return null;
|
||||
} else {
|
||||
return reader.getClasses();
|
||||
}
|
||||
}
|
||||
|
||||
protected void processDebugInfo(BytecodeInfo bcInfo) throws InvalidClassFileException {
|
||||
CodeReader cr = getCodeReader();
|
||||
bcInfo.lineNumberMap = LineNumberTableReader.makeBytecodeToSourceMap(cr);
|
||||
bcInfo.localVariableMap = LocalVariableTableReader.makeVarMap(cr);
|
||||
}
|
||||
|
||||
|
||||
public String getLocalVariableName(int bcIndex, int localNumber) {
|
||||
if (bcInfo == null) {
|
||||
try {
|
||||
processBytecodes();
|
||||
} catch (InvalidClassFileException e) {
|
||||
e.printStackTrace();
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
if (Assertions.verifyAssertions) {
|
||||
Assertions._assert(bcInfo != null);
|
||||
}
|
||||
int[][] map = bcInfo.localVariableMap;
|
||||
|
||||
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? (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable()
|
||||
*/
|
||||
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().toString().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().toString().equals("Exceptions")) {
|
||||
result = new ExceptionsReader(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (InvalidClassFileException e) {
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public TypeReference getReturnType() {
|
||||
return getReference().getReturnType();
|
||||
}
|
||||
|
||||
public ClassHierarchy getClassHierarchy() {
|
||||
return cha;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,840 @@
|
|||
/*******************************************************************************
|
||||
* 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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.ibm.wala.ipa.cha.ClassHierarchy;
|
||||
import com.ibm.wala.ipa.cha.ClassHierarchyException;
|
||||
import com.ibm.wala.ipa.cha.ClassHierarchyWarning;
|
||||
import com.ibm.wala.shrikeBT.Constants;
|
||||
import com.ibm.wala.shrikeCT.ClassConstants;
|
||||
import com.ibm.wala.shrikeCT.ClassReader;
|
||||
import com.ibm.wala.shrikeCT.InvalidClassFileException;
|
||||
import com.ibm.wala.types.FieldReference;
|
||||
import com.ibm.wala.types.MethodReference;
|
||||
import com.ibm.wala.types.Selector;
|
||||
import com.ibm.wala.types.TypeName;
|
||||
import com.ibm.wala.types.TypeReference;
|
||||
import com.ibm.wala.util.Atom;
|
||||
import com.ibm.wala.util.ImmutableByteArray;
|
||||
import com.ibm.wala.util.ShrikeClassReaderHandle;
|
||||
import com.ibm.wala.util.collections.BimodalMap;
|
||||
import com.ibm.wala.util.collections.HashMapFactory;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
import com.ibm.wala.util.collections.SmallMap;
|
||||
import com.ibm.wala.util.debug.Assertions;
|
||||
import com.ibm.wala.util.debug.Trace;
|
||||
import com.ibm.wala.util.warnings.Warning;
|
||||
import com.ibm.wala.util.warnings.WarningSet;
|
||||
|
||||
/**
|
||||
* A class read from Shrike
|
||||
*
|
||||
* @author sfink
|
||||
*/
|
||||
public final class ShrikeClass implements IClass {
|
||||
|
||||
static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* The Shrike object that knows how to read the class file
|
||||
*/
|
||||
private final ShrikeClassReaderHandle reader;
|
||||
|
||||
/**
|
||||
* The object that loaded this class.
|
||||
*/
|
||||
private final IClassLoader loader;
|
||||
|
||||
/**
|
||||
* Governing class hierarchy for this class
|
||||
*/
|
||||
private final ClassHierarchy cha;
|
||||
|
||||
/**
|
||||
* A mapping from Selector to IMethod
|
||||
*
|
||||
* TODO: get rid of this for classes (though keep it for interfaces) instead
|
||||
* ... use a VMT.
|
||||
*/
|
||||
private Map<Selector, IMethod> methodMap;
|
||||
|
||||
/**
|
||||
* A mapping from Selector to IMethod used to cache method lookups from
|
||||
* superclasses
|
||||
*/
|
||||
private Map<Selector, IMethod> inheritCache;
|
||||
|
||||
/**
|
||||
* Canonical type representation
|
||||
*/
|
||||
private TypeReference typeReference;
|
||||
|
||||
/**
|
||||
* An object to track warnings
|
||||
*/
|
||||
private final WarningSet warnings;
|
||||
|
||||
/**
|
||||
* superclass
|
||||
*/
|
||||
private IClass superClass;
|
||||
|
||||
/**
|
||||
* Compute the superclass lazily.
|
||||
*/
|
||||
private boolean superclassComputed = false;
|
||||
|
||||
/**
|
||||
* An Atom which holds the name of the super class. We cache this for
|
||||
* efficiency reasons.
|
||||
*/
|
||||
private ImmutableByteArray superName;
|
||||
|
||||
/**
|
||||
* The names of interfaces for this class. We cache this for efficiency
|
||||
* reasons.
|
||||
*/
|
||||
private ImmutableByteArray[] interfaceNames;
|
||||
|
||||
/**
|
||||
* The IClasses that represent all interfaces this class implements (if it's a
|
||||
* class) or extends (it it's an interface)
|
||||
*/
|
||||
private Collection<IClass> allInterfaces = null;
|
||||
|
||||
/**
|
||||
* The instance fields declared in this class.
|
||||
*/
|
||||
private IField[] instanceFields;
|
||||
|
||||
/**
|
||||
* The static fields declared in this class.
|
||||
*/
|
||||
private IField[] staticFields;
|
||||
|
||||
/**
|
||||
* JVM-level modifiers; cached here for efficiency
|
||||
*/
|
||||
private int modifiers;
|
||||
|
||||
/**
|
||||
* hash code; cached here for efficiency
|
||||
*/
|
||||
private final int hashCode;
|
||||
|
||||
/**
|
||||
* @param reader
|
||||
* @param loader
|
||||
* @param cha
|
||||
* @param warnings
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
public ShrikeClass(ShrikeClassReaderHandle reader, IClassLoader loader, ClassHierarchy cha, WarningSet warnings)
|
||||
throws InvalidClassFileException {
|
||||
if (reader == null)
|
||||
throw new NullPointerException();
|
||||
this.reader = reader;
|
||||
this.loader = loader;
|
||||
this.cha = cha;
|
||||
this.warnings = warnings;
|
||||
computeTypeReference();
|
||||
this.hashCode = 2161 * getReference().hashCode();
|
||||
// as long as the reader is around, pull more data out
|
||||
// of it before the soft reference to it disappears
|
||||
computeSuperName();
|
||||
computeModifiers();
|
||||
computeInterfaceNames();
|
||||
computeFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the fields declared by this class
|
||||
*
|
||||
* @throws InvalidClassFileException
|
||||
* iff Shrike fails to read the class file correctly
|
||||
*/
|
||||
private void computeFields() throws InvalidClassFileException {
|
||||
ClassReader cr = reader.get();
|
||||
int fieldCount = cr.getFieldCount();
|
||||
List<FieldImpl> instanceList = new ArrayList<FieldImpl>(fieldCount);
|
||||
List<FieldImpl> staticList = new ArrayList<FieldImpl>(fieldCount);
|
||||
try {
|
||||
for (int i = 0; i < fieldCount; i++) {
|
||||
int accessFlags = cr.getFieldAccessFlags(i);
|
||||
Atom name = Atom.findOrCreateUnicodeAtom(cr.getFieldName(i));
|
||||
ImmutableByteArray b = ImmutableByteArray.make(cr.getFieldType(i));
|
||||
|
||||
if ((accessFlags & ClassConstants.ACC_STATIC) == 0) {
|
||||
addFieldToList(instanceList, name, b, accessFlags);
|
||||
} else {
|
||||
addFieldToList(staticList, name, b, accessFlags);
|
||||
}
|
||||
}
|
||||
instanceFields = new IField[instanceList.size()];
|
||||
populateFieldArrayFromList(instanceList, instanceFields);
|
||||
staticFields = new IField[staticList.size()];
|
||||
populateFieldArrayFromList(staticList, staticFields);
|
||||
|
||||
} catch (InvalidClassFileException e) {
|
||||
e.printStackTrace();
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
private void populateFieldArrayFromList(List<FieldImpl> L, IField[] A) {
|
||||
Iterator<FieldImpl> it = L.iterator();
|
||||
for (int i = 0; i < A.length; i++) {
|
||||
A[i] = it.next();
|
||||
}
|
||||
}
|
||||
|
||||
private void addFieldToList(List<FieldImpl> L, Atom name, ImmutableByteArray fieldType, int accessFlags) {
|
||||
TypeName T = null;
|
||||
if (fieldType.get(fieldType.length() - 1) == ';') {
|
||||
T = TypeName.findOrCreate(fieldType, 0, fieldType.length() - 1);
|
||||
} else {
|
||||
T = TypeName.findOrCreate(fieldType);
|
||||
}
|
||||
TypeReference type = TypeReference.findOrCreate(getClassLoader().getReference(), T);
|
||||
FieldReference fr = FieldReference.findOrCreate(getReference(), name, type);
|
||||
FieldImpl f = new FieldImpl(this, fr, accessFlags);
|
||||
L.add(f);
|
||||
}
|
||||
|
||||
public IClassLoader getClassLoader() {
|
||||
return loader;
|
||||
}
|
||||
|
||||
public boolean isInterface() {
|
||||
boolean result = ((modifiers & Constants.ACC_INTERFACE) != 0);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#isAbstract()
|
||||
*/
|
||||
public boolean isAbstract() {
|
||||
boolean result = ((modifiers & Constants.ACC_ABSTRACT) != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
private void computeModifiers() throws InvalidClassFileException {
|
||||
modifiers = reader.get().getAccessFlags();
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that this is called from the constructor, at which point this class is
|
||||
* not yet ready to actually load the superclass. Instead, we pull out the
|
||||
* name of the superclass and cache it here, to avoid hitting the reader
|
||||
* later.
|
||||
*/
|
||||
private void computeSuperName() {
|
||||
try {
|
||||
String s = reader.get().getSuperName();
|
||||
if (s != null) {
|
||||
superName = ImmutableByteArray.make("L" + s);
|
||||
}
|
||||
} catch (InvalidClassFileException e) {
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
private void computeSuperclass() {
|
||||
superclassComputed = true;
|
||||
|
||||
if (superName == null) {
|
||||
if (!getReference().equals(TypeReference.JavaLangObject)) {
|
||||
superClass = loader.lookupClass(TypeReference.JavaLangObject.getName(), getClassHierarchy());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
superClass = loader.lookupClass(TypeName.findOrCreate(superName), getClassHierarchy());
|
||||
if (DEBUG) {
|
||||
Trace.println("got superclass " + superClass + " for " + this);
|
||||
}
|
||||
}
|
||||
|
||||
public IClass getSuperclass() throws ClassHierarchyException {
|
||||
if (!superclassComputed) {
|
||||
computeSuperclass();
|
||||
}
|
||||
if (superClass == null && !getReference().equals(TypeReference.JavaLangObject)) {
|
||||
try {
|
||||
throw new ClassHierarchyException("No superclass " + reader.get().getSuperName() + " found for " + this);
|
||||
} catch (InvalidClassFileException e) {
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
return superClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that this is called from the constructor, at which point this class is
|
||||
* not yet ready to actually load the interfaces. Instead, we pull out the
|
||||
* name of the interfaces and cache it here, to avoid hitting the reader
|
||||
* later.
|
||||
*/
|
||||
private void computeInterfaceNames() {
|
||||
try {
|
||||
String[] s = reader.get().getInterfaceNames();
|
||||
interfaceNames = new ImmutableByteArray[s.length];
|
||||
for (int i = 0; i < interfaceNames.length; i++) {
|
||||
interfaceNames[i] = ImmutableByteArray.make("L" + s[i]);
|
||||
}
|
||||
} catch (InvalidClassFileException e) {
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method getAllInterfacesAsCollection.
|
||||
*
|
||||
* @return Collection of IClasses, representing the interfaces this class
|
||||
* implements.
|
||||
*/
|
||||
private Collection<IClass> computeAllInterfacesAsCollection() throws ClassHierarchyException {
|
||||
Collection<IClass> c = getDirectInterfaces();
|
||||
Set<IClass> result = HashSetFactory.make();
|
||||
for (Iterator<IClass> it = c.iterator(); it.hasNext();) {
|
||||
IClass klass = it.next();
|
||||
if (klass.isInterface()) {
|
||||
result.add(klass);
|
||||
} else {
|
||||
warnings.add(ClassHierarchyWarning.create("expected an interface " + klass));
|
||||
}
|
||||
}
|
||||
for (Iterator<IClass> it = c.iterator(); it.hasNext();) {
|
||||
ShrikeClass I = (ShrikeClass) it.next();
|
||||
if (I.isInterface()) {
|
||||
result.addAll(I.computeAllInterfacesAsCollection());
|
||||
} else {
|
||||
warnings.add(ClassHierarchyWarning.create("expected an interface " + I));
|
||||
}
|
||||
}
|
||||
|
||||
// now add any interfaces from the super class
|
||||
ShrikeClass sup = null;
|
||||
try {
|
||||
sup = (ShrikeClass) getSuperclass();
|
||||
} catch (ClassHierarchyException e1) {
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
if (sup != null) {
|
||||
result.addAll(sup.computeAllInterfacesAsCollection());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getDirectInterfaces()
|
||||
*/
|
||||
public Collection<IClass> getDirectInterfaces() {
|
||||
return array2IClassSet(interfaceNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method array2Set.
|
||||
*
|
||||
* @param interfaces
|
||||
* a set of class names
|
||||
* @return Set of all IClasses that can be loaded corresponding to the class
|
||||
* names in the interfaces array; raise warnings if classes can not be
|
||||
* loaded
|
||||
*/
|
||||
private Collection<IClass> array2IClassSet(ImmutableByteArray[] interfaces) {
|
||||
ArrayList<IClass> result = new ArrayList<IClass>(interfaces.length);
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
ImmutableByteArray name = interfaces[i];
|
||||
IClass klass = null;
|
||||
klass = loader.lookupClass(TypeName.findOrCreate(name), getClassHierarchy());
|
||||
if (klass == null) {
|
||||
warnings.add(ClassNotFoundWarning.create(name));
|
||||
} else {
|
||||
result.add(klass);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author sfink
|
||||
*
|
||||
* A warning for when we get a class not found exception
|
||||
*/
|
||||
private static class ClassNotFoundWarning extends Warning {
|
||||
|
||||
final ImmutableByteArray className;
|
||||
|
||||
ClassNotFoundWarning(ImmutableByteArray className) {
|
||||
super(Warning.SEVERE);
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return getClass().toString() + " : " + className;
|
||||
}
|
||||
|
||||
public static ClassNotFoundWarning create(ImmutableByteArray className) {
|
||||
return new ClassNotFoundWarning(className);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set up the methodMap mapping
|
||||
*/
|
||||
private void computeMethodMap() throws InvalidClassFileException {
|
||||
|
||||
if (methodMap == null) {
|
||||
ShrikeCTMethod[] methods = computeDeclaredMethods();
|
||||
if (methods.length > 5) {
|
||||
methodMap = HashMapFactory.make(methods.length);
|
||||
} else {
|
||||
methodMap = new SmallMap<Selector, IMethod>();
|
||||
}
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
ShrikeCTMethod m = methods[i];
|
||||
methodMap.put(m.getReference().getSelector(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize the declared methods array
|
||||
*
|
||||
* @throws InvalidClassFileException
|
||||
*/
|
||||
private ShrikeCTMethod[] computeDeclaredMethods() throws InvalidClassFileException {
|
||||
int methodCount = reader.get().getMethodCount();
|
||||
ShrikeCTMethod[] result = new ShrikeCTMethod[methodCount];
|
||||
for (int i = 0; i < methodCount; i++) {
|
||||
ShrikeCTMethod m = new ShrikeCTMethod(this, i);
|
||||
if (DEBUG) {
|
||||
Trace.println("Register method " + m + " for class " + this);
|
||||
}
|
||||
result[i] = m;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getMethod(com.ibm.wala.types.Selector)
|
||||
*/
|
||||
public IMethod getMethod(Selector selector) {
|
||||
if (DEBUG) {
|
||||
Trace.println("getMethod " + selector + " in " + this);
|
||||
}
|
||||
|
||||
if (methodMap == null) {
|
||||
try {
|
||||
computeMethodMap();
|
||||
} catch (InvalidClassFileException e1) {
|
||||
e1.printStackTrace();
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
// my methods + cached parent stuff
|
||||
IMethod result = (IMethod) methodMap.get(selector);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
if (inheritCache != null) {
|
||||
result = inheritCache.get(selector);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// check parent, caching if found
|
||||
try {
|
||||
if (!selector.equals(MethodReference.clinitSelector) && !selector.equals(MethodReference.initSelector)) {
|
||||
ShrikeClass superclass = (ShrikeClass) getSuperclass();
|
||||
if (superclass != null) {
|
||||
IMethod inherit = superclass.getMethod(selector);
|
||||
if (inherit != null) {
|
||||
if (inheritCache == null) {
|
||||
inheritCache = new BimodalMap<Selector, IMethod>(5);
|
||||
}
|
||||
inheritCache.put(selector, inherit);
|
||||
return inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ClassHierarchyException e) {
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
|
||||
// didn't find it yet. special logic for interfaces
|
||||
try {
|
||||
if (isInterface()) {
|
||||
// try each superinterface
|
||||
for (Iterator it = getAllAncestorInterfaces().iterator(); it.hasNext();) {
|
||||
IClass k = (IClass) it.next();
|
||||
result = k.getMethod(selector);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (ClassHierarchyException e) {
|
||||
e.printStackTrace();
|
||||
Assertions.UNREACHABLE("Bad method lookup in " + this);
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
private final HashMap<Atom, IField> fieldMap = new HashMap<Atom, IField>(5);
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getField(com.ibm.wala.util.Atom)
|
||||
*/
|
||||
public IField getField(Atom name) {
|
||||
if (fieldMap.containsKey(name)) {
|
||||
return fieldMap.get(name);
|
||||
} else {
|
||||
IField f = findDeclaredField(name);
|
||||
if (f != null) {
|
||||
fieldMap.put(name, f);
|
||||
return f;
|
||||
} else if (superClass != null) {
|
||||
f = superClass.getField(name);
|
||||
if (f != null) {
|
||||
fieldMap.put(name, f);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IField findDeclaredField(Atom name) {
|
||||
for (int i = 0; i < instanceFields.length; i++) {
|
||||
if (instanceFields[i].getName() == name) {
|
||||
return instanceFields[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < staticFields.length; i++) {
|
||||
if (staticFields[i].getName() == name) {
|
||||
return staticFields[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize the TypeReference field for this instance
|
||||
*
|
||||
* @throws InvalidClassFileException
|
||||
* iff Shrike can't read this class
|
||||
*/
|
||||
private void computeTypeReference() throws InvalidClassFileException {
|
||||
String className = "L" + reader.get().getName();
|
||||
ImmutableByteArray name = ImmutableByteArray.make(className);
|
||||
|
||||
typeReference = TypeReference.findOrCreate(getClassLoader().getReference(), TypeName.findOrCreate(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getReference()
|
||||
*/
|
||||
public TypeReference getReference() {
|
||||
return typeReference;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getSourceFileName()
|
||||
*/
|
||||
public String getSourceFileName() {
|
||||
return loader.getSourceFileName(this);
|
||||
}
|
||||
|
||||
public Collection<IClass> getAllImplementedInterfaces() throws ClassHierarchyException {
|
||||
if (Assertions.verifyAssertions) {
|
||||
if (isInterface()) {
|
||||
Assertions.UNREACHABLE("shouldn't ask for implemented interfaces of " + this);
|
||||
}
|
||||
}
|
||||
if (allInterfaces != null) {
|
||||
return allInterfaces;
|
||||
} else {
|
||||
Collection<IClass> C = computeAllInterfacesAsCollection();
|
||||
allInterfaces = Collections.unmodifiableCollection(C);
|
||||
return allInterfaces;
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<IClass> getAllAncestorInterfaces() throws ClassHierarchyException {
|
||||
if (Assertions.verifyAssertions) {
|
||||
if (!isInterface()) {
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
if (allInterfaces != null) {
|
||||
return allInterfaces;
|
||||
} else {
|
||||
Collection<IClass> C = computeAllInterfacesAsCollection();
|
||||
allInterfaces = Collections.unmodifiableCollection(C);
|
||||
return allInterfaces;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return getReference().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#equals(Object)
|
||||
*/
|
||||
public boolean equals(Object obj) {
|
||||
// it's ok to use instanceof since this class is final
|
||||
// if (this.getClass().equals(obj.getClass())) {
|
||||
if (obj instanceof ShrikeClass) {
|
||||
return getReference().equals(((ShrikeClass) obj).getReference());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method getReader.
|
||||
*/
|
||||
public ClassReader getReader() {
|
||||
try {
|
||||
return reader.get();
|
||||
} catch (InvalidClassFileException e) {
|
||||
e.printStackTrace();
|
||||
Assertions.UNREACHABLE();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see com.ibm.wala.classLoader.IClass#getClassInitializer()
|
||||
*/
|
||||
public IMethod getClassInitializer() {
|
||||
if (methodMap == null) {
|
||||
try {
|
||||
computeMethodMap();
|
||||
} catch (InvalidClassFileException e) {
|
||||
e.printStackTrace();
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
return (IMethod) methodMap.get(MethodReference.clinitSelector);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getDeclaredMethods()
|
||||
*/
|
||||
public Collection<IMethod> getDeclaredMethods() {
|
||||
if (methodMap == null) {
|
||||
try {
|
||||
computeMethodMap();
|
||||
} catch (InvalidClassFileException e) {
|
||||
e.printStackTrace();
|
||||
Assertions.UNREACHABLE();
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableCollection(methodMap.values());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#isArrayClass()
|
||||
*/
|
||||
public boolean isArrayClass() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public ClassHierarchy getClassHierarchy() {
|
||||
return cha;
|
||||
}
|
||||
|
||||
public WarningSet getWarnings() {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getDeclaredFields()
|
||||
*/
|
||||
public Collection<IField> getDeclaredInstanceFields() {
|
||||
return Collections.unmodifiableList(Arrays.asList(instanceFields));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getDeclaredFields()
|
||||
*/
|
||||
public Collection<IField> getDeclaredStaticFields() {
|
||||
return Collections.unmodifiableList(Arrays.asList(staticFields));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getName()
|
||||
*/
|
||||
public TypeName getName() {
|
||||
return getReference().getName();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#isReferenceType()
|
||||
*/
|
||||
public boolean isReferenceType() {
|
||||
return getReference().isReferenceType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all optional cached data associated with this class
|
||||
*/
|
||||
public void clearSoftCaches() {
|
||||
// toss optional information from each method.
|
||||
if (methodMap != null) {
|
||||
for (Iterator it = getDeclaredMethods().iterator(); it.hasNext();) {
|
||||
ShrikeCTMethod m = (ShrikeCTMethod) it.next();
|
||||
m.clearCaches();
|
||||
}
|
||||
}
|
||||
// clear the methodMap cache
|
||||
// SJF: don't do this!!! makes it hard to clear caches on methods.
|
||||
// methodMap = null;
|
||||
inheritCache = null;
|
||||
// clear the cached interfaces
|
||||
allInterfaces = null;
|
||||
// toss away the Shrike reader
|
||||
reader.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getAllInstanceFields()
|
||||
*/
|
||||
public Collection<IField> getAllInstanceFields() throws ClassHierarchyException {
|
||||
Collection<IField> result = new LinkedList<IField>(getDeclaredInstanceFields());
|
||||
IClass s = getSuperclass();
|
||||
while (s != null) {
|
||||
result.addAll(s.getDeclaredInstanceFields());
|
||||
s = s.getSuperclass();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getAllStaticFields()
|
||||
*/
|
||||
public Collection<IField> getAllStaticFields() throws ClassHierarchyException {
|
||||
Collection<IField> result = new LinkedList<IField>(getDeclaredStaticFields());
|
||||
IClass s = getSuperclass();
|
||||
while (s != null) {
|
||||
result.addAll(s.getDeclaredStaticFields());
|
||||
s = s.getSuperclass();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getAllMethods()
|
||||
*/
|
||||
public Collection<IMethod> getAllMethods() throws ClassHierarchyException {
|
||||
Collection<IMethod> result = new LinkedList<IMethod>();
|
||||
Iterator<IMethod> declaredMethods = getDeclaredMethods().iterator();
|
||||
while (declaredMethods.hasNext()) {
|
||||
result.add(declaredMethods.next());
|
||||
}
|
||||
IClass s = getSuperclass();
|
||||
while (s != null) {
|
||||
Iterator<IMethod> superDeclaredMethods = s.getDeclaredMethods().iterator();
|
||||
while (superDeclaredMethods.hasNext()) {
|
||||
result.add(superDeclaredMethods.next());
|
||||
}
|
||||
s = s.getSuperclass();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.wala.classLoader.IClass#getAllFields()
|
||||
*/
|
||||
public Collection<IField> getAllFields() throws ClassHierarchyException {
|
||||
Collection<IField> result = new LinkedList<IField>();
|
||||
result.addAll(getAllInstanceFields());
|
||||
result.addAll(getAllStaticFields());
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isPublic() {
|
||||
boolean result = ((modifiers & Constants.ACC_PUBLIC) != 0);
|
||||
return result;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue