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

soot.SootMethod Maven / Gradle / Ivy

package soot;

/*-
 * #%L
 * Soot - a J*va Optimization Framework
 * %%
 * Copyright (C) 1997 - 1999 Raja Vallee-Rai
 * Copyright (C) 2004 Ondrej Lhotak
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 2.1 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import soot.dava.DavaBody;
import soot.dava.toolkits.base.renamer.RemoveFullyQualifiedName;
import soot.jimple.toolkits.callgraph.VirtualCalls;
import soot.options.Options;
import soot.tagkit.AbstractHost;
import soot.util.IterableSet;
import soot.util.Numberable;
import soot.util.NumberedString;

/**
 * Soot representation of a Java method. Can be declared to belong to a {@link SootClass}. Does not contain the actual code,
 * which belongs to a {@link Body}. The {@link #getActiveBody()} method points to the currently-active body.
 */
public class SootMethod extends AbstractHost implements ClassMember, Numberable, MethodOrMethodContext, SootMethodInterface {

  private static final Logger logger = LoggerFactory.getLogger(SootMethod.class);

  public static final String constructorName = "";
  public static final String staticInitializerName = "";

  /** Name of the current method. */
  protected String name;

  /**
   * An array of parameter types taken by this {@link SootMethod} object, in declaration order.
   */
  protected Type[] parameterTypes;

  /** The return type of this object. */
  protected Type returnType;

  /**
   * True when some {@link SootClass} object declares this {@link SootMethod} object.
   */
  protected boolean isDeclared;

  /** Holds the class which declares this SootClass method. */
  protected SootClass declaringClass;

  /**
   * Modifiers associated with this {@link SootMethod} (e.g. private, protected, etc.)
   */
  protected int modifiers;

  /** Is this method a phantom method? */
  protected boolean isPhantom = false;

  /** Declared exceptions thrown by this method. Created upon demand. */
  protected List exceptions = null;

  /** Active body associated with this method. */
  protected volatile Body activeBody;

  /** Tells this method how to find out where its body lives. */
  protected volatile MethodSource ms;

  protected volatile String sig;
  protected volatile String subSig;

  /**
   * Constructs a {@link SootMethod} with the given name, parameter types and return type.
   */
  public SootMethod(String name, List parameterTypes, Type returnType) {
    this(name, parameterTypes, returnType, 0, Collections.emptyList());
  }

  /**
   * Constructs a {@link SootMethod} with the given name, parameter types, return type and modifiers.
   */
  public SootMethod(String name, List parameterTypes, Type returnType, int modifiers) {
    this(name, parameterTypes, returnType, modifiers, Collections.emptyList());
  }

  /**
   * Constructs a {@link SootMethod} with the given name, parameter types, return type, and list of thrown exceptions.
   */
  public SootMethod(String name, List parameterTypes, Type returnType, int modifiers,
      List thrownExceptions) {
    this.name = name;

    if (parameterTypes != null && !parameterTypes.isEmpty()) {
      this.parameterTypes = parameterTypes.toArray(new Type[parameterTypes.size()]);
    }

    this.returnType = returnType;
    this.modifiers = modifiers;

    if (exceptions == null && !thrownExceptions.isEmpty()) {
      exceptions = new ArrayList();
      this.exceptions.addAll(thrownExceptions);
    }
    subsignature = Scene.v().getSubSigNumberer().findOrAdd(getSubSignature());
  }

  /**
   * Returns a hash code for this method consistent with structural equality.
   */
  public int equivHashCode() {
    return returnType.hashCode() * 101 + modifiers * 17 + name.hashCode();
  }

  /** Returns the name of this method. */
  public String getName() {
    return name;
  }

  /** Sets the name of this method. */
  public synchronized void setName(String name) {
    boolean wasDeclared = isDeclared;
    SootClass oldDeclaringClass = declaringClass;
    if (wasDeclared) {
      oldDeclaringClass.removeMethod(this);
    }
    this.name = name;
    subSig = null;
    sig = null;
    subsignature = Scene.v().getSubSigNumberer().findOrAdd(getSubSignature());
    if (wasDeclared) {
      oldDeclaringClass.addMethod(this);
    }
  }

