为什么创建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. excel查找窗口被拉边上_你会做 Excel目录 吗?这个奇葩方法100%的人不知道
  2. pandas把多个列相加求和、输出字母a-z
  3. python课堂笔记
  4. 初探swift语言的学习笔记四-2(对上一节有些遗留进行处理)
  5. mysql添加外键的时候报错_mysql处理添加外键时 error 150 问题
  6. leetcode解题记录(一)
  7. asp 开发app_ASP.NET Core应用的错误处理[1]:三种呈现错误页面的方式
  8. C#泛型委托Predicate、Action、Func
  9. 程序员测试死循环给老板发了10000条短信……
  10. 算法----其他排序
  11. Vicdas实时历史数据库
  12. sqlserver连接问题圣经
  13. 网易音乐人2021年度盘点上线 揭晓2021年华语原创音乐年度记忆
  14. 【vue3】vue3+ts+vite项目设置路径别名
  15. android 背光控制,Android P背光策略分析(一)
  16. Keil的安装及使用
  17. 二零一五,谁偷走了我的青春
  18. 证券行业基础知识(二)
  19. 内容:提出含冰蓄冷空调的微电网多时间尺度优化调度模型,研究冰蓄冷空调 的不同运行方式对优化调度的影响
  20. RDP协议,remote desktop protocol,远程桌面协议

热门文章

  1. Oracle入门(十四D)之常规函数
  2. SpringCloud注册中心高可用搭建
  3. Spring Boot 发布 jar 包转为 war 包秘籍。
  4. Shell入门(九)之字符串比较
  5. 你不知道ADo.Net中操作数据库的步骤【超详细整理】
  6. PL/SQL编程基本概念
  7. wrapper 并集如何使用
  8. 转:运维监控系统-监控项及指标的梳理
  9. maven项目 报错 java.lang.ClassNotFoundException: org.springframework.web.filter.HiddenHttpMethodFilter
  10. java native关键字(java本地方法)