Java代码分析器(五): 语义分析简介

498 查看

另载于 http://www.qingjingjie.com/blogs/6

本系列临近尾声,科技树到此点满。

语法分析主要用库解决了,代码变成了一棵树,但是变量类型,方法签名之类的东东都不清楚。

如果做IDE插件,可以享受IDE的语义分析能力。为了让程序独立运行,我自己按需实现了语义分析。

比较复杂,就分享一种最典型的吧。贴代码为主,有问题请告诉我,我再改文章。

解析某个变量的声明类型,会上溯当前方法和类,以及祖先类。为了提速(分析大型代码库),可以手动限制范围。
直接贴代码了,已在Exia项目提供。

public class VariableTypeResolver {
  private final String symbol;
  private final ASTNode minScope;

  private boolean methodLevel = true;
  private boolean typeLevel = true;
  
  /**
   * The found result
   */
  private SimpleName declSN;

  private final ASTVisitor visitor = new ASTVisitor() {
    @Override
    public boolean visit(SimpleName sn) {
      if (found()) {
        return false;
      }
      if (sn.getIdentifier().equals(symbol) && sn.getParent() instanceof VariableDeclaration) {
        declSN = sn;
        return false;
      }
      return true;
    }
  };
  
  /**
   * Starts resolving with the requested symbol
   * @param varSymbolNode the variable symbol node to resolve (node must be in the AST)
   */
  public VariableTypeResolver(SimpleName varSymbolNode) {
    this.symbol = varSymbolNode.getIdentifier();
    this.minScope = varSymbolNode;
  }
  
  public VariableTypeResolver(String varSymbol, ASTNode minScope) {
    this.symbol = varSymbol;
    this.minScope = minScope;
  }
  
  public VariableTypeResolver disableMethodLevel() {
    methodLevel = false;
    return this;
  }
  public VariableTypeResolver disableTypeLevel() {
    typeLevel = false;
    return this;
  }
  
  /**
   * Node's parent is instance of {@link VariableDeclarationFragment} or {@link SingleVariableDeclaration}
   * @return the SimpleName node of declaration
   */
  public SimpleName resolveDeclSimpleName() {
    if (!found()) {
      resolve();
    }
    return declSN;
  }
  
  private void resolve() {
    if(found()) {return;}
    
    if (methodLevel) {
      apply(FindUpper.methodScope(minScope));
    }

    if(found()) {return;}
    
    if (typeLevel) {
      AbstractTypeDeclaration typeScope = FindUpper.abstractTypeScope(minScope);
      applyInFields(typeScope);

      if(found()) {return;}
      
      for (TypeDeclaration superClass : superClasses(typeScope)) {
        if(found()) {return;}
        applyInFields(superClass);
      }
    }
  }
  
  private boolean found() {
    return declSN != null;
  }

  private void apply(ASTNode scope) {
    if (scope == null) {
      throw new NullPointerException();
    }
    scope.accept(visitor);
  }

  private void applyInFields(AbstractTypeDeclaration typeScope) {
    for (Object bd : typeScope.bodyDeclarations()) {
      if (bd instanceof FieldDeclaration) {
        apply((ASTNode) bd);
      }
    }
  }
  
  private List<TypeDeclaration> superClasses(AbstractTypeDeclaration atd) {
    if (atd instanceof TypeDeclaration) {
      return AstUtils.superClasses((TypeDeclaration) atd);
    }
    else {
      return Collections.EMPTY_LIST;
    }
  }
}

它依赖的AstUtils, FindUpper, SourcePathCollector设施都在Exia里面,配合使用即可。https://github.com/sorra/exia