使用JavaSymbolSolver解决Java代码中的方法调用
為什么創(chuàng)建java-symbol-solver?
幾年前,我開始使用JavaParser ,然后開始做出貢獻(xiàn)。 不久之后,我意識到我們想對Java代碼執(zhí)行的許多操作不能僅通過使用解析器生成的抽象語法樹來完成,我們還需要解析類型,符號和方法調(diào)用。 因此,我創(chuàng)建了JavaSymbolSolver 。 現(xiàn)在, Coati已將其用于生產(chǎn)靜態(tài)分析工具。
缺少的一件事是文檔:人們在JavaParser上打開問題,詢問如何回答某個(gè)問題,而答案通常是“為此,您需要使用JavaSymbolSolver”。 從這些問題開始,我將展示一些示例。
受此問題的啟發(fā),我將展示如何生成對特定方法的所有調(diào)用的列表。
我們?nèi)绾问褂胘ava-symbol-solver解決Java中的方法調(diào)用?
可以分兩步完成:
我們將寫一個(gè)簡短的例子。 最后,我們將得到一個(gè)應(yīng)用程序,給定源文件將產(chǎn)生以下結(jié)果:
* 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()在此源文件上執(zhí)行時(shí):
/** 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;} }設(shè)置項(xiàng)目
我們將使用Kotlin和Gradle。 我們的構(gòu)建文件如下所示:
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非常容易,您只需調(diào)用此方法:
JavaParser.parse(file)我還使用了幾種方法來導(dǎo)航AST并獲取特定的節(jié)點(diǎn)。 特別是,我將使用它僅接受方法調(diào)用。 如果您有興趣,他們看起來像這樣:
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 }指定類型求解器
什么是類型求解器? 它是知道在哪里查找類的對象。 在處理源代碼時(shí),通常會引用尚未編譯的代碼,但這些代碼僅存在于其他源文件中。 您還可以使用JAR中包含的類或Java標(biāo)準(zhǔn)庫中的類。 您只需要告訴TypeSolver在哪里尋找類,它就會弄清楚。
在我們的示例中,我們將解析JavaParser項(xiàng)目的源代碼(如何轉(zhuǎn)換成meta ?!)。 該項(xiàng)目的源代碼位于兩個(gè)不同的目錄中,以獲取正確的源代碼和JavaCC生成的代碼(您可以忽略JavaCC是什么,它與您無關(guān))。 我們當(dāng)然也使用來自Java標(biāo)準(zhǔn)庫的類。 這就是我們的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 }我們的應(yīng)用
這是我們調(diào)用JavaParserFacade的地方,JavaParserFacade是JavaSymbolSolver提供的類之一。 當(dāng)時(shí)我們只接受一個(gè)方法調(diào)用,然后將其傳遞給JavaParserFacade的方法求解 。 我們得到一個(gè)MethodUsage(基本上是一個(gè)方法聲明+該特定調(diào)用的參數(shù)類型的值)。 從中,我們獲得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()}} }結(jié)論
有很多事情要做,但是基本上JavaSymbolSolver會在后臺完成所有繁重的工作。 一旦有了AST的節(jié)點(diǎn),就可以將其扔到JavaParserFacade類上,它將為您提供可能需要的所有信息:它將找到相應(yīng)的類型,字段,方法等。
問題是……我們需要更多文檔和用戶反饋。 我希望你們中的一些人將開始使用JavaSymbolSolver并告訴我們?nèi)绾胃倪M(jìn)它。
同樣,上周JavaSymbolSolver被移至JavaParser組織之下。 這意味著將來我們將與JavaParser項(xiàng)目更加緊密地合作。
該代碼可在GitHub上找到: java-symbol-solver-examples
翻譯自: https://www.javacodegeeks.com/2016/11/resolve-method-calls-java-code-using-javasymbolsolver.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的使用JavaSymbolSolver解决Java代码中的方法调用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1一12月份的英文缩写 1一12月份的英
- 下一篇: 苹果锁屏密码忘了怎么解开手机