  /** Sets the declaring class */
  public synchronized void setDeclaringClass(SootClass declClass) {
    // There is nothing to stop this field from being null except when it actually gets in
    // other classes such as SootMethodRef (when it tries to resolve the method). However, if
    // the method is not declared, it should not be trying to resolve it anyways. So I see no
    // problem with having it able to be null.
    if (declClass != null) {
      Scene.v().getMethodNumberer().add(this);
    }
    // We could call setDeclared here, however, when SootClass adds a method, it checks isDeclared
    // and throws an exception if set. So we currently cannot call setDeclared here.
    declaringClass = declClass;
    sig = null;
  }

  /** Returns the class which declares the current {@link SootMethod}. */
  @Override
  public SootClass getDeclaringClass() {
    if (!isDeclared) {
      throw new RuntimeException("not declared: " + getName());
    }

    return declaringClass;
  }

  public void setDeclared(boolean isDeclared) {
    this.isDeclared = isDeclared;
  }

  /**
   * Returns true when some {@link SootClass} object declares this {@link SootMethod} object.
   */
  @Override
  public boolean isDeclared() {
    return isDeclared;
  }

  /** Returns true when this {@link SootMethod} object is phantom. */
  @Override
  public boolean isPhantom() {
    return isPhantom;
  }

  /** Returns true if this method is not phantom, abstract or native, i.e. this method can have a body. */
  public boolean isConcrete() {
    return !isPhantom() && !isAbstract() && !isNative();
  }

  /** Sets the phantom flag on this method. */
  @Override
  public void setPhantom(boolean value) {
    if (value) {
      if (!Scene.v().allowsPhantomRefs()) {
        throw new RuntimeException("Phantom refs not allowed");
      }
      if (!Options.v().allow_phantom_elms() && declaringClass != null && !declaringClass.isPhantom()) {
        throw new RuntimeException("Declaring class would have to be phantom");
      }
    }
    isPhantom = value;
  }

  /**
   * Gets the modifiers of this method.
   *
   * @see soot.Modifier
   */
  @Override
  public int getModifiers() {
    return modifiers;
  }

  /**
   * Sets the modifiers of this method.
   *
   * @see soot.Modifier
   */
  @Override
  public void setModifiers(int modifiers) {
    this.modifiers = modifiers;
  }

  /** Returns the return type of this method. */
  public Type getReturnType() {
    return returnType;
  }

  /** Sets the return type of this method. */
  public synchronized void setReturnType(Type t) {
    boolean wasDeclared = isDeclared;
    SootClass oldDeclaringClass = declaringClass;
    if (wasDeclared) {
      oldDeclaringClass.removeMethod(this);
    }
    returnType = t;
    subSig = null;
    sig = null;
    subsignature = Scene.v().getSubSigNumberer().findOrAdd(getSubSignature());
    if (wasDeclared) {
      oldDeclaringClass.addMethod(this);
    }
  }

  /** Returns the number of parameters taken by this method. */
  public int getParameterCount() {
    return parameterTypes == null ? 0 : parameterTypes.length;
  }

  /** Gets the type of the nth parameter of this method. */
  public Type getParameterType(int n) {
    return parameterTypes[n];
  }

  /**
   * Returns a read-only list of the parameter types of this method.
   */
  public List getParameterTypes() {
    return parameterTypes == null ? Collections.emptyList() : Arrays.asList(parameterTypes);
  }

  /**
   * Changes the set of parameter types of this method.
   */
  public synchronized void setParameterTypes(List l) {
    boolean wasDeclared = isDeclared;
    SootClass oldDeclaringClass = declaringClass;
    if (wasDeclared) {
      oldDeclaringClass.removeMethod(this);
    }
    this.parameterTypes = l.toArray(new Type[l.size()]);
    subSig = null;
    sig = null;
    subsignature = Scene.v().getSubSigNumberer().findOrAdd(getSubSignature());
    if (wasDeclared) {
      oldDeclaringClass.addMethod(this);
    }
  }

  /** Returns the {@link MethodSource} of the current {@link SootMethod}. */
  public MethodSource getSource() {
    return ms;
  }

  /** Sets the {@link MethodSource} of the current {@link SootMethod}. */
  public synchronized void setSource(MethodSource ms) {
    this.ms = ms;
  }

