lombok.javac.JavacResolution Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2011-2021 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.javac;
import static lombok.javac.Javac.*;
import static lombok.javac.JavacTreeMaker.TypeTag.typeTag;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.lang.model.type.TypeKind;
import javax.tools.JavaFileObject;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.ArrayType;
import com.sun.tools.javac.code.Type.CapturedType;
import com.sun.tools.javac.code.Type.ClassType;
import com.sun.tools.javac.code.Type.TypeVar;
import com.sun.tools.javac.code.Type.WildcardType;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.comp.ArgumentAttr;
import com.sun.tools.javac.comp.Attr;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Enter;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.comp.MemberEnter;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.JCWildcard;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
import lombok.core.debug.AssertionLogger;
import lombok.permit.Permit;
public class JavacResolution {
private final Context context;
private final Attr attr;
private final CompilerMessageSuppressor messageSuppressor;
private static final Method isLocal;
static {
Method local = Permit.permissiveGetMethod(TypeSymbol.class, "isLocal");
if (local == null) {
local = Permit.permissiveGetMethod(TypeSymbol.class, "isDirectlyOrIndirectlyLocal");
}
isLocal = local;
}
public JavacResolution(Context context) {
this.context = context;
attr = Attr.instance(context);
messageSuppressor = new CompilerMessageSuppressor(context);
}
/*
* We need to dig down to the level of the method or field declaration or (static) initializer block, then attribute that entire method/field/block using
* the appropriate environment. So, we start from the top and walk down the node tree until we hit that method/field/block and stop there, recording both
* the environment object (`env`) and the exact tree node (`copyAt`) at which to begin the attr process.
*/
private static final class EnvFinder extends JCTree.Visitor {
private Env env = null;
private Enter enter;
private MemberEnter memberEnter;
private JCTree copyAt = null;
EnvFinder(Context context) {
this.enter = Enter.instance(context);
this.memberEnter = MemberEnter.instance(context);
}
Env get() {
return env;
}
JCTree copyAt() {
return copyAt;
}
@Override public void visitTopLevel(JCCompilationUnit tree) {
if (copyAt != null) return;
env = enter.getTopLevelEnv(tree);
}
@Override public void visitClassDef(JCClassDecl tree) {
if (copyAt != null) return;
if (tree.sym != null) env = enter.getClassEnv(tree.sym);
}
@Override public void visitMethodDef(JCMethodDecl tree) {
if (copyAt != null) return;
env = memberEnter.getMethodEnv(tree, env);
copyAt = tree;
}
public void visitVarDef(JCVariableDecl tree) {
if (copyAt != null) return;
env = memberEnter.getInitEnv(tree, env);
copyAt = tree;
}
@Override public void visitBlock(JCBlock tree) {
if (copyAt != null) return;
copyAt = tree;
}
@Override public void visitTree(JCTree that) {
}
}
public Map resolveMethodMember(JavacNode node) {
ArrayDeque stack = new ArrayDeque();
{
JavacNode n = node;
while (n != null) {
stack.push(n.get());
n = n.up();
}
}
messageSuppressor.disableLoggers();
try {
EnvFinder finder = new EnvFinder(node.getContext());
while (!stack.isEmpty()) stack.pop().accept(finder);
TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node.getTreeMaker(), node.getContext());
JCTree copy = mirrorMaker.copy(finder.copyAt());
Log log = Log.instance(node.getContext());
JavaFileObject oldFileObject = log.useSource(((JCCompilationUnit) node.top().get()).getSourceFile());
try {
memberEnterAndAttribute(copy, finder.get(), node.getContext());
return mirrorMaker.getOriginalToCopyMap();
} finally {
log.useSource(oldFileObject);
}
} finally {
messageSuppressor.enableLoggers();
}
}
private static Field memberEnterDotEnv;
private static Field getMemberEnterDotEnv() {
if (memberEnterDotEnv != null) return memberEnterDotEnv;
try {
return memberEnterDotEnv = Permit.getField(MemberEnter.class, "env");
} catch (NoSuchFieldException e) {
return null;
}
}
@SuppressWarnings("unchecked")
private static Env getEnvOfMemberEnter(MemberEnter memberEnter) {
Field f = getMemberEnterDotEnv();
try {
return (Env) f.get(memberEnter);
} catch (Exception e) {
return null;
}
}
private static void setEnvOfMemberEnter(MemberEnter memberEnter, Env env) {
Field f = getMemberEnterDotEnv();
try {
f.set(memberEnter, env);
} catch (Exception e) {
return;
}
}
private void memberEnterAndAttribute(JCTree copy, Env env, Context context) {
MemberEnter memberEnter = MemberEnter.instance(context);
Env oldEnv = getEnvOfMemberEnter(memberEnter);
setEnvOfMemberEnter(memberEnter, env);
try {
copy.accept(memberEnter);
} catch (Exception ignore) {
// intentionally ignored; usually even if this step fails, val will work (but not for val in method local inner classes and anonymous inner classes).
AssertionLogger.assertLog("member enter failed.", ignore);
} finally {
setEnvOfMemberEnter(memberEnter, oldEnv);
}
attrib(copy, env);
}
public void resolveClassMember(JavacNode node) {
ArrayDeque stack = new ArrayDeque();
{
JavacNode n = node;
while (n != null) {
stack.push(n.get());
n = n.up();
}
}
messageSuppressor.disableLoggers();
try {
EnvFinder finder = new EnvFinder(node.getContext());
while (!stack.isEmpty()) stack.pop().accept(finder);
attrib(node.get(), finder.get());
} finally {
messageSuppressor.enableLoggers();
}
}
private void attrib(JCTree tree, Env env) {
if (env.enclClass.type == null) try {
env.enclClass.type = Type.noType;
} catch (Throwable ignore) {
// This addresses issue #1553 which involves JDK9; if it doesn't exist, we probably don't need to set it.
}
Map,?> cache = null;
try {
cache = ArgumentAttrReflect.enableTempCache(context);
if (tree instanceof JCBlock) attr.attribStat(tree, env);
else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl) tree).body, env);
else if (tree instanceof JCVariableDecl) attr.attribStat(tree, env);
else throw new IllegalStateException("Called with something that isn't a block, method decl, or variable decl");
} finally {
ArgumentAttrReflect.restoreCache(cache, context);
}
}
public static class TypeNotConvertibleException extends Exception {
public TypeNotConvertibleException(String msg) {
super(msg);
}
}
private static class ReflectiveAccess {
private static Method UPPER_BOUND;
private static Throwable initError;
static {
Method upperBound = null;
try {
upperBound = Permit.getMethod(Types.class, "upperBound", Type.class);
} catch (Throwable e) {
initError = e;
}
if (upperBound == null) try {
upperBound = Permit.getMethod(Types.class, "wildUpperBound", Type.class);
} catch (Throwable e) {
initError = e;
}
UPPER_BOUND = upperBound;
}
public static Type Types_upperBound(Types types, Type type) {
return (Type) Permit.invokeSneaky(initError, UPPER_BOUND, types, type);
}
}
/**
* ArgumentAttr was added in Java 9 and caches some method arguments. Lombok should cleanup its changes after resolution.
*/
private static class ArgumentAttrReflect {
private static Field ARGUMENT_TYPE_CACHE;
static {
if (Javac.getJavaCompilerVersion() >= 9) {
try {
ARGUMENT_TYPE_CACHE = Permit.getField(ArgumentAttr.class, "argumentTypeCache");
} catch (Exception ignore) {}
}
}
public static Map, ?> enableTempCache(Context context) {
if (ARGUMENT_TYPE_CACHE == null) return null;
ArgumentAttr argumentAttr = ArgumentAttr.instance(context);
try {
Map, ?> cache = (Map, ?>) Permit.get(ARGUMENT_TYPE_CACHE, argumentAttr);
Permit.set(ARGUMENT_TYPE_CACHE, argumentAttr, new LinkedHashMap
© 2015 - 2025 Weber Informatics LLC | Privacy Policy