/*
* Copyright 2017-present Facebook, Inc.
*
* 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.facebook.buck.jvm.java.abi.source;
import com.facebook.buck.util.liteinfersupport.Nullable;
import com.sun.source.tree.CatchTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Scope;
import com.sun.source.tree.Tree;
import com.sun.source.util.SimpleTreeVisitor;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import java.util.HashMap;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
/**
* An implementation of {@link Trees} that uses our tree-backed elements and types when available.
*/
class TreeBackedTrees extends Trees {
private final TreeBackedElements elements;
private final TreeBackedTypes types;
private final Trees javacTrees;
private final Map<Tree, TypeMirror> canonicalTypes = new HashMap<>();
public static TreeBackedTrees instance(JavaCompiler.CompilationTask task) {
return ((FrontendOnlyJavacTask) task).getTrees();
}
/* package */ TreeBackedTrees(
Trees javacTrees, TreeBackedElements elements, TreeBackedTypes types) {
this.javacTrees = javacTrees;
this.elements = elements;
this.types = types;
}
/* package */ void clear() {
canonicalTypes.clear();
}
@Override
public SourcePositions getSourcePositions() {
return javacTrees.getSourcePositions();
}
@Override
@Nullable
public Tree getTree(Element element) {
if (element.getKind() == ElementKind.TYPE_PARAMETER) {
// This is a weird javac behavior that we're emulating.
return null;
}
TreePath path = getPath(element);
if (path == null) {
return null;
}
return path.getLeaf();
}
@Override
@Nullable
public ClassTree getTree(TypeElement element) {
return (ClassTree) getTree((Element) element);
}
@Override
@Nullable
public MethodTree getTree(ExecutableElement method) {
return (MethodTree) getTree((Element) method);
}
@Override
public Tree getTree(Element e, AnnotationMirror a) {
throw new UnsupportedOperationException();
}
@Override
public Tree getTree(Element e, AnnotationMirror a, AnnotationValue v) {
throw new UnsupportedOperationException();
}
@Override
public TreePath getPath(CompilationUnitTree unit, Tree node) {
return javacTrees.getPath(unit, node);
}
@Override
@Nullable
public TreePath getPath(Element e) {
if (e instanceof TreeBackedElement) {
return ((TreeBackedElement) e).getTreePath();
}
TreePath result = javacTrees.getPath(e);
if (result != null) {
// If we've properly hidden all the javac implementations of things, the only way a caller
// should be able to get a non-`TreeBackedElement` is by looking at classes on the classpath.
// Those come from .class files, and thus by definition do not have ASTs.
throw new AssertionError(String.format("Leaked a javac element for: %s", e));
}
return null;
}
@Override
public TreePath getPath(Element e, AnnotationMirror a) {
throw new UnsupportedOperationException();
}
@Override
public TreePath getPath(Element e, AnnotationMirror a, AnnotationValue v) {
throw new UnsupportedOperationException();
}
@Override
@Nullable
public Element getElement(TreePath path) {
return elements.getCanonicalElement(javacTrees.getElement(path));
}
@Override
@Nullable
public TypeMirror getTypeMirror(TreePath path) {
Tree leaf = path.getLeaf();
TypeMirror result = canonicalTypes.get(leaf);
if (result == null) {
result = types.getCanonicalType(javacTrees.getTypeMirror(path));
canonicalTypes.put(leaf, result);
}
return result;
}
@Override
public Scope getScope(TreePath path) {
throw new UnsupportedOperationException();
}
@Override
@Nullable
public String getDocComment(TreePath path) {
return javacTrees.getDocComment(path);
}
@Override
public boolean isAccessible(Scope scope, TypeElement type) {
throw new UnsupportedOperationException();
}
@Override
public boolean isAccessible(Scope scope, Element member, DeclaredType type) {
throw new UnsupportedOperationException();
}
@Override
public TypeMirror getOriginalType(ErrorType errorType) {
throw new UnsupportedOperationException();
}
@Override
public void printMessage(
Diagnostic.Kind kind, CharSequence msg, Tree t, CompilationUnitTree root) {
javacTrees.printMessage(kind, msg, t, root);
}
@Override
public TypeMirror getLub(CatchTree tree) {
throw new UnsupportedOperationException();
}
/**
* Takes a {@link MemberSelectTree} or {@link IdentifierTree} and returns the name it represents
* as a {@link CharSequence}.
*/
/* package */
static CharSequence treeToName(Tree tree) {
if (tree == null) {
return "";
}
return tree.accept(
new SimpleTreeVisitor<CharSequence, Void>() {
@Override
protected CharSequence defaultAction(Tree node, Void aVoid) {
throw new AssertionError(String.format("Unexpected tree of kind: %s", node.getKind()));
}
@Override
public CharSequence visitMemberSelect(MemberSelectTree node, Void aVoid) {
return String.format(
"%s.%s", node.getExpression().accept(this, aVoid), node.getIdentifier());
}
@Override
public CharSequence visitIdentifier(IdentifierTree node, Void aVoid) {
return node.getName();
}
},
null);
}
}