/* * Copyright 2013 Google Inc. All rights reserved. * * 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.errorprone.refaster; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Function; import com.google.common.collect.ImmutableClassToInstanceMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.reflect.TypeToken; import com.google.errorprone.SubContext; import com.google.errorprone.VisitorState; import com.google.errorprone.matchers.Matcher; import com.google.errorprone.refaster.annotation.Matches; import com.google.errorprone.refaster.annotation.NotMatches; import com.google.errorprone.refaster.annotation.OfKind; import com.google.errorprone.refaster.annotation.Repeated; import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.AnnotatedTypeTree; import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.ArrayAccessTree; import com.sun.source.tree.ArrayTypeTree; import com.sun.source.tree.AssertTree; import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.BinaryTree; import com.sun.source.tree.BlockTree; import com.sun.source.tree.BreakTree; import com.sun.source.tree.CatchTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompoundAssignmentTree; import com.sun.source.tree.ConditionalExpressionTree; import com.sun.source.tree.ContinueTree; import com.sun.source.tree.DoWhileLoopTree; import com.sun.source.tree.EmptyStatementTree; import com.sun.source.tree.EnhancedForLoopTree; import com.sun.source.tree.ExpressionStatementTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.ForLoopTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.IfTree; import com.sun.source.tree.InstanceOfTree; import com.sun.source.tree.IntersectionTypeTree; import com.sun.source.tree.LabeledStatementTree; import com.sun.source.tree.LambdaExpressionTree; import com.sun.source.tree.LiteralTree; import com.sun.source.tree.MemberReferenceTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.ModifiersTree; import com.sun.source.tree.NewArrayTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.ParameterizedTypeTree; import com.sun.source.tree.ParenthesizedTree; import com.sun.source.tree.PrimitiveTypeTree; import com.sun.source.tree.ReturnTree; import com.sun.source.tree.StatementTree; import com.sun.source.tree.SynchronizedTree; import com.sun.source.tree.ThrowTree; import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.TryTree; import com.sun.source.tree.TypeCastTree; import com.sun.source.tree.TypeParameterTree; import com.sun.source.tree.UnaryTree; import com.sun.source.tree.UnionTypeTree; import com.sun.source.tree.VariableTree; import com.sun.source.tree.WhileLoopTree; import com.sun.source.tree.WildcardTree; import com.sun.source.util.SimpleTreeVisitor; import com.sun.tools.javac.code.Attribute.Compound; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.ArrayType; import com.sun.tools.javac.code.Type.ClassType; import com.sun.tools.javac.code.Type.ForAll; import com.sun.tools.javac.code.Type.IntersectionClassType; import com.sun.tools.javac.code.Type.MethodType; 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.model.AnnotationProxyMaker; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCLambda; import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree; import com.sun.tools.javac.util.Context; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Nullable; import javax.lang.model.element.Element; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.MirroredTypeException; /** * Converts a type-checked syntax tree to a portable {@code UTree} template. * * @author lowasser@google.com (Louis Wasserman) */ public class UTemplater extends SimpleTreeVisitor<Tree, Void> { /** * Context key to indicate that templates should be treated as BlockTemplates, regardless * of their structure. */ public static final Context.Key<Boolean> REQUIRE_BLOCK_KEY = new Context.Key<>(); /** * Returns a template based on a method. One-line methods starting with a {@code return} statement * are guessed to be expression templates, and all other methods are guessed to be block * templates. */ public static Template<?> createTemplate(Context context, MethodTree decl) { MethodSymbol declSym = ASTHelpers.getSymbol(decl); ImmutableClassToInstanceMap<Annotation> annotations = UTemplater.annotationMap(declSym); ImmutableMap<String, VarSymbol> freeExpressionVars = freeExpressionVariables(decl); Context subContext = new SubContext(context); final UTemplater templater = new UTemplater(freeExpressionVars, subContext); ImmutableMap<String, UType> expressionVarTypes = ImmutableMap.copyOf( Maps.transformValues(freeExpressionVars, new Function<VarSymbol, UType>() { @Override public UType apply(VarSymbol sym) { return templater.template(sym.type); } })); UType genericType = templater.template(declSym.type); List<UTypeVar> typeParameters; UMethodType methodType; if (genericType instanceof UForAll) { UForAll forAllType = (UForAll) genericType; typeParameters = forAllType.getTypeVars(); methodType = (UMethodType) forAllType.getQuantifiedType(); } else if (genericType instanceof UMethodType) { typeParameters = ImmutableList.of(); methodType = (UMethodType) genericType; } else { throw new IllegalArgumentException( "Expected genericType to be either a ForAll or a UMethodType, but was " + genericType); } List<? extends StatementTree> bodyStatements = decl.getBody().getStatements(); if (bodyStatements.size() == 1 && Iterables.getOnlyElement(bodyStatements).getKind() == Kind.RETURN && context.get(REQUIRE_BLOCK_KEY) == null) { ExpressionTree expression = ((ReturnTree) Iterables.getOnlyElement(bodyStatements)).getExpression(); return ExpressionTemplate.create( annotations, typeParameters, expressionVarTypes, templater.template(expression), methodType.getReturnType()); } else { List<UStatement> templateStatements = new ArrayList<>(); for (StatementTree statement : bodyStatements) { templateStatements.add(templater.template(statement)); } return BlockTemplate.create(annotations, typeParameters, expressionVarTypes, templateStatements); } } public static ImmutableMap<String, VarSymbol> freeExpressionVariables( MethodTree templateMethodDecl) { ImmutableMap.Builder<String, VarSymbol> builder = ImmutableMap.builder(); for (VariableTree param : templateMethodDecl.getParameters()) { builder.put(param.getName().toString(), ASTHelpers.getSymbol(param)); } return builder.build(); } private final ImmutableMap<String, VarSymbol> freeVariables; private final Context context; public UTemplater(Map<String, VarSymbol> freeVariables, Context context) { this.freeVariables = ImmutableMap.copyOf(freeVariables); this.context = context; } UTemplater(Context context) { this(ImmutableMap.<String, VarSymbol>of(), context); } public Tree template(Tree tree) { return tree.accept(this, null); } @Nullable private List<Tree> templateTrees(@Nullable Iterable<? extends Tree> trees) { if (trees == null) { return null; } ImmutableList.Builder<Tree> builder = ImmutableList.builder(); for (Tree tree : trees) { builder.add(template(tree)); } return builder.build(); } private static <T> ImmutableList<T> cast(Iterable<?> elements, Class<T> clazz) { ImmutableList.Builder<T> builder = ImmutableList.builder(); for (Object element : elements) { builder.add(clazz.cast(element)); } return builder.build(); } @Override public UMethodDecl visitMethod(MethodTree decl, Void v) { return UMethodDecl.create( visitModifiers(decl.getModifiers(), null), decl.getName(), templateType(decl.getReturnType()), cast(templateStatements(decl.getParameters()), UVariableDecl.class), templateExpressions(decl.getThrows()), (UBlock) template(decl.getBody())); } @Override public UModifiers visitModifiers(ModifiersTree modifiers, Void v) { return UModifiers.create(((JCModifiers) modifiers).flags, cast(templateExpressions(modifiers.getAnnotations()), UAnnotation.class)); } public UExpression template(ExpressionTree tree) { return (UExpression) tree.accept(this, null); } @Nullable private List<UExpression> templateExpressions( @Nullable Iterable<? extends ExpressionTree> expressions) { if (expressions == null) { return null; } ImmutableList.Builder<UExpression> builder = ImmutableList.builder(); for (ExpressionTree expression : expressions) { builder.add(template(expression)); } return builder.build(); } public UExpression templateType(Tree tree) { checkArgument(tree instanceof ExpressionTree, "Trees representing types are expected to implement ExpressionTree, but %s does not", tree); return template((ExpressionTree) tree); } @Nullable private List<UExpression> templateTypeExpressions(@Nullable Iterable<? extends Tree> types) { if (types == null) { return null; } ImmutableList.Builder<UExpression> builder = ImmutableList.builder(); for (Tree type : types) { builder.add(templateType(type)); } return builder.build(); } @Override public UInstanceOf visitInstanceOf(InstanceOfTree tree, Void v) { return UInstanceOf.create(template(tree.getExpression()), templateType(tree.getType())); } @Override public UPrimitiveTypeTree visitPrimitiveType(PrimitiveTypeTree tree, Void v) { return UPrimitiveTypeTree.create(((JCPrimitiveTypeTree) tree).typetag); } @Override public ULiteral visitLiteral(LiteralTree tree, Void v) { return ULiteral.create(tree.getKind(), tree.getValue()); } @Override public UParens visitParenthesized(ParenthesizedTree tree, Void v) { return UParens.create(template(tree.getExpression())); } @Override public UAssign visitAssignment(AssignmentTree tree, Void v) { return UAssign.create(template(tree.getVariable()), template(tree.getExpression())); } @Override public UArrayAccess visitArrayAccess(ArrayAccessTree tree, Void v) { return UArrayAccess.create(template(tree.getExpression()), template(tree.getIndex())); } @Override public UAnnotation visitAnnotation(AnnotationTree tree, Void v) { return UAnnotation.create(templateType(tree.getAnnotationType()), templateExpressions(tree.getArguments())); } @Override public UAnnotatedType visitAnnotatedType(AnnotatedTypeTree tree, Void v) { return UAnnotatedType.create( cast(templateExpressions(tree.getAnnotations()), UAnnotation.class), template(tree.getUnderlyingType())); } @Override public UExpression visitMemberSelect(MemberSelectTree tree, Void v) { Symbol sym = ASTHelpers.getSymbol(tree); if (sym instanceof ClassSymbol) { return UClassIdent.create((ClassSymbol) sym); } else if (sym.isStatic()) { ExpressionTree selected = tree.getExpression(); checkState(ASTHelpers.getSymbol(selected) instanceof ClassSymbol, "Refaster cannot match static methods used on instances"); return staticMember(sym); } return UMemberSelect.create(template(tree.getExpression()), tree.getIdentifier(), template(sym.type)); } private UStaticIdent staticMember(Symbol symbol) { return UStaticIdent.create((ClassSymbol) symbol.getEnclosingElement(), symbol.getSimpleName(), template(symbol.asType())); } private static final UStaticIdent ANY_OF; private static final UStaticIdent IS_INSTANCE; private static final UStaticIdent CLAZZ; private static final UStaticIdent NEW_ARRAY; private static final UStaticIdent ENUM_VALUE_OF; private static final UStaticIdent AS_VARARGS; static { UTypeVar tVar = UTypeVar.create("T"); ANY_OF = UStaticIdent.create( Refaster.class.getCanonicalName(), "anyOf", UForAll.create(ImmutableList.of(tVar), UMethodType.create(tVar, UArrayType.create(tVar)))); IS_INSTANCE = UStaticIdent.create( Refaster.class.getCanonicalName(), "isInstance", UForAll.create(ImmutableList.of(tVar), UMethodType.create(UPrimitiveType.BOOLEAN, UClassType.create(Object.class.getCanonicalName())))); CLAZZ = UStaticIdent.create( Refaster.class.getCanonicalName(), "clazz", UForAll.create(ImmutableList.of(tVar), UMethodType.create(UClassType.create(Class.class.getCanonicalName(), tVar)))); NEW_ARRAY = UStaticIdent.create( Refaster.class.getCanonicalName(), "newArray", UForAll.create(ImmutableList.of(tVar), UMethodType.create(UArrayType.create(tVar), UPrimitiveType.INT))); UTypeVar eVar = UTypeVar.create("E", UClassType.create(Enum.class.getCanonicalName(), UTypeVar.create("E"))); ENUM_VALUE_OF = UStaticIdent.create( Refaster.class.getCanonicalName(), "enumValueOf", UForAll.create(ImmutableList.of(eVar), UMethodType.create(eVar, UClassType.create(String.class.getCanonicalName())))); AS_VARARGS = UStaticIdent.create( Refaster.class.getCanonicalName(), "asVarargs", UForAll.create( ImmutableList.of(tVar), UMethodType.create(UArrayType.create(tVar), tVar))); } private static Tree getSingleExplicitTypeArgument(MethodInvocationTree tree) { if (tree.getTypeArguments().isEmpty()) { throw new IllegalArgumentException("Methods in the Refaster class must be invoked with " + "an explicit type parameter; for example, 'Refaster.<T>isInstance(o)'."); } return Iterables.getOnlyElement(tree.getTypeArguments()); } static <T, U extends Unifiable<? super T>> boolean anyMatch( U toUnify, T target, Unifier unifier) { return toUnify.unify(target, unifier).first().isPresent(); } @Override public UExpression visitMethodInvocation(MethodInvocationTree tree, Void v) { if (anyMatch(ANY_OF, tree.getMethodSelect(), new Unifier(context))) { return UAnyOf.create(templateExpressions(tree.getArguments())); } else if (anyMatch(IS_INSTANCE, tree.getMethodSelect(), new Unifier(context))) { return UInstanceOf.create(template(Iterables.getOnlyElement(tree.getArguments())), templateType(getSingleExplicitTypeArgument(tree))); } else if (anyMatch(CLAZZ, tree.getMethodSelect(), new Unifier(context))) { Tree typeArg = getSingleExplicitTypeArgument(tree); return UMemberSelect.create(templateType(typeArg), "class", UClassType.create("java.lang.Class", template(((JCTree) typeArg).type))); } else if (anyMatch(NEW_ARRAY, tree.getMethodSelect(), new Unifier(context))) { Tree typeArg = getSingleExplicitTypeArgument(tree); ExpressionTree lengthArg = Iterables.getOnlyElement(tree.getArguments()); return UNewArray.create(templateType(typeArg), ImmutableList.of(template(lengthArg)), null); } else if (anyMatch(ENUM_VALUE_OF, tree.getMethodSelect(), new Unifier(context))) { Tree typeArg = getSingleExplicitTypeArgument(tree); ExpressionTree strArg = Iterables.getOnlyElement(tree.getArguments()); return UMethodInvocation.create(UMemberSelect.create(templateType(typeArg), "valueOf", UMethodType.create(template(((JCTree) typeArg).type), UClassType.create("java.lang.String"))), template(strArg)); } else if (anyMatch(AS_VARARGS, tree.getMethodSelect(), new Unifier(context))) { ExpressionTree arg = Iterables.getOnlyElement(tree.getArguments()); checkArgument(ASTHelpers.hasAnnotation(arg, Repeated.class, new VisitorState(context))); return template(arg); } Map<MethodSymbol, PlaceholderMethod> placeholderMethods = context.get(RefasterRuleBuilderScanner.PLACEHOLDER_METHODS_KEY); if (placeholderMethods != null && placeholderMethods.containsKey(ASTHelpers.getSymbol(tree))) { return UPlaceholderExpression.create( placeholderMethods.get(ASTHelpers.getSymbol(tree)), templateExpressions(tree.getArguments())); } else { return UMethodInvocation.create(template(tree.getMethodSelect()), templateExpressions(tree.getArguments())); } } @Override public UBinary visitBinary(BinaryTree tree, Void v) { return UBinary.create(tree.getKind(), template(tree.getLeftOperand()), template(tree.getRightOperand())); } @Override public UAssignOp visitCompoundAssignment(CompoundAssignmentTree tree, Void v) { return UAssignOp.create(template(tree.getVariable()), tree.getKind(), template(tree.getExpression())); } @Override public UUnary visitUnary(UnaryTree tree, Void v) { return UUnary.create(tree.getKind(), template(tree.getExpression())); } @Override public UExpression visitConditionalExpression(ConditionalExpressionTree tree, Void v) { return UConditional.create(template(tree.getCondition()), template(tree.getTrueExpression()), template(tree.getFalseExpression())); } @Override public UNewArray visitNewArray(NewArrayTree tree, Void v) { return UNewArray.create((UExpression) template(tree.getType()), templateExpressions(tree.getDimensions()), templateExpressions(tree.getInitializers())); } @Override public UNewClass visitNewClass(NewClassTree tree, Void v) { return UNewClass.create( tree.getEnclosingExpression() == null ? null : template(tree.getEnclosingExpression()), templateTypeExpressions(tree.getTypeArguments()), template(tree.getIdentifier()), templateExpressions(tree.getArguments()), (tree.getClassBody() == null) ? null : visitClass(tree.getClassBody(), null)); } @Override public UClassDecl visitClass(ClassTree tree, Void v) { ImmutableList.Builder<UMethodDecl> decls = ImmutableList.builder(); for (MethodTree decl : Iterables.filter(tree.getMembers(), MethodTree.class)) { if (decl.getReturnType() != null) { decls.add(visitMethod(decl, null)); } } return UClassDecl.create(decls.build()); } @Override public UArrayTypeTree visitArrayType(ArrayTypeTree tree, Void v) { return UArrayTypeTree.create(templateType(tree.getType())); } @Override public UTypeApply visitParameterizedType(ParameterizedTypeTree tree, Void v) { return UTypeApply.create( templateType(tree.getType()), templateTypeExpressions(tree.getTypeArguments())); } @Override public UUnionType visitUnionType(UnionTypeTree tree, Void v) { return UUnionType.create(templateTypeExpressions(tree.getTypeAlternatives())); } @Override public UWildcard visitWildcard(WildcardTree tree, Void v) { return UWildcard.create(tree.getKind(), (tree.getBound() == null) ? null : templateType(tree.getBound())); } @Override public UIntersectionType visitIntersectionType(IntersectionTypeTree tree, Void v) { return UIntersectionType.create(templateTypeExpressions(tree.getBounds())); } @Override public UTypeParameter visitTypeParameter(TypeParameterTree tree, Void v) { return UTypeParameter.create( tree.getName(), templateTypeExpressions(tree.getBounds()), cast(templateExpressions(tree.getAnnotations()), UAnnotation.class)); } @Override public UTypeCast visitTypeCast(TypeCastTree tree, Void v) { return UTypeCast.create(templateType(tree.getType()), template(tree.getExpression())); } @Override public ULambda visitLambdaExpression(LambdaExpressionTree tree, Void v) { return ULambda.create( ((JCLambda) tree).paramKind, cast(templateStatements(tree.getParameters()), UVariableDecl.class), (UTree<?>) template(tree.getBody())); } @Override public UMemberReference visitMemberReference(MemberReferenceTree tree, Void v) { return UMemberReference.create( tree.getMode(), template(tree.getQualifierExpression()), tree.getName(), (tree.getTypeArguments() == null) ? null : templateExpressions(tree.getTypeArguments())); } @Override public UExpression visitIdentifier(IdentifierTree tree, Void v) { Symbol sym = ASTHelpers.getSymbol(tree); if (sym instanceof ClassSymbol) { return UClassIdent.create((ClassSymbol) sym); } else if (sym != null && sym.isStatic()) { return staticMember(sym); } else if (freeVariables.containsKey(tree.getName().toString())) { VarSymbol symbol = freeVariables.get(tree.getName().toString()); checkState(symbol == sym); UExpression ident = UFreeIdent.create(tree.getName()); Matches matches = ASTHelpers.getAnnotation(symbol, Matches.class); if (matches != null) { ident = UMatches.create(getValue(matches), true, ident); } NotMatches notMatches = ASTHelpers.getAnnotation(symbol, NotMatches.class); if (notMatches != null) { ident = UMatches.create(getValue(notMatches), false, ident); } OfKind hasKind = ASTHelpers.getAnnotation(symbol, OfKind.class); if (hasKind != null) { EnumSet<Kind> allowed = EnumSet.copyOf(Arrays.asList(hasKind.value())); ident = UOfKind.create(ident, ImmutableSet.copyOf(allowed)); } // @Repeated annotations need to be checked last. Repeated repeated = ASTHelpers.getAnnotation(symbol, Repeated.class); if (repeated != null) { ident = URepeated.create(tree.getName(), ident); } return ident; } if (sym == null) { return UTypeVarIdent.create(tree.getName()); } switch (sym.getKind()) { case TYPE_PARAMETER: return UTypeVarIdent.create(tree.getName()); default: return ULocalVarIdent.create(tree.getName()); } } /** * Returns the {@link Class} instance for the {@link Matcher} associated with the provided * {@link Matches} annotation. This roundabout solution is recommended and explained by * {@link Element#getAnnotation(Class)}. */ static Class<? extends Matcher<? super ExpressionTree>> getValue(Matches matches) { String name; try { matches.value(); throw new RuntimeException("unreachable"); } catch (MirroredTypeException e) { DeclaredType type = (DeclaredType) e.getTypeMirror(); name = ((TypeElement) type.asElement()).getQualifiedName().toString(); } try { return asSubclass(Class.forName(name), new TypeToken<Matcher<? super ExpressionTree>>() {}); } catch (ClassNotFoundException|ClassCastException e) { throw new RuntimeException(e); } } /** * Returns the {@link Class} instance for the {@link Matcher} associated with the provided * {@link NotMatches} annotation. This roundabout solution is recommended and explained by * {@link Element#getAnnotation(Class)}. */ static Class<? extends Matcher<? super ExpressionTree>> getValue(NotMatches matches) { String name; try { matches.value(); throw new RuntimeException("unreachable"); } catch (MirroredTypeException e) { DeclaredType type = (DeclaredType) e.getTypeMirror(); name = ((TypeElement) type.asElement()).getQualifiedName().toString(); } try { return asSubclass(Class.forName(name), new TypeToken<Matcher<? super ExpressionTree>>() {}); } catch (ClassNotFoundException|ClassCastException e) { throw new RuntimeException(e); } } /** * Similar to {@link Class#asSubclass(Class)}, but it accepts a {@link TypeToken} so it handles * generics better. */ @SuppressWarnings("unchecked") private static <T> Class<? extends T> asSubclass(Class<?> klass, TypeToken<T> token) throws ClassCastException { if (!token.isSupertypeOf(klass)) { throw new ClassCastException(klass + " is not assignable to " + token); } return (Class<? extends T>) klass; } public UStatement template(StatementTree tree) { return (UStatement) tree.accept(this, null); } @Nullable private List<UStatement> templateStatements(@Nullable List<? extends StatementTree> statements) { if (statements == null) { return null; } ImmutableList.Builder<UStatement> builder = ImmutableList.builder(); for (StatementTree statement : statements) { builder.add(template(statement)); } return builder.build(); } @Override public UTry visitTry(TryTree tree, Void v) { @SuppressWarnings({"unchecked", "rawtypes"}) List<UTree<?>> resources = cast(templateTrees(tree.getResources()), (Class<UTree<?>>) (Class) UTree.class); UBlock block = visitBlock(tree.getBlock(), null); ImmutableList.Builder<UCatch> catchesBuilder = ImmutableList.builder(); for (CatchTree catchTree : tree.getCatches()) { catchesBuilder.add(visitCatch(catchTree, null)); } UBlock finallyBlock = (tree.getFinallyBlock() == null) ? null : visitBlock(tree.getFinallyBlock(), null); return UTry.create(resources, block, catchesBuilder.build(), finallyBlock); } @Override public UCatch visitCatch(CatchTree tree, Void v) { return UCatch.create( visitVariable(tree.getParameter(), null), visitBlock(tree.getBlock(), null)); } @Nullable private PlaceholderMethod placeholder(@Nullable ExpressionTree expr) { Map<MethodSymbol, PlaceholderMethod> placeholderMethods = context.get(RefasterRuleBuilderScanner.PLACEHOLDER_METHODS_KEY); return (placeholderMethods != null && expr != null) ? placeholderMethods.get(ASTHelpers.getSymbol(expr)) : null; } @Override public UStatement visitExpressionStatement(ExpressionStatementTree tree, Void v) { PlaceholderMethod placeholderMethod = placeholder(tree.getExpression()); if (placeholderMethod != null && placeholderMethod.returnType().equals(UPrimitiveType.VOID)) { MethodInvocationTree invocation = (MethodInvocationTree) tree.getExpression(); return UPlaceholderStatement.create(placeholderMethod, templateExpressions(invocation.getArguments()), ControlFlowVisitor.Result.NEVER_EXITS); } return UExpressionStatement.create(template(tree.getExpression())); } @Override public UStatement visitReturn(ReturnTree tree, Void v) { PlaceholderMethod placeholderMethod = placeholder(tree.getExpression()); if (placeholderMethod != null) { MethodInvocationTree invocation = (MethodInvocationTree) tree.getExpression(); return UPlaceholderStatement.create(placeholderMethod, templateExpressions(invocation.getArguments()), ControlFlowVisitor.Result.ALWAYS_RETURNS); } return UReturn.create( (tree.getExpression() == null) ? null : template(tree.getExpression())); } @Override public UWhileLoop visitWhileLoop(WhileLoopTree tree, Void v) { return UWhileLoop.create(template(tree.getCondition()), template(tree.getStatement())); } @Override public UVariableDecl visitVariable(VariableTree tree, Void v) { return UVariableDecl.create( tree.getName(), templateType(tree.getType()), (tree.getInitializer() == null) ? null : template(tree.getInitializer())); } @Override public USkip visitEmptyStatement(EmptyStatementTree tree, Void v) { return USkip.INSTANCE; } @Override public UForLoop visitForLoop(ForLoopTree tree, Void v) { return UForLoop.create( templateStatements(tree.getInitializer()), (tree.getCondition() == null) ? null : template(tree.getCondition()), cast(templateStatements(tree.getUpdate()), UExpressionStatement.class), template(tree.getStatement())); } @Override public ULabeledStatement visitLabeledStatement(LabeledStatementTree tree, Void v) { return ULabeledStatement.create(tree.getLabel(), template(tree.getStatement())); } @Override public UBreak visitBreak(BreakTree tree, Void v) { return UBreak.create(tree.getLabel()); } @Override public UContinue visitContinue(ContinueTree tree, Void v) { return UContinue.create(tree.getLabel()); } @Override public UBlock visitBlock(BlockTree tree, Void v) { return UBlock.create(templateStatements(tree.getStatements())); } @Override public UThrow visitThrow(ThrowTree tree, Void v) { return UThrow.create(template(tree.getExpression())); } @Override public UDoWhileLoop visitDoWhileLoop(DoWhileLoopTree tree, Void v) { return UDoWhileLoop.create(template(tree.getStatement()), template(tree.getCondition())); } @Override public UEnhancedForLoop visitEnhancedForLoop(EnhancedForLoopTree tree, Void v) { return UEnhancedForLoop.create( visitVariable(tree.getVariable(), null), template(tree.getExpression()), template(tree.getStatement())); } @Override public USynchronized visitSynchronized(SynchronizedTree tree, Void v) { return USynchronized.create( template(tree.getExpression()), visitBlock(tree.getBlock(), null)); } @Override public UIf visitIf(IfTree tree, Void v) { return UIf.create( template(tree.getCondition()), template(tree.getThenStatement()), (tree.getElseStatement() == null) ? null : template(tree.getElseStatement())); } @Override public UAssert visitAssert(AssertTree tree, Void v) { return UAssert.create( template(tree.getCondition()), (tree.getDetail() == null) ? null : template(tree.getDetail())); } @Override protected UTree<?> defaultAction(Tree tree, Void v) { throw new IllegalArgumentException( "Refaster does not currently support syntax " + tree.getClass()); } public UType template(Type type) { return type.accept(typeTemplater, null); } List<UType> templateTypes(Iterable<? extends Type> types) { ImmutableList.Builder<UType> builder = ImmutableList.builder(); for (Type ty : types) { builder.add(template(ty)); } return builder.build(); } private final Type.Visitor<UType, Void> typeTemplater = new Types.SimpleVisitor<UType, Void>() { private final Map<TypeSymbol, UTypeVar> typeVariables = new HashMap<>(); @Override public UType visitType(Type type, Void v) { if (UPrimitiveType.isDeFactoPrimitive(type.getKind())) { return UPrimitiveType.create(type.getKind()); } else { throw new IllegalArgumentException( "Refaster does not currently support syntax " + type.getKind()); } } @Override public UArrayType visitArrayType(ArrayType type, Void v) { return UArrayType.create(type.getComponentType().accept(this, null)); } @Override public UMethodType visitMethodType(MethodType type, Void v) { return UMethodType.create( type.getReturnType().accept(this, null), templateTypes(type.getParameterTypes())); } @Override public UType visitClassType(ClassType type, Void v) { if (type instanceof IntersectionClassType) { return UIntersectionClassType.create( templateTypes(((IntersectionClassType) type).getComponents())); } return UClassType.create( type.tsym.getQualifiedName().toString(), templateTypes(type.getTypeArguments())); } @Override public UWildcardType visitWildcardType(WildcardType type, Void v) { return UWildcardType.create(type.kind, type.type.accept(this, null)); } @Override public UTypeVar visitTypeVar(TypeVar type, Void v) { /* * In order to handle recursively bounded type variables without a stack overflow, we first * cache a type var with no bounds, then we template the bounds. */ TypeSymbol tsym = type.asElement(); if (typeVariables.containsKey(tsym)) { return typeVariables.get(tsym); } UTypeVar var = UTypeVar.create(tsym.getSimpleName().toString()); typeVariables.put(tsym, var); // so the type variable can be used recursively in the bounds var.setLowerBound(type.getLowerBound().accept(this, null)); var.setUpperBound(type.getUpperBound().accept(this, null)); return var; } @Override public UForAll visitForAll(ForAll type, Void v) { List<UTypeVar> vars = cast(templateTypes(type.getTypeVariables()), UTypeVar.class); return UForAll.create(vars, type.qtype.accept(this, null)); } }; @SuppressWarnings("unchecked") public static ImmutableClassToInstanceMap<Annotation> annotationMap(Symbol symbol) { ImmutableClassToInstanceMap.Builder<Annotation> builder = ImmutableClassToInstanceMap.builder(); for (Compound compound : symbol.getAnnotationMirrors()) { Name qualifiedAnnotationType = ((TypeElement) compound.getAnnotationType().asElement()).getQualifiedName(); try { Class<? extends Annotation> annotationClazz = Class.forName(qualifiedAnnotationType.toString()).asSubclass(Annotation.class); builder.put((Class) annotationClazz, AnnotationProxyMaker.generateAnnotation(compound, annotationClazz)); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Unrecognized annotation type", e); } } return builder.build(); } }