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

com.google.common.flogger.util.CallerFinder Maven / Gradle / Ivy

/*
 * Copyright (C) 2012 The Flogger Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.flogger.util;

import static com.google.common.flogger.util.Checks.checkNotNull;

import com.google.errorprone.annotations.CheckReturnValue;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/** A helper class for determining callers of a specified class currently on the stack. */
@CheckReturnValue
public final class CallerFinder {
  private static final String[] STACK_GETTER_IMPLEMENTATIONS = {
    "com.google.common.flogger.util.StackWalkerStackGetter",
    "com.google.common.flogger.util.JavaLangAccessStackGetter"
  };

  private static final StackGetter STACK_GETTER = getBestStackGetter();

  /**
   * Returns the first available class implementing the {@link StackGetter} methods. The
   * implementation returned is dependent on the current java version.
   */
  private static StackGetter getBestStackGetter() {
    for (String stackGetterName : STACK_GETTER_IMPLEMENTATIONS) {
      StackGetter stackGetter = maybeCreateStackGetter(stackGetterName);
      if (stackGetter != null) {
        return stackGetter;
      }
    }
    return new ThrowableStackGetter();
  }

  private static StackGetter maybeCreateStackGetter(String className) {
    try {
      return Class.forName(className)
          .asSubclass(StackGetter.class)
          .getDeclaredConstructor()
          .newInstance();
    } catch (Throwable t) {
      // We may not be able to create one or both of the implementations in some cases, like for
      // exmaple on Android. This is not a problem because we have ThrowableStackGetter as a
      // fallback.
      // Note, this Throwable is replacing ClassNotFoundException, NoSuchMethodException,
      // InstantiationException, IllegalAccessException, InvocationTargetException, LinkageError,
      // because multi-catch is not available with -source 6 and we would need a separate empty
      // catch clause for all of those. Also, we generally don't want this method to throw any kind
      // of Exception.
    }
    return null;
  }

  /**
   * Returns the stack trace element of the immediate caller of the specified class.
   *
   * @param target the target class whose callers we are looking for.
   * @param skip the minimum number of calls known to have occurred between the first call to the
   *     target class and the point at which the specified throwable was created. If in doubt,
   *     specify zero here to avoid accidentally skipping past the caller. This is particularly
   *     important for code which might be used in Android, since you cannot know whether a tool
   *     such as Proguard has merged methods or classes and reduced the number of intermediate stack
   *     frames.
   * @return the stack trace element representing the immediate caller of the specified class, or
   *     null if no caller was found (due to incorrect target, wrong skip count or use of JNI).
   */
  @NullableDecl
  public static StackTraceElement findCallerOf(Class target, int skip) {
    checkNotNull(target, "target");
    if (skip < 0) {
      throw new IllegalArgumentException("skip count cannot be negative: " + skip);
    }
    return STACK_GETTER.callerOf(target, skip + 1);
  }

  /**
   * Returns a synthetic stack trace starting at the immediate caller of the specified target.
   *
   * @param target the class who caller the returned stack trace will start at.
   * @param maxDepth the maximum size of the returned stack (pass -1 for the complete stack).
   * @param skip the minimum number of stack frames to skip before looking for callers.
   * @return a synthetic stack trace starting at the immediate caller of the specified target, or
   *     the empty array if no caller was found (due to incorrect target, wrong skip count or use of
   *     JNI).
   */
  public static StackTraceElement[] getStackForCallerOf(Class target, int maxDepth, int skip) {
    checkNotNull(target, "target");
    if (maxDepth <= 0 && maxDepth != -1) {
      throw new IllegalArgumentException("invalid maximum depth: " + maxDepth);
    }
    if (skip < 0) {
      throw new IllegalArgumentException("skip count cannot be negative: " + skip);
    }
    return STACK_GETTER.getStackForCaller(target, maxDepth, skip + 1);
  }

  private CallerFinder() {}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy