aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java')
-rw-r--r--third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java381
1 files changed, 381 insertions, 0 deletions
diff --git a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java
new file mode 100644
index 0000000000..45d959fe02
--- /dev/null
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java
@@ -0,0 +1,381 @@
+package org.checkerframework.javacutil;
+
+import static com.sun.tools.javac.code.Kinds.PCK;
+import static com.sun.tools.javac.code.Kinds.TYP;
+import static com.sun.tools.javac.code.Kinds.VAR;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.JavacScope;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.PackageSymbol;
+import com.sun.tools.javac.code.Symbol.TypeSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.DeferredAttr;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.comp.Resolve;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Names;
+
+/**
+ * A Utility class to find symbols corresponding to string references.
+ */
+public class Resolver {
+ private final Resolve resolve;
+ private final Names names;
+ private final Trees trees;
+ private final Log log;
+
+ private static final Method FIND_METHOD;
+ private static final Method FIND_VAR;
+ private static final Method FIND_IDENT;
+ private static final Method FIND_IDENT_IN_TYPE;
+ private static final Method FIND_IDENT_IN_PACKAGE;
+ private static final Method FIND_TYPE;
+
+ private static final Class<?> ACCESSERROR;
+ // Note that currently access(...) is defined in InvalidSymbolError, a superclass of AccessError
+ private static final Method ACCESSERROR_ACCESS;
+
+ static {
+ try {
+ FIND_METHOD = Resolve.class.getDeclaredMethod("findMethod",
+ Env.class, Type.class, Name.class, List.class, List.class,
+ boolean.class, boolean.class, boolean.class);
+ FIND_METHOD.setAccessible(true);
+
+ FIND_VAR = Resolve.class.getDeclaredMethod("findVar",
+ Env.class, Name.class);
+ FIND_VAR.setAccessible(true);
+
+ FIND_IDENT = Resolve.class.getDeclaredMethod(
+ "findIdent", Env.class, Name.class, int.class);
+ FIND_IDENT.setAccessible(true);
+
+ FIND_IDENT_IN_TYPE = Resolve.class.getDeclaredMethod(
+ "findIdentInType", Env.class, Type.class, Name.class,
+ int.class);
+ FIND_IDENT_IN_TYPE.setAccessible(true);
+
+ FIND_IDENT_IN_PACKAGE = Resolve.class.getDeclaredMethod(
+ "findIdentInPackage", Env.class, TypeSymbol.class, Name.class,
+ int.class);
+ FIND_IDENT_IN_PACKAGE.setAccessible(true);
+
+ FIND_TYPE = Resolve.class.getDeclaredMethod(
+ "findType", Env.class, Name.class);
+ FIND_TYPE.setAccessible(true);
+ } catch (Exception e) {
+ Error err = new AssertionError(
+ "Compiler 'Resolve' class doesn't contain required 'find' method");
+ err.initCause(e);
+ throw err;
+ }
+
+ try {
+ ACCESSERROR = Class.forName("com.sun.tools.javac.comp.Resolve$AccessError");
+ ACCESSERROR_ACCESS = ACCESSERROR.getMethod("access", Name.class, TypeSymbol.class);
+ ACCESSERROR_ACCESS.setAccessible(true);
+ } catch (ClassNotFoundException e) {
+ ErrorReporter.errorAbort("Compiler 'Resolve$AccessError' class could not be retrieved.", e);
+ // Unreachable code - needed so the compiler does not warn about a possibly uninitialized final field.
+ throw new AssertionError();
+ } catch (NoSuchMethodException e) {
+ ErrorReporter.errorAbort("Compiler 'Resolve$AccessError' class doesn't contain required 'access' method", e);
+ // Unreachable code - needed so the compiler does not warn about a possibly uninitialized final field.
+ throw new AssertionError();
+ }
+ }
+
+ public Resolver(ProcessingEnvironment env) {
+ Context context = ((JavacProcessingEnvironment) env).getContext();
+ this.resolve = Resolve.instance(context);
+ this.names = Names.instance(context);
+ this.trees = Trees.instance(env);
+ this.log = Log.instance(context);
+ }
+
+ /**
+ * Finds the package with name {@code name}.
+ *
+ * @param name
+ * The name of the package.
+ * @param path
+ * The tree path to the local scope.
+ * @return the {@code PackageSymbol} for the package if it is found,
+ * {@code null} otherwise
+ */
+ public PackageSymbol findPackage(String name, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+ Element res = wrapInvocationOnResolveInstance(FIND_IDENT, env,
+ names.fromString(name), PCK);
+ // findIdent will return a PackageSymbol even for a symbol that is not a package,
+ // such as a.b.c.MyClass.myStaticField. "exists()" must be called on it to ensure
+ // that it exists.
+ if (res.getKind() == ElementKind.PACKAGE) {
+ PackageSymbol ps = (PackageSymbol) res;
+ return ps.exists() ? ps : null;
+ } else {
+ return null;
+ }
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Finds the field with name {@code name} in a given type.
+ *
+ * <p>
+ * The method adheres to all the rules of Java's scoping (while also
+ * considering the imports) for name resolution.
+ *
+ * @param name
+ * The name of the field.
+ * @param type
+ * The type of the receiver (i.e., the type in which to look for
+ * the field).
+ * @param path
+ * The tree path to the local scope.
+ * @return the element for the field
+ */
+ public VariableElement findField(String name, TypeMirror type, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+ Element res = wrapInvocationOnResolveInstance(FIND_IDENT_IN_TYPE, env, type,
+ names.fromString(name), VAR);
+ if (res.getKind() == ElementKind.FIELD) {
+ return (VariableElement) res;
+ } else if (res.getKind() == ElementKind.OTHER && ACCESSERROR.isInstance(res)) {
+ // Return the inaccessible field that was found
+ return (VariableElement) wrapInvocation(res, ACCESSERROR_ACCESS, null, null);
+ } else {
+ // Most likely didn't find the field and the Element is a SymbolNotFoundError
+ return null;
+ }
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Finds the local variable with name {@code name} in the given scope.
+ *
+ * @param name
+ * The name of the local variable.
+ * @param path
+ * The tree path to the local scope.
+ * @return the element for the local variable
+ */
+ public VariableElement findLocalVariableOrParameter(String name, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+ Element res = wrapInvocationOnResolveInstance(FIND_VAR, env,
+ names.fromString(name));
+ if (res.getKind() == ElementKind.LOCAL_VARIABLE
+ || res.getKind() == ElementKind.PARAMETER) {
+ return (VariableElement) res;
+ } else {
+ // Most likely didn't find the variable and the Element is a SymbolNotFoundError
+ return null;
+ }
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Finds the class literal with name {@code name}.
+ *
+ * <p>
+ * The method adheres to all the rules of Java's scoping (while also
+ * considering the imports) for name resolution.
+ *
+ * @param name
+ * The name of the class.
+ * @param path
+ * The tree path to the local scope.
+ * @return the element for the class
+ */
+ public Element findClass(String name, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+ return wrapInvocationOnResolveInstance(FIND_TYPE, env, names.fromString(name));
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Finds the class with name {@code name} in a given package.
+ *
+ * @param name
+ * The name of the class.
+ * @param pck
+ * The PackageSymbol for the package.
+ * @param path
+ * The tree path to the local scope.
+ * @return the {@code ClassSymbol} for the class if it is found,
+ * {@code null} otherwise
+ */
+ public ClassSymbol findClassInPackage(String name, PackageSymbol pck, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+ Element res = wrapInvocationOnResolveInstance(FIND_IDENT_IN_PACKAGE, env, pck,
+ names.fromString(name), TYP);
+ if (res.getKind() == ElementKind.CLASS) {
+ return (ClassSymbol) res;
+ } else {
+ return null;
+ }
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Finds the method element for a given name and list of expected parameter
+ * types.
+ *
+ * <p>
+ * The method adheres to all the rules of Java's scoping (while also
+ * considering the imports) for name resolution.
+ *
+ * @param methodName
+ * Name of the method to find.
+ * @param receiverType
+ * Type of the receiver of the method
+ * @param path
+ * Tree path.
+ * @return the method element (if found)
+ */
+ public Element findMethod(String methodName, TypeMirror receiverType,
+ TreePath path, java.util.List<TypeMirror> argumentTypes) {
+ Log.DiagnosticHandler discardDiagnosticHandler =
+ new Log.DiscardDiagnosticHandler(log);
+ try {
+ JavacScope scope = (JavacScope) trees.getScope(path);
+ Env<AttrContext> env = scope.getEnv();
+
+ Type site = (Type) receiverType;
+ Name name = names.fromString(methodName);
+ List<Type> argtypes = List.nil();
+ for (TypeMirror a : argumentTypes) {
+ argtypes = argtypes.append((Type) a);
+ }
+ List<Type> typeargtypes = List.nil();
+ boolean allowBoxing = true;
+ boolean useVarargs = false;
+ boolean operator = true;
+
+ try {
+ // For some reason we have to set our own method context, which is rather ugly.
+ // TODO: find a nicer way to do this.
+ Object methodContext = buildMethodContext();
+ Object oldContext = getField(resolve, "currentResolutionContext");
+ setField(resolve, "currentResolutionContext", methodContext);
+ Element result = wrapInvocationOnResolveInstance(FIND_METHOD, env, site, name, argtypes,
+ typeargtypes, allowBoxing, useVarargs, operator);
+ setField(resolve, "currentResolutionContext", oldContext);
+ return result;
+ } catch (Throwable t) {
+ Error err = new AssertionError("Unexpected Reflection error");
+ err.initCause(t);
+ throw err;
+ }
+ } finally {
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ }
+ }
+
+ /**
+ * Build an instance of {@code Resolve$MethodResolutionContext}.
+ */
+ protected Object buildMethodContext() throws ClassNotFoundException,
+ InstantiationException, IllegalAccessException,
+ InvocationTargetException, NoSuchFieldException {
+ // Class is not accessible, instantiate reflectively.
+ Class<?> methCtxClss = Class.forName("com.sun.tools.javac.comp.Resolve$MethodResolutionContext");
+ Constructor<?> constructor = methCtxClss.getDeclaredConstructors()[0];
+ constructor.setAccessible(true);
+ Object methodContext = constructor.newInstance(resolve);
+ // we need to also initialize the fields attrMode and step
+ setField(methodContext, "attrMode", DeferredAttr.AttrMode.CHECK);
+ @SuppressWarnings("rawtypes")
+ List<?> phases = (List) getField(resolve, "methodResolutionSteps");
+ setField(methodContext, "step", phases.get(1));
+ return methodContext;
+ }
+
+ /** Reflectively set a field. */
+ private void setField(Object receiver, String fieldName,
+ Object value) throws NoSuchFieldException,
+ IllegalAccessException {
+ Field f = receiver.getClass().getDeclaredField(fieldName);
+ f.setAccessible(true);
+ f.set(receiver, value);
+ }
+
+ /** Reflectively get the value of a field. */
+ private Object getField(Object receiver, String fieldName) throws NoSuchFieldException,
+ IllegalAccessException {
+ Field f = receiver.getClass().getDeclaredField(fieldName);
+ f.setAccessible(true);
+ return f.get(receiver);
+ }
+
+ private Symbol wrapInvocationOnResolveInstance(Method method, Object... args) {
+ return wrapInvocation(resolve, method, args);
+ }
+
+ private Symbol wrapInvocation(Object receiver, Method method, Object... args) {
+ try {
+ return (Symbol) method.invoke(receiver, args);
+ } catch (IllegalAccessException e) {
+ Error err = new AssertionError("Unexpected Reflection error");
+ err.initCause(e);
+ throw err;
+ } catch (IllegalArgumentException e) {
+ Error err = new AssertionError("Unexpected Reflection error");
+ err.initCause(e);
+ throw err;
+ } catch (InvocationTargetException e) {
+ Error err = new AssertionError("Unexpected Reflection error");
+ err.initCause(e);
+ throw err;
+ }
+ }
+}