All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibm.wala.classLoader.ShrikeBTMethod Maven / Gradle / Ivy

/*
 * 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.core.util.bytecode.BytecodeStream;
import com.ibm.wala.core.util.shrike.ShrikeUtil;
import com.ibm.wala.core.util.strings.Atom;
import com.ibm.wala.core.util.strings.ImmutableByteArray;
import com.ibm.wala.shrike.shrikeBT.BytecodeConstants;
import com.ibm.wala.shrike.shrikeBT.Constants;
import com.ibm.wala.shrike.shrikeBT.Decoder;
import com.ibm.wala.shrike.shrikeBT.ExceptionHandler;
import com.ibm.wala.shrike.shrikeBT.IArrayLoadInstruction;
import com.ibm.wala.shrike.shrikeBT.IArrayStoreInstruction;
import com.ibm.wala.shrike.shrikeBT.IGetInstruction;
import com.ibm.wala.shrike.shrikeBT.IInstruction;
import com.ibm.wala.shrike.shrikeBT.IInvokeInstruction;
import com.ibm.wala.shrike.shrikeBT.IPutInstruction;
import com.ibm.wala.shrike.shrikeBT.ITypeTestInstruction;
import com.ibm.wala.shrike.shrikeBT.MonitorInstruction;
import com.ibm.wala.shrike.shrikeBT.NewInstruction;
import com.ibm.wala.shrike.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.collections.EmptyIterator;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import java.lang.ref.SoftReference;
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;

/** A wrapper around a Shrike object that represents a method */
public abstract class ShrikeBTMethod implements IMethod, BytecodeConstants {

  /** Some verbose progress output? */
  private static final 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;

    /* BEGIN Custom change: precise positions */

    /** Cached map representing position information for bytecode instruction at given index */
    protected SourcePosition[] positionMap;

    /** Sourcecode positions for method parameters */
    protected SourcePosition[] paramPositionMap;

    /* END Custom change: precise positions */

    /**
     * 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. */
  private SoftReference bcInfo;

  public ShrikeBTMethod(IClass klass) {
    this.declaringClass = klass;
  }

  protected synchronized BytecodeInfo getBCInfo() throws InvalidClassFileException {
    BytecodeInfo result = null;
    if (bcInfo != null) {
      result = bcInfo.get();
    }
    if (result == null) {
      result = computeBCInfo();
      bcInfo = new SoftReference<>(result);
    }
    return result;
  }

  /** Return the program counter (bytecode index) for a particular Shrike instruction index. */
  public int getBytecodeIndex(int instructionIndex) throws InvalidClassFileException {
    return getBCInfo().pcMap[instructionIndex];
  }

  /**
   * Return the Shrike instruction index for a particular valid program counter (bytecode index), or
   * -1 if the Shrike instriction index could not be determined.
   *
   * 

This ShrikeBTMethod must not be native. */ public int getInstructionIndex(int bcIndex) throws InvalidClassFileException { if (isNative()) { throw new UnsupportedOperationException( "getInstructionIndex(int bcIndex) is only supported for non-native bytecode"); } final BytecodeInfo info = getBCInfo(); if (info.decoder.containsSubroutines()) return -1; final int[] pcMap = info.pcMap; assert isSorted(pcMap); int iindex = Arrays.binarySearch(pcMap, bcIndex); if (iindex < 0) return -1; // Unfortunately, pcMap is not always *strictly* sorted: given bcIndex, there may be multiple // adjacent indices // i,j such that pcMap[i] == bcIndex == pcMap[j]. We pick the least such index. while (iindex > 0 && pcMap[iindex - 1] == bcIndex) iindex--; return iindex; } private static boolean isSorted(int[] a) { for (int i = 0; i < a.length - 1; i++) { if (a[i + 1] < a[i]) return false; } return true; } /** Return the number of Shrike instructions for this method. */ public int getNumShrikeInstructions() throws InvalidClassFileException { return getBCInfo().pcMap.length; } public Collection getCallSites() throws InvalidClassFileException { return isNative() || getBCInfo().callSites == null ? Collections.emptySet() : Collections.unmodifiableCollection(Arrays.asList(getBCInfo().callSites)); } public Collection getNewSites() throws InvalidClassFileException { return (isNative() || getBCInfo().newSites == null) ? Collections.emptySet() : Collections.unmodifiableCollection(Arrays.asList(getBCInfo().newSites)); } /** * @return {@link Set}<{@link TypeReference}>, the exceptions that statements in this method * may throw, */ public Collection getImplicitExceptionTypes() throws InvalidClassFileException { if (isNative()) { return Collections.emptySet(); } return (getBCInfo().implicitExceptions == null) ? Collections.emptyList() : Arrays.asList(getBCInfo().implicitExceptions); } /** * Do a cheap pass over the bytecodes to collect some mapping information. Some methods require * this as a pre-req to accessing ShrikeCT information. */ private BytecodeInfo computeBCInfo() throws InvalidClassFileException { BytecodeInfo result = new BytecodeInfo(); result.exceptionTypes = computeDeclaredExceptions(); if (isNative()) { return result; } if (verbose) { methodsParsed += 1; if (methodsParsed % 100 == 0) { System.out.println(methodsParsed + " methods processed..."); } } processBytecodesWithShrikeBT(result); return result; } /** * @return true iff this method has a monitorenter or monitorexit */ public boolean hasMonitorOp() throws InvalidClassFileException { if (isNative()) { return false; } return getBCInfo().hasMonitorOp; } /** * @return Set of FieldReference */ public Iterator getFieldsWritten() throws InvalidClassFileException { if (isNative()) { return EmptyIterator.instance(); } if (getBCInfo().fieldsWritten == null) { return EmptyIterator.instance(); } else { List l = Arrays.asList(getBCInfo().fieldsWritten); return l.iterator(); } } /** * @return Iterator of FieldReference */ public Iterator getFieldsRead() throws InvalidClassFileException { if (isNative()) { return EmptyIterator.instance(); } if (getBCInfo().fieldsRead == null) { return EmptyIterator.instance(); } else { List l = Arrays.asList(getBCInfo().fieldsRead); return l.iterator(); } } /** * @return Iterator of TypeReference */ public Iterator getArraysRead() throws InvalidClassFileException { if (isNative()) { return EmptyIterator.instance(); } return (getBCInfo().arraysRead == null) ? EmptyIterator.instance() : Arrays.asList(getBCInfo().arraysRead).iterator(); } /** * @return Iterator of TypeReference */ public Iterator getArraysWritten() throws InvalidClassFileException { if (isNative()) { return EmptyIterator.instance(); } if (getBCInfo().fieldsRead == null) { return EmptyIterator.instance(); } else { List list = Arrays.asList(getBCInfo().arraysWritten); return list.iterator(); } } /** * @return Iterator of TypeReference */ public Iterator getCastTypes() throws InvalidClassFileException { if (isNative()) { return EmptyIterator.instance(); } return (getBCInfo().castTypes == null) ? EmptyIterator.instance() : Arrays.asList(getBCInfo().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(declaringClass.getClassLoader().getLanguage(), desc); return MethodReference.findOrCreate(declaringClass.getReference(), name, D); } catch (InvalidClassFileException e) { Assertions.UNREACHABLE(); return null; } } @Override public MethodReference getReference() { if (methodReference == null) { methodReference = computeMethodReference(); } return methodReference; } @Override public boolean isClinit() { return getReference().getSelector().equals(MethodReference.clinitSelector); } @Override public boolean isInit() { return getReference().getName().equals(MethodReference.initAtom); } protected abstract int getModifiers(); @Override public boolean isNative() { return ((getModifiers() & Constants.ACC_NATIVE) != 0); } @Override public boolean isAbstract() { return ((getModifiers() & Constants.ACC_ABSTRACT) != 0); } @Override public boolean isPrivate() { return ((getModifiers() & Constants.ACC_PRIVATE) != 0); } @Override public boolean isProtected() { return ((getModifiers() & Constants.ACC_PROTECTED) != 0); } @Override public boolean isPublic() { return ((getModifiers() & Constants.ACC_PUBLIC) != 0); } @Override public boolean isFinal() { return ((getModifiers() & Constants.ACC_FINAL) != 0); } @Override public boolean isBridge() { return ((getModifiers() & Constants.ACC_VOLATILE) != 0); } @Override public boolean isSynchronized() { return ((getModifiers() & Constants.ACC_SYNCHRONIZED) != 0); } @Override public boolean isStatic() { return ((getModifiers() & Constants.ACC_STATIC) != 0); } @Override public boolean isSynthetic() { return ((getModifiers() & Constants.ACC_SYNTHETIC) != 0); } @Override public boolean isAnnotation() { return ((getModifiers() & Constants.ACC_ANNOTATION) != 0); } @Override public boolean isEnum() { return ((getModifiers() & Constants.ACC_ENUM) != 0); } @Override public boolean isModule() { return ((getModifiers() & Constants.ACC_MODULE) != 0); } @Override public boolean isWalaSynthetic() { return false; } @Override 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. */ protected abstract void processDebugInfo(BytecodeInfo bcInfo) throws InvalidClassFileException; private void processBytecodesWithShrikeBT(BytecodeInfo info) throws InvalidClassFileException { info.decoder = makeDecoder(); if (!isAbstract() && info.decoder == null) { throw new InvalidClassFileException( -1, "non-abstract method " + getReference() + " has no bytecodes"); } if (info.decoder == null) { return; } info.pcMap = info.decoder.getInstructionsToBytecodes(); processDebugInfo(info); SimpleVisitor simpleVisitor = new SimpleVisitor(info); BytecodeLanguage lang = (BytecodeLanguage) getDeclaringClass().getClassLoader().getLanguage(); IInstruction[] instructions = info.decoder.getInstructions(); for (int i = 0; i < instructions.length; i++) { simpleVisitor.setInstructionIndex(i); instructions[i].visit(simpleVisitor); if (instructions[i].isPEI()) { Collection t = lang.getImplicitExceptionTypes(instructions[i]); if (t != null) { simpleVisitor.implicitExceptions.addAll(t); } } } // copy the Set results into arrays; will use less // storage copyVisitorSetsToArrays(simpleVisitor, info); } private static void copyVisitorSetsToArrays(SimpleVisitor simpleVisitor, BytecodeInfo info) { info.newSites = new NewSiteReference[simpleVisitor.newSites.size()]; int i = 0; for (NewSiteReference newSiteReference : simpleVisitor.newSites) { info.newSites[i++] = newSiteReference; } info.fieldsRead = new FieldReference[simpleVisitor.fieldsRead.size()]; i = 0; for (FieldReference fieldReference : simpleVisitor.fieldsRead) { info.fieldsRead[i++] = fieldReference; } info.fieldsRead = new FieldReference[simpleVisitor.fieldsRead.size()]; i = 0; for (FieldReference fieldReference : simpleVisitor.fieldsRead) { info.fieldsRead[i++] = fieldReference; } info.fieldsWritten = new FieldReference[simpleVisitor.fieldsWritten.size()]; i = 0; for (FieldReference fieldReference : simpleVisitor.fieldsWritten) { info.fieldsWritten[i++] = fieldReference; } info.callSites = new CallSiteReference[simpleVisitor.callSites.size()]; i = 0; for (CallSiteReference callSiteReference : simpleVisitor.callSites) { info.callSites[i++] = callSiteReference; } info.arraysRead = new TypeReference[simpleVisitor.arraysRead.size()]; i = 0; for (TypeReference typeReference : simpleVisitor.arraysRead) { info.arraysRead[i++] = typeReference; } info.arraysWritten = new TypeReference[simpleVisitor.arraysWritten.size()]; i = 0; for (TypeReference typeReference : simpleVisitor.arraysWritten) { info.arraysWritten[i++] = typeReference; } info.implicitExceptions = new TypeReference[simpleVisitor.implicitExceptions.size()]; i = 0; for (TypeReference typeReference : simpleVisitor.implicitExceptions) { info.implicitExceptions[i++] = typeReference; } info.castTypes = new TypeReference[simpleVisitor.castTypes.size()]; i = 0; for (TypeReference typeReference : simpleVisitor.castTypes) { info.castTypes[i++] = typeReference; } info.hasMonitorOp = simpleVisitor.hasMonitorOp; } @Override public String toString() { return getReference().toString(); } @Override 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; } } @Override public int hashCode() { return 9661 * getReference().hashCode(); } /** * @see com.ibm.wala.classLoader.SyntheticMethod#getMaxLocals() */ public abstract int getMaxLocals(); // TODO: ShrikeBT should have a getMaxStack method on Decoder, I think. public abstract int getMaxStackHeight(); @Override public Atom getName() { return getReference().getName(); } @Override public Descriptor getDescriptor() { return getReference().getDescriptor(); } /** A visitor used to process bytecodes */ private class SimpleVisitor extends IInstruction.Visitor { private final BytecodeInfo info; public SimpleVisitor(BytecodeInfo info) { this.info = info; } // TODO: make a better Set implementation for these. final Set callSites = HashSetFactory.make(5); final Set fieldsWritten = HashSetFactory.make(5); final Set fieldsRead = HashSetFactory.make(5); final Set newSites = HashSetFactory.make(5); final Set arraysRead = HashSetFactory.make(5); final Set arraysWritten = HashSetFactory.make(5); final Set implicitExceptions = HashSetFactory.make(5); final Set castTypes = HashSetFactory.make(5); boolean hasMonitorOp; private int instructionIndex; public void setInstructionIndex(int i) { instructionIndex = i; } public int getProgramCounter() { return info.pcMap[instructionIndex]; } @Override public void visitMonitor(MonitorInstruction instruction) { hasMonitorOp = true; } @Override public void visitNew(NewInstruction instruction) { ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader(); TypeReference t = ShrikeUtil.makeTypeReference(loader, instruction.getType()); newSites.add(NewSiteReference.make(getProgramCounter(), t)); } @Override public void visitGet(IGetInstruction instruction) { ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader(); FieldReference f = FieldReference.findOrCreate( loader, instruction.getClassType(), instruction.getFieldName(), instruction.getFieldType()); fieldsRead.add(f); } @Override public void visitPut(IPutInstruction instruction) { ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader(); FieldReference f = FieldReference.findOrCreate( loader, instruction.getClassType(), instruction.getFieldName(), instruction.getFieldType()); fieldsWritten.add(f); } @Override public void visitInvoke(IInvokeInstruction instruction) { IClassLoader loader = getDeclaringClass().getClassLoader(); MethodReference m = MethodReference.findOrCreate( loader.getLanguage(), loader.getReference(), instruction.getClassType(), instruction.getMethodName(), instruction.getMethodSignature()); int programCounter = getProgramCounter(); CallSiteReference site = CallSiteReference.make(programCounter, m, instruction.getInvocationCode()); callSites.add(site); } @Override public void visitArrayLoad(IArrayLoadInstruction instruction) { arraysRead.add( ShrikeUtil.makeTypeReference( getDeclaringClass().getClassLoader().getReference(), instruction.getType())); } @Override public void visitArrayStore(IArrayStoreInstruction instruction) { arraysWritten.add( ShrikeUtil.makeTypeReference( getDeclaringClass().getClassLoader().getReference(), instruction.getType())); } @Override public void visitCheckCast(ITypeTestInstruction instruction) { for (String t : instruction.getTypes()) { castTypes.add( ShrikeUtil.makeTypeReference(getDeclaringClass().getClassLoader().getReference(), t)); } } } /** */ public IInstruction[] getInstructions() throws InvalidClassFileException { if (getBCInfo().decoder == null) { return null; } else { return getBCInfo().decoder.getInstructions(); } } public ExceptionHandler[][] getHandlers() throws InvalidClassFileException { if (getBCInfo().decoder == null) { return null; } else { return getBCInfo().decoder.getHandlers(); } } /** By convention, for a non-static method, getParameterType(0) is the this pointer */ @Override 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 */ @Override public int getNumberOfParameters() { if (isStatic() || isClinit()) { return getReference().getNumberOfParameters(); } else { return getReference().getNumberOfParameters() + 1; } } @Override public abstract boolean hasExceptionHandler(); /** Clients should not modify the returned array. TODO: clone to avoid the problem? */ @Override public TypeReference[] getDeclaredExceptions() throws InvalidClassFileException { return (getBCInfo().exceptionTypes == null) ? new TypeReference[0] : getBCInfo().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]; Arrays.setAll( result, i -> TypeReference.findOrCreate( loader, TypeName.findOrCreate(ImmutableByteArray.make('L' + strings[i])))); return result; } catch (InvalidClassFileException e) { Assertions.UNREACHABLE(); return null; } } /* BEGIN Custom change: precise bytecode positions */ @Override public SourcePosition getSourcePosition(int bcIndex) throws InvalidClassFileException { return (getBCInfo().positionMap == null) ? null : getBCInfo().positionMap[bcIndex]; } @Override public SourcePosition getParameterSourcePosition(int paramNum) throws InvalidClassFileException { return (getBCInfo().paramPositionMap == null) ? null : getBCInfo().paramPositionMap[paramNum]; } /* END Custom change: precise bytecode positions */ @Override public int getLineNumber(int bcIndex) { try { return (getBCInfo().lineNumberMap == null) ? -1 : getBCInfo().lineNumberMap[bcIndex]; } catch (InvalidClassFileException e) { return -1; } } /** * @return {@link Set}<{@link TypeReference}> */ public Set getCaughtExceptionTypes() throws InvalidClassFileException { ExceptionHandler[][] handlers = getHandlers(); if (handlers == null) { return Collections.emptySet(); } HashSet result = HashSetFactory.make(10); ClassLoaderReference loader = getReference().getDeclaringClass().getClassLoader(); for (ExceptionHandler[] handler : handlers) { for (ExceptionHandler element : handler) { TypeReference t = ShrikeUtil.makeTypeReference(loader, element.getCatchClass()); if (t == null) { t = TypeReference.JavaLangThrowable; } result.add(t); } } return result; } @Override public String getSignature() { return getReference().getSignature(); } @Override public Selector getSelector() { return getReference().getSelector(); } @Override public abstract String getLocalVariableName(int bcIndex, int localNumber); /* * TODO: cache for efficiency? * * @see com.ibm.wala.classLoader.IMethod#hasLocalVariableTable() */ @Override public abstract boolean hasLocalVariableTable(); /** Clear all optional cached data associated with this class. */ public void clearCaches() { bcInfo = null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy