package checkers.lock; import java.util.*; import javax.lang.model.element.AnnotationMirror; import com.sun.source.tree.*; import checkers.lock.quals.GuardedBy; import checkers.types.AnnotatedTypeMirror; import checkers.types.BasicAnnotatedTypeFactory; import checkers.util.AnnotationUtils; import checkers.util.TreeUtils; import checkers.util.TypesUtils; import checkers.util.AnnotationUtils.AnnotationBuilder; import static checkers.util.AnnotationUtils.elementValue;; // Disclaimer: // This class is currently in its alpha form. For sample code on how to write // checkers, please review other checkers for code samples. /** * The type factory for {@code Lock} type system. * * The annotated types returned by class contain {@code GuardedBy} type * qualifiers only for the locks that are not currently held. * */ public class LockAnnotatedTypeFactory extends BasicAnnotatedTypeFactory<LockChecker> { private List<String> heldLocks = new ArrayList<String>(); private final AnnotationMirror GUARDED_BY; public LockAnnotatedTypeFactory(LockChecker checker, CompilationUnitTree root) { super(checker, root); GUARDED_BY = annotations.fromClass(GuardedBy.class); } public void setHeldLocks(List<String> heldLocks) { this.heldLocks = heldLocks; } public List<String> getHeldLock() { return Collections.unmodifiableList(heldLocks); } private void removeHeldLocks(AnnotatedTypeMirror type) { Set<AnnotationMirror> annos = type.getAnnotations(); if (annos.isEmpty()) return; AnnotationMirror guarded = annos.iterator().next(); String lock = elementValue(guarded, "value", String.class); if (heldLocks.contains(lock)) type.clearAnnotations(); } private AnnotationMirror createGuarded(String lock) { AnnotationUtils.AnnotationBuilder builder = new AnnotationUtils.AnnotationBuilder(env, GuardedBy.class.getCanonicalName()); builder.setValue("value", lock); return builder.build(); } private ExpressionTree receiver(ExpressionTree expr) { if (expr.getKind() == Tree.Kind.METHOD_INVOCATION) expr = ((MethodInvocationTree)expr).getMethodSelect(); expr = TreeUtils.skipParens(expr); if (expr.getKind() == Tree.Kind.MEMBER_SELECT) return ((MemberSelectTree)expr).getExpression(); else return null; } private void replaceThis(AnnotatedTypeMirror type, Tree tree) { if (tree.getKind() != Tree.Kind.IDENTIFIER && tree.getKind() != Tree.Kind.MEMBER_SELECT && tree.getKind() != Tree.Kind.METHOD_INVOCATION) return; ExpressionTree expr = (ExpressionTree)tree; if (!type.hasAnnotation(GUARDED_BY) || TreeUtils.isSelfAccess(expr)) return; AnnotationMirror guardedBy = type.getAnnotation(GuardedBy.class.getCanonicalName()); if (!"this".equals(elementValue(guardedBy, "value", String.class))) return; ExpressionTree receiver = receiver(expr); assert receiver != null; if (receiver != null) { AnnotationMirror newAnno = createGuarded(receiver.toString()); type.clearAnnotations(); type.addAnnotation(newAnno); } } private void replaceItself(AnnotatedTypeMirror type, Tree tree) { if (tree.getKind() != Tree.Kind.IDENTIFIER && tree.getKind() != Tree.Kind.MEMBER_SELECT && tree.getKind() != Tree.Kind.METHOD_INVOCATION) return; ExpressionTree expr = (ExpressionTree)tree; if (!type.hasAnnotation(GUARDED_BY)) return; AnnotationMirror guardedBy = type.getAnnotation(GuardedBy.class.getCanonicalName()); if (!"itself".equals(elementValue(guardedBy, "value", String.class))) return; AnnotationMirror newAnno = createGuarded(expr.toString()); type.clearAnnotations(); type.addAnnotation(newAnno); } @Override protected void annotateImplicit(Tree tree, AnnotatedTypeMirror type) { super.annotateImplicit(tree, type); replaceThis(type, tree); replaceItself(type, tree); removeHeldLocks(type); } @Override protected AnnotationMirror aliasedAnnotation(AnnotationMirror a) { if (TypesUtils.isDeclaredOfName(a.getAnnotationType(), net.jcip.annotations.GuardedBy.class.getCanonicalName())) { AnnotationBuilder builder = new AnnotationBuilder(env, GuardedBy.class); builder.setValue("value", AnnotationUtils.parseStringValue(a, "value")); return builder.build(); } else { return super.aliasedAnnotation(a); } } }