Package com.google.errorprone.bugpatterns.threadsafety

Source Code of com.google.errorprone.bugpatterns.threadsafety.GuardedBySymbolResolver

/*
* Copyright 2014 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.bugpatterns.threadsafety;

import static com.google.errorprone.bugpatterns.threadsafety.IllegalGuardedBy.checkGuardedBy;

import com.google.errorprone.VisitorState;
import com.google.errorprone.util.ASTHelpers;

import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Scope;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.comp.Attr;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;

import javax.lang.model.element.ElementKind;

/**
* A symbol resolver used while binding guardedby expressions from string literals.
*
* @author cushon@google.com (Liam Miller-Cushon)
*/
public class GuardedBySymbolResolver implements GuardedByBinder.Resolver {

  private final ClassSymbol enclosingClass;
  private final Tree decl;
  private final JCTree.JCCompilationUnit compilationUnit;
  private final Context context;
  private final Types types;

  public static GuardedBySymbolResolver from(Tree tree, VisitorState visitorState) {
    return GuardedBySymbolResolver.from(
        (ClassSymbol) ASTHelpers.getSymbol(tree).owner,
        visitorState.getPath().getCompilationUnit(),
        visitorState.context,
        tree);
  }

  public static GuardedBySymbolResolver from(ClassSymbol owner,
      CompilationUnitTree compilationUnit, Context context, Tree leaf) {
    return new GuardedBySymbolResolver(owner, compilationUnit, context, leaf);
  }

  private GuardedBySymbolResolver(ClassSymbol enclosingClass,
      CompilationUnitTree compilationUnit, Context context, Tree leaf) {
    this.compilationUnit = (JCCompilationUnit) compilationUnit;
    this.enclosingClass = enclosingClass;
    this.context = context;
    this.types = Types.instance(context);
    this.decl = leaf;
  }

  public Context context() {
    return context;
  }

  public ClassSymbol enclosingClass() {
    return enclosingClass;
  }

  @Override
  public Symbol resolveIdentifier(IdentifierTree node) {
    String name = node.getName().toString();

    if (name.equals("this")) {
      return enclosingClass;
    }

    // TODO(user): consider disallowing this? It's the only case where the lock description
    // isn't legal java.
    if (name.equals("itself")) {
      if (decl instanceof VariableTree) {
        name = ((VariableTree) decl).getName().toString();
      } else if (decl instanceof MethodTree) {
        name = ((MethodTree) decl).getName().toString();
      } else {
        throw new IllegalGuardedBy(decl.getClass().toString());
      }
      return getField(enclosingClass, name);
    }

    Symbol.VarSymbol field = getField(enclosingClass, name);
    if (field != null) {
      return field;
    }

    Symbol type = resolveType(name, SearchSuperTypes.YES);
    if (type != null) {
      return type;
    }

    throw new IllegalGuardedBy(name);
  }

  @Override
  public Symbol.MethodSymbol resolveMethod(MethodInvocationTree node,
      javax.lang.model.element.Name name) {
    return getMethod(enclosingClass, name.toString());
  }

  @Override
  public Symbol.MethodSymbol resolveMethod(MethodInvocationTree node, GuardedByExpression base,
      javax.lang.model.element.Name identifier) {
    Symbol baseSym = base.kind() == GuardedByExpression.Kind.THIS
        ? enclosingClass
        : base.type().asElement();
    return getMethod(baseSym, identifier.toString());
  }

  private Symbol.MethodSymbol getMethod(Symbol classSymbol, String name) {
    return getMember(Symbol.MethodSymbol.class, ElementKind.METHOD, classSymbol, name);
  }

  @Override
  public Symbol resolveSelect(GuardedByExpression base, MemberSelectTree node) {
    Symbol baseSym = base.kind() == GuardedByExpression.Kind.THIS
        ? enclosingClass
        : base.type().asElement();
    return getField(baseSym, node.getIdentifier().toString());
  }

  private Symbol.VarSymbol getField(Symbol classSymbol, String name) {
    return getMember(Symbol.VarSymbol.class, ElementKind.FIELD, classSymbol, name);
  }

  private <T extends Symbol> T getMember(Class<T> type, ElementKind kind, Symbol classSymbol,
      String name) {
    if (classSymbol.type == null) {
      return null;
    }
    for (Type t : types.closure(classSymbol.type)) {
      Scope scope = t.tsym.members();
      for (Scope.Entry e = scope.lookup(getName(name)); e.scope != null; e = e.next()) {
        if (e.sym.getKind().equals(kind)) {
          return type.cast(e.sym);
        }
      }
    }
    if (classSymbol.owner != null && classSymbol != classSymbol.owner
        && classSymbol.owner instanceof Symbol.ClassSymbol) {
      T sym = getMember(type, kind, classSymbol.owner, name);
      if (sym != null) {
        return sym;
      }
    }
    if (classSymbol.hasOuterInstance()) {
      T sym = getMember(type, kind, classSymbol.type.getEnclosingType().asElement(), name);
      if (sym != null) {
        return sym;
      }
    }
    return null;
  }

  @Override
  public Symbol resolveTypeLiteral(ExpressionTree expr) {
    checkGuardedBy(expr instanceof IdentifierTree, "bad type literal: %s", expr);
    IdentifierTree ident = (IdentifierTree) expr;
    Symbol type = resolveType(ident.getName().toString(), SearchSuperTypes.YES);
    if (type instanceof Symbol.ClassSymbol) {
      return type;
    }
    return null;
  }

  private static enum SearchSuperTypes { YES, NO }

  /**
   * Resolves a simple name as a type. Considers super classes, lexically enclosing classes, and
   * then arbitrary types available in the current environment.
   */
  private Symbol resolveType(String name, SearchSuperTypes searchSuperTypes) {
    Symbol type = null;
    if (searchSuperTypes == SearchSuperTypes.YES) {
      type = getSuperType(enclosingClass, name);
    }
    if (type == null) {
      type = getLexicallyEnclosing(enclosingClass, name);
    }
    if (type == null) {
      type = attribIdent(name);
    }
    checkGuardedBy(!(type instanceof Symbol.PackageSymbol),
        "All we could find for '%s' was a package symbol.", name);
    return type;
  }

  private Symbol getSuperType(Symbol symbol, String name) {
    for (Type t : types.closure(symbol.type)) {
      if (t.asElement().getSimpleName().contentEquals(name)) {
        return t.asElement();
      }
    }
    return null;
  }

  private Symbol getLexicallyEnclosing(ClassSymbol symbol, String name) {
    Symbol current = symbol.owner;
    while (true) {
      if (current == null || current.getSimpleName().contentEquals(name)) {
        return current;
      }

      if (current != current.owner && current.owner instanceof Symbol.ClassSymbol) {
        current = current.owner;
      } else {
        return null;
      }
    }
  }

  private Symbol attribIdent(String name) {
    Attr attr = Attr.instance(context);
    TreeMaker tm = TreeMaker.instance(context);
    return attr.attribIdent(tm.Ident(getName(name)), compilationUnit);
  }

  private Name getName(String name) {
    return Names.instance(context).fromString(name);
  }

  @Override
  public Symbol resolveEnclosingClass(ExpressionTree expr) {
    checkGuardedBy(expr instanceof IdentifierTree, "bad type literal: %s", expr);
    IdentifierTree ident = (IdentifierTree) expr;
    Symbol type = resolveType(ident.getName().toString(), SearchSuperTypes.NO);
    if (type instanceof Symbol.ClassSymbol) {
      return type;
    }
    return null;
  }
}
TOP

Related Classes of com.google.errorprone.bugpatterns.threadsafety.GuardedBySymbolResolver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.