  /**
   * Retrieves the active body for this method.
   */
  @SuppressWarnings("deprecation")
  public Body getActiveBody() {
    // Retrieve the active body so thread changes do not affect the
    // synchronization between if the body exists and the returned body.
    // This is a quick check just in case the activeBody exists.
    Body activeBody = this.activeBody;
    if (activeBody != null) {
      return activeBody;
    }

    // Synchronize because we are operating on two fields that may be updated
    // separately otherwise.
    synchronized (this) {
      // Re-check the activeBody because things might have changed
      activeBody = this.activeBody;
      if (activeBody != null) {
        return activeBody;
      }

      if (declaringClass != null) {
        declaringClass.checkLevel(SootClass.BODIES);
      }
      if ((declaringClass != null && declaringClass.isPhantomClass()) || isPhantom()) {
        throw new RuntimeException("cannot get active body for phantom method: " + getSignature());
      }

      // ignore empty body exceptions if we are just computing coffi metrics
      if (!soot.jbco.Main.metrics) {
        throw new RuntimeException("no active body present for method " + getSignature());
      }
      return null;
    }
  }

  /**
   * Sets the active body for this method.
   */
  public synchronized void setActiveBody(Body body) {
    if ((declaringClass != null) && declaringClass.isPhantomClass()) {
      throw new RuntimeException("cannot set active body for phantom class! " + this);
    }

    // If someone sets a body for a phantom method, this method then is no
    // longer phantom
    setPhantom(false);

    if (!isConcrete()) {
      throw new RuntimeException("cannot set body for non-concrete method! " + this);
    }

    if (body != null && body.getMethod() != this) {
      body.setMethod(this);
    }

    this.activeBody = body;
  }

  /**
   * Returns the active body if present, else constructs an active body and returns that.
   *
   * If you called Scene.v().loadClassAndSupport() for a class yourself, it will not be an application class, so you cannot
   * get retrieve its active body. Please call {@link SootClass#setApplicationClass()} on the relevant class.
   */
  public Body retrieveActiveBody() {
    // Retrieve the active body so thread changes do not affect the
    // synchronization between if the body exists and the returned body.
    // This is a quick check just in case the activeBody exists.
    Body activeBody = this.activeBody;
    if (activeBody != null) {
      return activeBody;
    }

    // Synchronize because we are operating on multiple fields that may be updated
    // separately otherwise.
    synchronized (this) {
      // Re-check the activeBody because things might have changed
      activeBody = this.activeBody;
      if (activeBody != null) {
        return activeBody;
      }

      if (declaringClass != null) {
        declaringClass.checkLevel(SootClass.BODIES);
      }
      if ((declaringClass != null && declaringClass.isPhantomClass()) || isPhantom()) {
        throw new RuntimeException("cannot get resident body for phantom method : " + this);
      }

      if (ms == null) {
        throw new RuntimeException("No method source set for method " + this);
      }

      // Method sources are not expected to be thread safe
      activeBody = ms.getBody(this, "jb");
      setActiveBody(activeBody);

      // If configured, we drop the method source to save memory
      if (Options.v().drop_bodies_after_load()) {
        ms = null;
      }
      return activeBody;
    }
  }

  /** Returns true if this method has an active body. */
  public boolean hasActiveBody() {
    return activeBody != null;
  }

  /** Releases the active body associated with this method. */
  public synchronized void releaseActiveBody() {
    activeBody = null;
  }

  /**
   * Adds the given exception to the list of exceptions thrown by this method unless the exception is already in the list.
   */
  public void addExceptionIfAbsent(SootClass e) {
    if (!throwsException(e)) {
      addException(e);
    }
  }

  /**
   * Adds the given exception to the list of exceptions thrown by this method.
   */
  public void addException(SootClass e) {
    logger.trace("Adding exception {}", e);

    if (exceptions == null) {
      exceptions = new ArrayList();
    } else if (exceptions.contains(e)) {
      throw new RuntimeException("already throws exception " + e.getName());
    }

    exceptions.add(e);
  }

  /**
   * Removes the given exception from the list of exceptions thrown by this method.
   */
  public void removeException(SootClass e) {
    logger.trace("Removing exception {}", e);

    if (exceptions == null) {
      throw new RuntimeException("does not throw exception " + e.getName());
    }

    if (!exceptions.contains(e)) {
      throw new RuntimeException("does not throw exception " + e.getName());
    }

    exceptions.remove(e);
  }

