为什么创建java-symbol-solver?

几年前,我开始使用JavaParser ,然后开始做出贡献。 不久之后,我意识到我们想对Java代码执行的许多操作不能仅通过使用解析器生成的抽象语法树来完成,我们还需要解析类型,符号和方法调用。 因此,我创建了JavaSymbolSolver 。 现在, Coati已将其用于生产静态分析工具。

缺少的一件事是文档:人们在JavaParser上打开问题,询问如何回答某个问题,而答案通常是“为此,您需要使用JavaSymbolSolver”。 从这些问题开始,我将展示一些示例。

受此问题的启发,我将展示如何生成对特定方法的所有调用的列表。

我们如何使用java-symbol-solver解决Java中的方法调用?

可以分两步完成:

  1. 您在源代码上使用JavaParser来构建AST
  2. 您在表示方法调用的AST的节点上调用JavaSymbolSolver并获得答案

我们将写一个简短的例子。 最后,我们将得到一个应用程序,给定源文件将产生以下结果:

* L55 setId(id)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setId(com.github.javaparser.ast.body.VariableDeclaratorId)* L59 setId(new VariableDeclaratorId(variableName))   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setId(com.github.javaparser.ast.body.VariableDeclaratorId)* L71 setId(id)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setId(com.github.javaparser.ast.body.VariableDeclaratorId)* L72 setInit(init)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setInit(com.github.javaparser.ast.expr.Expression)* L76 setId(new VariableDeclaratorId(variableName))   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setId(com.github.javaparser.ast.body.VariableDeclaratorId)* L77 setInit(init)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setInit(com.github.javaparser.ast.expr.Expression)* L82 setId(id)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setId(com.github.javaparser.ast.body.VariableDeclaratorId)* L83 setInit(init)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setInit(com.github.javaparser.ast.expr.Expression)* L88 v.visit(this, arg)   -&qt; com.github.javaparser.ast.visitor.GenericVisitor.visit(com.github.javaparser.ast.body.VariableDeclarator, A)* L93 v.visit(this, arg)   -&qt; com.github.javaparser.ast.visitor.VoidVisitor.visit(com.github.javaparser.ast.body.VariableDeclarator, A)* L106 setAsParentNodeOf(this.id)   -&qt; com.github.javaparser.ast.Node.setAsParentNodeOf(com.github.javaparser.ast.Node)* L112 setAsParentNodeOf(this.init)   -&qt; com.github.javaparser.ast.Node.setAsParentNodeOf(com.github.javaparser.ast.Node)* L121 setAsParentNodeOf(this.init)   -&qt; com.github.javaparser.ast.Node.setAsParentNodeOf(com.github.javaparser.ast.Node)* L128 getParentNodeOfType(NodeWithElementType.class)   -&qt; com.github.javaparser.ast.Node.getParentNodeOfType(java.lang.Class<T&qt;)* L130 wrapInArrayTypes(elementType.getElementType(), elementType.getArrayBracketPairsAfterElementType(), getId().getArrayBracketPairsAfterId())   -&qt; com.github.javaparser.ast.type.ArrayType.wrapInArrayTypes(com.github.javaparser.ast.type.Type, java.util.List<com.github.javaparser.ast.ArrayBracketPair&qt;...)* L130 elementType.getElementType()   -&qt; com.github.javaparser.ast.nodeTypes.NodeWithElementType.getElementType()* L131 elementType.getArrayBracketPairsAfterElementType()   -&qt; com.github.javaparser.ast.nodeTypes.NodeWithElementType.getArrayBracketPairsAfterElementType()* L132 getId().getArrayBracketPairsAfterId()   -&qt; com.github.javaparser.ast.body.VariableDeclaratorId.getArrayBracketPairsAfterId()* L132 getId()   -&qt; com.github.javaparser.ast.body.VariableDeclarator.getId()* L137 ArrayType.unwrapArrayTypes(type)   -&qt; com.github.javaparser.ast.type.ArrayType.unwrapArrayTypes(com.github.javaparser.ast.type.Type)* L138 getParentNodeOfType(NodeWithElementType.class)   -&qt; com.github.javaparser.ast.Node.getParentNodeOfType(java.lang.Class<T&qt;)* L142 nodeWithElementType.setElementType(unwrapped.a)   -&qt; com.github.javaparser.ast.nodeTypes.NodeWithElementType.setElementType(com.github.javaparser.ast.type.Type<?&qt;)* L143 nodeWithElementType.setArrayBracketPairsAfterElementType(null)   -&qt; com.github.javaparser.ast.nodeTypes.NodeWithElementType.setArrayBracketPairsAfterElementType(java.util.List<com.github.javaparser.ast.ArrayBracketPair&qt;)* L144 getId().setArrayBracketPairsAfterId(unwrapped.b)   -&qt; com.github.javaparser.ast.body.VariableDeclaratorId.setArrayBracketPairsAfterId(java.util.List<com.github.javaparser.ast.ArrayBracketPair&qt;)* L144 getId()   -&qt; com.github.javaparser.ast.body.VariableDeclarator.getId()

在此源文件上执行时:

/** Copyright (C) 2007-2010 J?lio Vilmar Gesser.* Copyright (C) 2011, 2013-2016 The JavaParser Team.** This file is part of JavaParser.* * JavaParser can be used either under the terms of* a) the GNU Lesser General Public License as published by*     the Free Software Foundation, either version 3 of the License, or*     (at your option) any later version.* b) the terms of the Apache License ** You should have received a copy of both licenses in LICENCE.LGPL and* LICENCE.APACHE. Please refer to those files for details.** JavaParser is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the* GNU Lesser General Public License for more details.*/package com.github.javaparser.ast.body;import com.github.javaparser.Range;
import com.github.javaparser.ast.ArrayBracketPair;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.nodeTypes.NodeWithElementType;
import com.github.javaparser.ast.nodeTypes.NodeWithType;
import com.github.javaparser.ast.type.ArrayType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.visitor.GenericVisitor;
import com.github.javaparser.ast.visitor.VoidVisitor;
import com.github.javaparser.utils.Pair;import java.util.List;import static com.github.javaparser.ast.type.ArrayType.wrapInArrayTypes;/*** @author Julio Vilmar Gesser*/
public final class VariableDeclarator extends Node implementsNodeWithType<VariableDeclarator&qt; {private VariableDeclaratorId id;private Expression init;public VariableDeclarator() {}public VariableDeclarator(VariableDeclaratorId id) {setId(id);}public VariableDeclarator(String variableName) {setId(new VariableDeclaratorId(variableName));}/*** Defines the declaration of a variable.* * @param id The identifier for this variable. IE. The variables name.* @param init What this variable should be initialized to.*            An {@link com.github.javaparser.ast.expr.AssignExpr} is unnecessary as the <code&qt;=</code&qt; operator is*            already added.*/public VariableDeclarator(VariableDeclaratorId id, Expression init) {setId(id);setInit(init);}public VariableDeclarator(String variableName, Expression init) {setId(new VariableDeclaratorId(variableName));setInit(init);}public VariableDeclarator(Range range, VariableDeclaratorId id, Expression init) {super(range);setId(id);setInit(init);}@Overridepublic <R, A&qt; R accept(GenericVisitor<R, A&qt; v, A arg) {return v.visit(this, arg);}@Overridepublic <A&qt; void accept(VoidVisitor<A&qt; v, A arg) {v.visit(this, arg);}public VariableDeclaratorId getId() {return id;}public Expression getInit() {return init;}public VariableDeclarator setId(VariableDeclaratorId id) {this.id = id;setAsParentNodeOf(this.id);return this;}public VariableDeclarator setInit(Expression init) {this.init = init;setAsParentNodeOf(this.init);return this;}/*** Will create a {@link NameExpr} with the init param*/public VariableDeclarator setInit(String init) {this.init = new NameExpr(init);setAsParentNodeOf(this.init);return this;}@Overridepublic Type getType() {NodeWithElementType<?&qt; elementType = getParentNodeOfType(NodeWithElementType.class);return wrapInArrayTypes(elementType.getElementType(),elementType.getArrayBracketPairsAfterElementType(),getId().getArrayBracketPairsAfterId());}@Overridepublic VariableDeclarator setType(Type type) {Pair<Type, List<ArrayBracketPair&qt;&qt; unwrapped = ArrayType.unwrapArrayTypes(type);NodeWithElementType<?&qt; nodeWithElementType = getParentNodeOfType(NodeWithElementType.class);if (nodeWithElementType == null) {throw new IllegalStateException("Cannot set type without a parent");}nodeWithElementType.setElementType(unwrapped.a);nodeWithElementType.setArrayBracketPairsAfterElementType(null);getId().setArrayBracketPairsAfterId(unwrapped.b);return this;}
}

设置项目

我们将使用Kotlin和Gradle。 我们的构建文件如下所示:

buildscript {ext.kotlin_version = '1.0.4'repositories {mavenCentral()maven {name 'JFrog OSS snapshot repo'url  'https://oss.jfrog.org/oss-snapshot-local/'}jcenter()}dependencies {classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"}
}apply plugin: 'kotlin'
apply plugin: 'application'
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'antlr'repositories {mavenLocal()mavenCentral()jcenter()
}dependencies {compile "me.tomassetti:java-symbol-solver-core:0.3.1"compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"testCompile "junit:junit:latest.release"
}idea {module {excludeDirs += file('src/main/resources')}
}* L55 setId(id)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setId(com.github.javaparser.ast.body.VariableDeclaratorId)* L59 setId(new VariableDeclaratorId(variableName))   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setId(com.github.javaparser.ast.body.VariableDeclaratorId)* L71 setId(id)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setId(com.github.javaparser.ast.body.VariableDeclaratorId)* L72 setInit(init)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setInit(com.github.javaparser.ast.expr.Expression)* L76 setId(new VariableDeclaratorId(variableName))   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setId(com.github.javaparser.ast.body.VariableDeclaratorId)* L77 setInit(init)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setInit(com.github.javaparser.ast.expr.Expression)* L82 setId(id)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setId(com.github.javaparser.ast.body.VariableDeclaratorId)* L83 setInit(init)   -&qt; com.github.javaparser.ast.body.VariableDeclarator.setInit(com.github.javaparser.ast.expr.Expression)* L88 v.visit(this, arg)   -&qt; com.github.javaparser.ast.visitor.GenericVisitor.visit(com.github.javaparser.ast.body.VariableDeclarator, A)* L93 v.visit(this, arg)   -&qt; com.github.javaparser.ast.visitor.VoidVisitor.visit(com.github.javaparser.ast.body.VariableDeclarator, A)* L106 setAsParentNodeOf(this.id)   -&qt; com.github.javaparser.ast.Node.setAsParentNodeOf(com.github.javaparser.ast.Node)* L112 setAsParentNodeOf(this.init)   -&qt; com.github.javaparser.ast.Node.setAsParentNodeOf(com.github.javaparser.ast.Node)* L121 setAsParentNodeOf(this.init)   -&qt; com.github.javaparser.ast.Node.setAsParentNodeOf(com.github.javaparser.ast.Node)* L128 getParentNodeOfType(NodeWithElementType.class)   -&qt; com.github.javaparser.ast.Node.getParentNodeOfType(java.lang.Class<T&qt;)* L130 wrapInArrayTypes(elementType.getElementType(), elementType.getArrayBracketPairsAfterElementType(), getId().getArrayBracketPairsAfterId())   -&qt; com.github.javaparser.ast.type.ArrayType.wrapInArrayTypes(com.github.javaparser.ast.type.Type, java.util.List<com.github.javaparser.ast.ArrayBracketPair&qt;...)* L130 elementType.getElementType()   -&qt; com.github.javaparser.ast.nodeTypes.NodeWithElementType.getElementType()* L131 elementType.getArrayBracketPairsAfterElementType()   -&qt; com.github.javaparser.ast.nodeTypes.NodeWithElementType.getArrayBracketPairsAfterElementType()* L132 getId().getArrayBracketPairsAfterId()   -&qt; com.github.javaparser.ast.body.VariableDeclaratorId.getArrayBracketPairsAfterId()* L132 getId()   -&qt; com.github.javaparser.ast.body.VariableDeclarator.getId()* L137 ArrayType.unwrapArrayTypes(type)   -&qt; com.github.javaparser.ast.type.ArrayType.unwrapArrayTypes(com.github.javaparser.ast.type.Type)* L138 getParentNodeOfType(NodeWithElementType.class)   -&qt; com.github.javaparser.ast.Node.getParentNodeOfType(java.lang.Class<T&qt;)* L142 nodeWithElementType.setElementType(unwrapped.a)   -&qt; com.github.javaparser.ast.nodeTypes.NodeWithElementType.setElementType(com.github.javaparser.ast.type.Type<?&qt;)* L143 nodeWithElementType.setArrayBracketPairsAfterElementType(null)   -&qt; com.github.javaparser.ast.nodeTypes.NodeWithElementType.setArrayBracketPairsAfterElementType(java.util.List<com.github.javaparser.ast.ArrayBracketPair&qt;)* L144 getId().setArrayBracketPairsAfterId(unwrapped.b)   -&qt; com.github.javaparser.ast.body.VariableDeclaratorId.setArrayBracketPairsAfterId(java.util.List<com.github.javaparser.ast.ArrayBracketPair&qt;)* L144 getId()   -&qt; com.github.javaparser.ast.body.VariableDeclarator.getId()

建立AST

建立AST非常容易,您只需调用此方法:

JavaParser.parse(file)

我还使用了几种方法来导航AST并获取特定的节点。 特别是,我将使用它仅接受方法调用。 如果您有兴趣,他们看起来像这样:

class SpecificNodeIterator<T&qt;(private val type: Class<T&qt;, private val nodeHandler: SpecificNodeIterator.NodeHandler<T&qt;) {interface NodeHandler<T&qt; {fun handle(node: T): Boolean}fun explore(node: Node) {if (type.isInstance(node)) {if (!nodeHandler.handle(type.cast(node))) {return}}for (child in node.childrenNodes) {explore(child)}}
}// this is a method extension: we had this method to the existing class "Node"
fun <T&qt; Node.descendantsOfType(type: Class<T&qt;) : List<T&qt; {val descendants = LinkedList<T&qt;()SpecificNodeIterator(type, object : SpecificNodeIterator.NodeHandler<T&qt; {override fun handle(node: T): Boolean {descendants.add(node)return true}}).explore(this)return descendants
}

指定类型求解器

什么是类型求解器? 它是知道在哪里查找类的对象。 在处理源代码时,通常会引用尚未编译的代码,但这些代码仅存在于其他源文件中。 您还可以使用JAR中包含的类或Java标准库中的类。 您只需要告诉TypeSolver在哪里寻找类,它就会弄清楚。

在我们的示例中,我们将解析JavaParser项目的源代码(如何转换成meta ?!)。 该项目的源代码位于两个不同的目录中,以获取正确的源代码和JavaCC生成的代码(您可以忽略JavaCC是什么,它与您无关)。 我们当然也使用来自Java标准库的类。 这就是我们的TypeSolver的样子:

fun typeSolver() : TypeSolver {val combinedTypeSolver = CombinedTypeSolver()combinedTypeSolver.add(JreTypeSolver())combinedTypeSolver.add(JavaParserTypeSolver(File("src/main/resources/javaparser-core")))combinedTypeSolver.add(JavaParserTypeSolver(File("src/main/resources/javaparser-generated-sources")))return combinedTypeSolver
}

我们的应用

这是我们调用JavaParserFacade的地方,JavaParserFacade是JavaSymbolSolver提供的类之一。 当时我们只接受一个方法调用,然后将其传递给JavaParserFacade的方法求解 。 我们得到一个MethodUsage(基本上是一个方法声明+该特定调用的参数类型的值)。 从中,我们获得MethodDeclaration,然后打印合格的签名,即类的合格名称,后跟方法的签名。 这是我们获得最终输出的方式:

var solved = 0
var unsolved = 0
var errors = 0fun processJavaFile(file: File, javaParserFacade: JavaParserFacade) {println(file)JavaParser.parse(file).descendantsOfType(MethodCallExpr::class.java).forEach {print(" * L${it.begin.line} $it ")try {val methodRef = javaParserFacade.solve(it)if (methodRef.isSolved) {solved++val methodDecl = methodRef.correspondingDeclarationprintln("  -> ${methodDecl.qualifiedSignature}")} else {unsolved++println(" ???")}} catch (e: Exception) {println(" ERR ${e.message}")errors++} catch (t: Throwable) {t.printStackTrace()}}
}

结论

有很多事情要做,但是基本上JavaSymbolSolver会在后台完成所有繁重的工作。 一旦有了AST的节点,就可以将其扔到JavaParserFacade类上,它将为您提供可能需要的所有信息:它将找到相应的类型,字段,方法等。

问题是……我们需要更多文档和用户反馈。 我希望你们中的一些人将开始使用JavaSymbolSolver并告诉我们如何改进它。

同样,上周JavaSymbolSolver被移至JavaParser组织之下。 这意味着将来我们将与JavaParser项目更加紧密地合作。

该代码可在GitHub上找到: java-symbol-solver-examples

翻译自: https://www.javacodegeeks.com/2016/11/resolve-method-calls-java-code-using-javasymbolsolver.html

使用JavaSymbolSolver解决Java代码中的方法调用相关推荐

  1. 关于Java代码中一个方法代码超出65535字节

    背景:在写代码的过程中有一个非常非常复杂的方法,里面很多的业务逻辑,经过了很多代人的修改,有需要有需要对他进行修改,结果在修改的时候报错,方法达到65535字节的限制,如下图所示: 由于java使用U ...

  2. Android开发之 当前日期String类型转date类型 java代码中实现方法

    2019独角兽企业重金招聘Python工程师标准>>> /*** 获取当前时间* * @return*/public Date getDate(String str) {try {j ...

  3. 你还在 Java 代码中写 set/get 方法?赶快试试这款插件吧!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:Mr.ml https://blog.csdn.net/Ma ...

  4. idea自动生成get set_CTO:不要在Java代码中写set/get方法了,逮一次罚款

    前言 what?你的 Java 代码中还充斥着大量的 set/get 方法? 我们在刚开始学习 Java 语言的时候讲过,面向对象的三大特征就是封装,继承,和多态.在 Java 中,要保证封装性,需要 ...

  5. java中getup用法_你还在 Java 代码中写 set/get 方法?赶快试试这款插件吧!

    前言 what?你的 Java 代码中还充斥着大量的 set/get 方法? 我们在刚开始学习 Java 语言的时候讲过,面向对象的三大特征就是封装,继承,和多态.在 Java 中,要保证封装性,需要 ...

  6. 一次性解决Java程序中的乱码问题

    java在字符串中统一用Unicode表示. 对于任意一个字符串:String string = "测试字符串"; 如果源文件是GBK编码,操作系统默认环境编码也为GBK,那么编译 ...

  7. thymeleaf 调用java,thymeleaf模板引擎调用java类中的方法(附源码)

    前言 由于开源了项目的缘故,很多使用了My Blog项目的朋友遇到问题也都会联系我去解决,有的是把问题留在项目的issue里提出,有的是在我的私人博客里留言,还有的则是直接添加我的qq来找我讲自己遇到 ...

  8. java代码中fastjson生成字符串和解析字符串的方法和javascript文件中字符串和json数组之间的转换方法...

    1.java代码中fastjson生成字符串和解析字符串的方法 List<TemplateFull> templateFulls = new ArrayList<TemplateFu ...

  9. 如何在android的XML和java代码中引用字符串常量

    使用字符串(string)资源        在一个Android工程中,我们可能会使用到大量的字符串作为提示信息.这些字符串都可以作为字符串资源声明在配置文件中,从而实现程序的可配置性. 在代码中我 ...

最新文章

  1. 没有精准定位,万物还能实现互联吗?
  2. pyhon滤镜详细教程
  3. Codeforces681D Gifts by the List
  4. [裴礼文数学分析中的典型问题与方法习题参考解答]5.1.27
  5. Array的sort() 方法
  6. 条款七 为多态基类声明virtual析构函数
  7. Python爬虫-利用代理IP访问网页(requests)
  8. redis专题:redis的主从、哨兵、集群架构的配置和部署详情、以及问题分析
  9. java 基础学习——基本技巧(一)
  10. Linux下安装Win10ARM,更多安卓旗舰机将可以安装运行Win10 ARM
  11. mysql将日期转换年份_mysql将日期转换为当前年份的相同日期
  12. unity4.6 failed to update unity web player
  13. Unity 中文不显示问题
  14. Oracle的diag文件可以删除,oracle11g rac diag/tnslsnr/pgis2/listener/alert 中的文件能删除吗...
  15. 兼容西门子 CPU226IE量产方案
  16. SaaS微信小程序电商系统,一键生成小程序【源码分享】
  17. 计算机报名验证码不出现怎么办,电脑显示验证码很慢或验证码显示不出来怎么办...
  18. Block-scoped declarations (let, const, function, class) not yet supported outs报错解决
  19. 置换密码及其python实现
  20. node重绘图片_使用nodejs生成图片的尝试

热门文章

  1. mybatis入门(五)之Java API
  2. Oracle入门(十三A2)之单行函数
  3. Oracle入门(十二E)之视图操作
  4. Java 必看的 Spring 知识汇总
  5. java实现遍历树形菜单方法——OpenSessionView实现
  6. 阿里云服务器本地连接(windows) 阿里云服务器和本地的磁盘共享数据
  7. mysql sample函数_Oracle SAMPLE 语法应用
  8. hashmap应用场景_工作中常用到的Java集合有哪些?应用场景是什么?
  9. 转:centos8开启防火墙端口
  10. java 程序的初始化顺序是怎样的?