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.java350
1 files changed, 249 insertions, 101 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
index c47566bfa2..361c6aef99 100644
--- a/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java
+++ b/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/Resolver.java
@@ -1,22 +1,15 @@
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;
@@ -29,81 +22,184 @@ 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;
+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;
-/**
- * A Utility class to find symbols corresponding to string references.
- */
+/** 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 final Method FIND_METHOD;
- private final Method FIND_IDENT_IN_TYPE;
- private final Method FIND_IDENT_IN_PACKAGE;
- private final Method FIND_TYPE;
+ 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;
- 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);
+ 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 =
+ 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_IDENT_IN_TYPE = Resolve.class.getDeclaredMethod(
- "findIdentInType", Env.class, Type.class, Name.class,
- int.class);
+ 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 =
+ 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 = 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");
+ 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);
+ }
+
+ /**
+ * Determine the environment for the given path.
+ *
+ * @param path the tree path to the local scope
+ * @return the corresponding attribution environment
+ */
+ public Env<AttrContext> getEnvForPath(TreePath path) {
+ TreePath iter = path;
+ JavacScope scope = null;
+ while (scope == null && iter != null) {
+ try {
+ scope = (JavacScope) trees.getScope(iter);
+ } catch (Throwable t) {
+ // Work around Issue #1059 by skipping through the TreePath until something
+ // doesn't crash. This probably returns the class scope, so users might not
+ // get the variables they expect. But that is better than crashing.
+ iter = iter.getParentPath();
+ }
+ }
+ if (scope != null) {
+ return scope.getEnv();
+ } else {
+ ErrorReporter.errorAbort(
+ "Could not determine any possible scope for path: " + path.getLeaf());
+ return null;
+ }
+ }
+
+ /**
+ * 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 {
+ Env<AttrContext> env = getEnvForPath(path);
+ 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.
+ * <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.
+ * @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);
+ Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log);
try {
- JavacScope scope = (JavacScope) trees.getScope(path);
- Env<AttrContext> env = scope.getEnv();
- Element res = wrapInvocation(FIND_IDENT_IN_TYPE, env, type,
- names.fromString(name), VAR);
+ Env<AttrContext> env = getEnvForPath(path);
+ 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;
@@ -114,53 +210,94 @@ public class Resolver {
}
/**
+ * 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 findLocalVariableOrParameterOrField(String name, TreePath path) {
+ Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log);
+ try {
+ Env<AttrContext> env = getEnvForPath(path);
+ Element res = wrapInvocationOnResolveInstance(FIND_VAR, env, names.fromString(name));
+ if (res.getKind() == ElementKind.LOCAL_VARIABLE
+ || res.getKind() == ElementKind.PARAMETER
+ || res.getKind() == ElementKind.FIELD) {
+ 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.
+ * <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.
+ * @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);
+ Log.DiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log);
+ try {
+ Env<AttrContext> env = getEnvForPath(path);
+ 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();
- return wrapInvocation(FIND_TYPE, env, names.fromString(name));
+ Env<AttrContext> env = getEnvForPath(path);
+ 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.
+ * 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.
+ * <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).
+ * @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);
+ 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();
+ Env<AttrContext> env = getEnvForPath(path);
Type site = (Type) receiverType;
Name name = names.fromString(methodName);
@@ -179,8 +316,17 @@ public class Resolver {
Object methodContext = buildMethodContext();
Object oldContext = getField(resolve, "currentResolutionContext");
setField(resolve, "currentResolutionContext", methodContext);
- Element result = wrapInvocation(FIND_METHOD, env, site, name, argtypes,
- typeargtypes, allowBoxing, useVarargs, operator);
+ Element result =
+ wrapInvocationOnResolveInstance(
+ FIND_METHOD,
+ env,
+ site,
+ name,
+ argtypes,
+ typeargtypes,
+ allowBoxing,
+ useVarargs,
+ operator);
setField(resolve, "currentResolutionContext", oldContext);
return result;
} catch (Throwable t) {
@@ -193,14 +339,13 @@ public class Resolver {
}
}
- /**
- * Build an instance of {@code Resolve$MethodResolutionContext}.
- */
- protected Object buildMethodContext() throws ClassNotFoundException,
- InstantiationException, IllegalAccessException,
- InvocationTargetException, NoSuchFieldException {
+ /** 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");
+ Class<?> methCtxClss =
+ Class.forName("com.sun.tools.javac.comp.Resolve$MethodResolutionContext");
Constructor<?> constructor = methCtxClss.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Object methodContext = constructor.newInstance(resolve);
@@ -213,25 +358,28 @@ public class Resolver {
}
/** Reflectively set a field. */
- private void setField(Object receiver, String fieldName,
- Object value) throws NoSuchFieldException,
- IllegalAccessException {
+ 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 {
+ 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 wrapInvocation(Method method, Object... args) {
+ 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(resolve, args);
+ return (Symbol) method.invoke(receiver, args);
} catch (IllegalAccessException e) {
Error err = new AssertionError("Unexpected Reflection error");
err.initCause(e);