  /** Returns true if this method throws exception e. */
  public boolean throwsException(SootClass e) {
    return exceptions != null && exceptions.contains(e);
  }

  public void setExceptions(List exceptions) {
    if (exceptions != null && !exceptions.isEmpty()) {
      this.exceptions = new ArrayList(exceptions);
    } else {
      this.exceptions = null;
    }
  }

  /**
   * Returns a backed list of the exceptions thrown by this method.
   */

  public List getExceptions() {
    if (exceptions == null) {
      exceptions = new ArrayList();
    }

    return exceptions;
  }

  public List getExceptionsUnsafe() {
    return exceptions;
  }

  /**
   * Convenience method returning true if this method is static.
   */
  @Override
  public boolean isStatic() {
    return Modifier.isStatic(this.getModifiers());
  }

  /**
   * Convenience method returning true if this method is private.
   */
  @Override
  public boolean isPrivate() {
    return Modifier.isPrivate(this.getModifiers());
  }

  /**
   * Convenience method returning true if this method is public.
   */
  @Override
  public boolean isPublic() {
    return Modifier.isPublic(this.getModifiers());
  }

  /**
   * Convenience method returning true if this method is protected.
   */
  @Override
  public boolean isProtected() {
    return Modifier.isProtected(this.getModifiers());
  }

  /**
   * Convenience method returning true if this method is abstract.
   */
  public boolean isAbstract() {
    return Modifier.isAbstract(this.getModifiers());
  }

  /**
   * Convenience method returning true if this method is final.
   */
  public boolean isFinal() {
    return Modifier.isFinal(this.getModifiers());
  }

  /**
   * Convenience method returning true if this method is native.
   */
  public boolean isNative() {
    return Modifier.isNative(this.getModifiers());
  }

  /**
   * Convenience method returning true if this method is synchronized.
   */
  public boolean isSynchronized() {
    return Modifier.isSynchronized(this.getModifiers());
  }

  /**
   *
   * @return yes if this is the main method
   */
  public boolean isMain() {
    if (isPublic() && isStatic()) {
      NumberedString main_sig = Scene.v().getSubSigNumberer().findOrAdd("void main(java.lang.String[])");
      if (main_sig.equals(subsignature)) {
        return true;
      }
    }

    return false;
  }

  /**
   *
   * @return yes, if this function is a constructor. Please not that  methods are not treated as constructors in this
   *         method.
   */
  public boolean isConstructor() {
    return name.equals(constructorName);
  }

  /**
   *
   * @return yes, if this function is a static initializer.
   */
  public boolean isStaticInitializer() {
    return name.equals(staticInitializerName);
  }

  /**
   * @return yes, if this is a class initializer or main function.
   */
  public boolean isEntryMethod() {
    if (isStatic() && subsignature.equals(VirtualCalls.v().sigClinit)) {
      return true;
    }

    return isMain();
  }

  /**
   * We rely on the JDK class recognition to decide if a method is JDK method.
   */
  public boolean isJavaLibraryMethod() {
    SootClass cl = getDeclaringClass();
    return cl.isJavaLibraryClass();
  }

  /**
   * Returns the parameters part of the signature in the format in which it appears in bytecode.
   */
  public String getBytecodeParms() {
    StringBuffer buffer = new StringBuffer();
    for (Iterator typeIt = getParameterTypes().iterator(); typeIt.hasNext();) {
      final Type type = typeIt.next();
      buffer.append(AbstractJasminClass.jasminDescriptorOf(type));
    }
    return buffer.toString().intern();
  }

  /**
   * Returns the signature of this method in the format in which it appears in bytecode (eg. [Ljava/lang/Object instead of
   * java.lang.Object[]).
   */
  public String getBytecodeSignature() {
    String name = getName();

    StringBuffer buffer = new StringBuffer();
    buffer.append("<" + Scene.v().quotedNameOf(getDeclaringClass().getName()) + ": ");
    buffer.append(name);
    buffer.append(AbstractJasminClass.jasminDescriptorOf(makeRef()));
    buffer.append(">");

    return buffer.toString().intern();
  }

  /**
   * Returns the Soot signature of this method. Used to refer to methods unambiguously.
   */
  public String getSignature() {
    if (sig == null) {
      synchronized (this) {
        if (sig == null) {
          sig = getSignature(getDeclaringClass(), getSubSignature());
        }
      }
    }
    return sig;
  }

  public static String getSignature(SootClass cl, String name, List params, Type returnType) {
    return getSignature(cl, getSubSignatureImpl(name, params, returnType));
  }

  public static String getSignature(SootClass cl, String subSignature) {
    StringBuilder buffer = new StringBuilder();
    buffer.append("<");
    buffer.append(Scene.v().quotedNameOf(cl.getName()));
    buffer.append(": ");
    buffer.append(subSignature);
    buffer.append(">");

    return buffer.toString();
  }

  /**
   * Returns the Soot subsignature of this method. Used to refer to methods unambiguously.
   */
  public String getSubSignature() {
    if (subSig == null) {
      synchronized (this) {
        if (subSig == null) {
          subSig = getSubSignatureImpl(getName(), getParameterTypes(), getReturnType());
        }
      }
    }
    return subSig;
  }

  public static String getSubSignature(String name, List params, Type returnType) {
    return getSubSignatureImpl(name, params, returnType);
  }

  private static String getSubSignatureImpl(String name, List params, Type returnType) {
    StringBuilder buffer = new StringBuilder();

    buffer.append(returnType.toQuotedString());

    buffer.append(" ");
    buffer.append(Scene.v().quotedNameOf(name));
    buffer.append("(");

    if (params != null) {
      for (int i = 0; i < params.size(); i++) {
        buffer.append(params.get(i).toQuotedString());
        if (i < params.size() - 1) {
          buffer.append(",");
        }
      }
    }
    buffer.append(")");

    return buffer.toString();
  }

  protected NumberedString subsignature;

  public NumberedString getNumberedSubSignature() {
    return subsignature;
  }

  /** Returns the signature of this method. */
  @Override
  public String toString() {
    return getSignature();
  }

  /*
   * TODO: Nomair A. Naeem .... 8th Feb 2006 This is really messy coding So much for modularization!! Should some day look
   * into creating the DavaDeclaration from within DavaBody
   */
  public String getDavaDeclaration() {
    if (getName().equals(staticInitializerName)) {
      return "static";
    }

    StringBuffer buffer = new StringBuffer();

    // modifiers
    StringTokenizer st = new StringTokenizer(Modifier.toString(this.getModifiers()));
    if (st.hasMoreTokens()) {
      buffer.append(st.nextToken());
    }

    while (st.hasMoreTokens()) {
      buffer.append(" " + st.nextToken());
    }

    if (buffer.length() != 0) {
      buffer.append(" ");
    }

    // return type + name

    if (getName().equals(constructorName)) {
      buffer.append(getDeclaringClass().getShortJavaStyleName());
    } else {
      Type t = this.getReturnType();

      String tempString = t.toString();

      /*
       * Added code to handle RuntimeExcepotion thrown by getActiveBody
       */
      if (hasActiveBody()) {
        DavaBody body = (DavaBody) getActiveBody();
        IterableSet importSet = body.getImportList();

        if (!importSet.contains(tempString)) {
          body.addToImportList(tempString);
        }
        tempString = RemoveFullyQualifiedName.getReducedName(importSet, tempString, t);
      }

      buffer.append(tempString + " ");

      buffer.append(Scene.v().quotedNameOf(this.getName()));
    }

    buffer.append("(");

    // parameters
    Iterator typeIt = this.getParameterTypes().iterator();
    int count = 0;
    while (typeIt.hasNext()) {
      Type t = typeIt.next();
      String tempString = t.toString();

      /*
       * Nomair A. Naeem 7th Feb 2006 It is nice to remove the fully qualified type names of parameters if the package they
       * belong to have been imported javax.swing.ImageIcon should be just ImageIcon if javax.swing is imported If not
       * imported WHY NOT..import it!!
       */
      if (hasActiveBody()) {
        DavaBody body = (DavaBody) getActiveBody();
        IterableSet importSet = body.getImportList();

        if (!importSet.contains(tempString)) {
          body.addToImportList(tempString);
        }
        tempString = RemoveFullyQualifiedName.getReducedName(importSet, tempString, t);
      }

      buffer.append(tempString + " ");

      buffer.append(" ");
      if (hasActiveBody()) {
        buffer.append(((DavaBody) getActiveBody()).get_ParamMap().get(new Integer(count++)));
      } else {
        if (t == BooleanType.v()) {
          buffer.append("z" + count++);
        } else if (t == ByteType.v()) {
          buffer.append("b" + count++);
        } else if (t == ShortType.v()) {
          buffer.append("s" + count++);
        } else if (t == CharType.v()) {
          buffer.append("c" + count++);
        } else if (t == IntType.v()) {
          buffer.append("i" + count++);
        } else if (t == LongType.v()) {
          buffer.append("l" + count++);
        } else if (t == DoubleType.v()) {
          buffer.append("d" + count++);
        } else if (t == FloatType.v()) {
          buffer.append("f" + count++);
        } else if (t == StmtAddressType.v()) {
          buffer.append("a" + count++);
        } else if (t == ErroneousType.v()) {
          buffer.append("e" + count++);
        } else if (t == NullType.v()) {
          buffer.append("n" + count++);
        } else {
          buffer.append("r" + count++);
        }
      }

      if (typeIt.hasNext()) {
        buffer.append(", ");
      }

    }

    buffer.append(")");

    // Print exceptions
    if (exceptions != null) {
      Iterator exceptionIt = this.getExceptions().iterator();

      if (exceptionIt.hasNext()) {
        buffer.append(" throws " + exceptionIt.next().getName());

        while (exceptionIt.hasNext()) {
          buffer.append(", " + exceptionIt.next().getName());
        }
      }
    }

    return buffer.toString().intern();
  }

  /**
   * Returns the declaration of this method, as used at the top of textual body representations (before the {}'s containing
   * the code for representation.)
   */
  public String getDeclaration() {
    StringBuffer buffer = new StringBuffer();

    // modifiers
    StringTokenizer st = new StringTokenizer(Modifier.toString(this.getModifiers()));
    if (st.hasMoreTokens()) {
      buffer.append(st.nextToken());
    }

    while (st.hasMoreTokens()) {
      buffer.append(" " + st.nextToken());
    }

    if (buffer.length() != 0) {
      buffer.append(" ");
    }

    // return type + name

    buffer.append(this.getReturnType().toQuotedString() + " ");
    buffer.append(Scene.v().quotedNameOf(this.getName()));

    buffer.append("(");

    // parameters
    Iterator typeIt = this.getParameterTypes().iterator();
    // int count = 0;
    while (typeIt.hasNext()) {
      Type t = typeIt.next();

      buffer.append(t.toQuotedString());

      if (typeIt.hasNext()) {
        buffer.append(", ");
      }

    }

    buffer.append(")");

    // Print exceptions
    if (exceptions != null) {
      Iterator exceptionIt = this.getExceptions().iterator();

      if (exceptionIt.hasNext()) {
        buffer.append(" throws " + Scene.v().quotedNameOf(exceptionIt.next().getName()));

        while (exceptionIt.hasNext()) {
          buffer.append(", " + Scene.v().quotedNameOf(exceptionIt.next().getName()));
        }
      }
    }

    return buffer.toString().intern();
  }

  @Override
  public final int getNumber() {
    return number;
  }

  @Override
  public final void setNumber(int number) {
    this.number = number;
  }

  protected int number = 0;

  @Override
  public SootMethod method() {
    return this;
  }

  @Override
  public Context context() {
    return null;
  }

  public SootMethodRef makeRef() {
    return Scene.v().makeMethodRef(declaringClass, name, parameterTypes == null ? null : Arrays.asList(parameterTypes),
        returnType, isStatic());
  }

  @Override
  public int getJavaSourceStartLineNumber() {
    super.getJavaSourceStartLineNumber();
    // search statements for first line number
    if (line == -1 && hasActiveBody()) {
      PatchingChain unit = getActiveBody().getUnits();
      for (Unit u : unit) {
        int l = u.getJavaSourceStartLineNumber();
        if (l > -1) {
          // store l-1, as method header is usually one line before
          // 1st statement
          line = l - 1;
          break;
        }
      }
    }
    return line;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy