diff --git a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/DeclarationHandler.kt b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/DeclarationHandler.kt index ace239b169..87becf8749 100644 --- a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/DeclarationHandler.kt +++ b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/DeclarationHandler.kt @@ -29,8 +29,10 @@ import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.* import sootup.core.jimple.basic.Local import sootup.core.model.SootClass +import sootup.core.model.SootField import sootup.core.model.SootMethod import sootup.java.core.JavaSootClass +import sootup.java.core.JavaSootField import sootup.java.core.JavaSootMethod import sootup.java.core.jimple.basic.JavaLocal @@ -41,6 +43,8 @@ class DeclarationHandler(frontend: JVMLanguageFrontend) : map.put(JavaSootClass::class.java) { handleClass(it as SootClass<*>) } map.put(SootMethod::class.java) { handleMethod(it as SootMethod) } map.put(JavaSootMethod::class.java) { handleMethod(it as SootMethod) } + map.put(SootField::class.java) { handleField(it as SootField) } + map.put(JavaSootField::class.java) { handleField(it as SootField) } map.put(Local::class.java) { handleLocal(it as Local) } map.put(JavaLocal::class.java) { handleLocal(it as Local) } } @@ -51,6 +55,12 @@ class DeclarationHandler(frontend: JVMLanguageFrontend) : // Enter the class scope frontend.scopeManager.enterScope(record) + // Loop through all fields + for (sootField in sootClass.getFields()) { + val field = handle(sootField) + frontend.scopeManager.addDeclaration(field) + } + // Loop through all methods for (sootMethod in sootClass.getMethods()) { val method = handle(sootMethod) @@ -102,6 +112,15 @@ class DeclarationHandler(frontend: JVMLanguageFrontend) : return method } + fun handleField(field: SootField): FieldDeclaration { + return newFieldDeclaration( + field.name, + frontend.typeOf(field.type), + field.modifiers.map { it.name.lowercase() }, + rawNode = field + ) + } + private fun handleLocal(local: Local): VariableDeclaration { return newVariableDeclaration(local.name, frontend.typeOf(local.type), rawNode = local) } diff --git a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ExpressionHandler.kt b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ExpressionHandler.kt index e402f47c28..b0b3d03105 100644 --- a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ExpressionHandler.kt +++ b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ExpressionHandler.kt @@ -40,10 +40,12 @@ import sootup.core.jimple.common.expr.JNewExpr import sootup.core.jimple.common.expr.JSpecialInvokeExpr import sootup.core.jimple.common.expr.JStaticInvokeExpr import sootup.core.jimple.common.expr.JVirtualInvokeExpr +import sootup.core.jimple.common.ref.JInstanceFieldRef import sootup.core.jimple.common.ref.JParameterRef import sootup.core.jimple.common.ref.JStaticFieldRef import sootup.core.jimple.common.ref.JThisRef import sootup.core.signatures.MethodSignature +import sootup.core.signatures.SootClassMemberSignature import sootup.java.core.jimple.basic.JavaLocal class ExpressionHandler(frontend: JVMLanguageFrontend) : @@ -57,6 +59,7 @@ class ExpressionHandler(frontend: JVMLanguageFrontend) : map.put(JavaLocal::class.java) { handleLocal(it as Local) } map.put(JThisRef::class.java) { handleThisRef(it as JThisRef) } map.put(JParameterRef::class.java) { handleParameterRef(it as JParameterRef) } + map.put(JInstanceFieldRef::class.java) { handleInstanceFieldRef(it as JInstanceFieldRef) } map.put(JStaticFieldRef::class.java) { handleStaticFieldRef(it as JStaticFieldRef) } map.put(JVirtualInvokeExpr::class.java) { handleVirtualInvokeExpr(it as JVirtualInvokeExpr) @@ -80,7 +83,7 @@ class ExpressionHandler(frontend: JVMLanguageFrontend) : lit } else { - val ref = newReference(local.name, rawNode = local) + val ref = newReference(local.name, frontend.typeOf(local.type), rawNode = local) ref } @@ -103,24 +106,24 @@ class ExpressionHandler(frontend: JVMLanguageFrontend) : return ref } - private fun handleStaticFieldRef(staticFieldRef: JStaticFieldRef): Reference { - // TODO(oxisto): not sure if this shouldn't be a regular reference instead - val base = - newReference( - staticFieldRef.fieldSignature.declClassType.fullyQualifiedName, - frontend.typeOf(staticFieldRef.fieldSignature.declClassType) - ) + private fun handleInstanceFieldRef(instanceFieldRef: JInstanceFieldRef): Reference { + val base = handle(instanceFieldRef.base) ?: newProblemExpression("missing base") - val expr = + val ref = newMemberExpression( - staticFieldRef.fieldSignature.name, + instanceFieldRef.fieldSignature.name, base, - frontend.typeOf(staticFieldRef.type), - rawNode = staticFieldRef + frontend.typeOf(instanceFieldRef.fieldSignature.type), + rawNode = instanceFieldRef ) - expr.isStaticAccess = true - return expr + return ref + } + + private fun handleStaticFieldRef(staticFieldRef: JStaticFieldRef): Reference { + val ref = staticFieldRef.fieldSignature.toStaticRef() + + return ref } private fun handleVirtualInvokeExpr( @@ -205,7 +208,7 @@ class ExpressionHandler(frontend: JVMLanguageFrontend) : private fun MethodSignature.toStaticRef(): Reference { // First, construct the name using . - val ref = newReference("${this.declClassType.fullyQualifiedName}.${this.name}") + val ref = (this as SootClassMemberSignature<*>).toStaticRef() // We can also provide a function type, since these are all statically known. This might // help in inferring some (unknown) functions later @@ -217,6 +220,13 @@ class ExpressionHandler(frontend: JVMLanguageFrontend) : frontend.language ) + return ref + } + + private fun SootClassMemberSignature<*>.toStaticRef(): Reference { + // First, construct the name using . + val ref = newReference("${this.declClassType.fullyQualifiedName}.${this.name}") + // Make it static ref.isStaticAccess = true diff --git a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguage.kt b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguage.kt index 1aedfba4e8..756263a88f 100644 --- a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguage.kt +++ b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguage.kt @@ -30,7 +30,7 @@ import kotlin.reflect.KClass class JVMLanguage : Language() { override val fileExtensions: List - get() = listOf("class", "jimple") + get() = listOf("class", "java", "jimple") override val namespaceDelimiter: String get() = "." diff --git a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguageFrontend.kt b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguageFrontend.kt index 4970e0d58a..e085184dd8 100644 --- a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguageFrontend.kt +++ b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguageFrontend.kt @@ -40,6 +40,7 @@ import sootup.core.types.UnknownType import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation import sootup.java.core.JavaSootClass import sootup.java.core.views.JavaView +import sootup.java.sourcecode.inputlocation.JavaSourcePathAnalysisInputLocation typealias SootType = sootup.core.types.Type @@ -73,9 +74,18 @@ class JVMLanguageFrontend( val project = JavaProject.builder(language).addInputLocation(inputLocation).build() project.createView() }*/ - val inputLocation: AnalysisInputLocation = - JavaClassPathAnalysisInputLocation(ctx.config.topLevel!!.path) - val view = JavaView(inputLocation) + val view = + if (file.extension == "class") { + val inputLocation: AnalysisInputLocation = + JavaClassPathAnalysisInputLocation(ctx.config.topLevel!!.path) + JavaView(inputLocation) + } else if (file.extension == "java") { + val inputLocation: AnalysisInputLocation = + JavaSourcePathAnalysisInputLocation(ctx.config.topLevel!!.path) + JavaView(inputLocation) + } else { + throw TranslationException("unsupported file") + } // This contains the whole directory val tu = newTranslationUnitDeclaration(file.parent) diff --git a/cpg-language-jvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguageFrontendTest.kt b/cpg-language-jvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguageFrontendTest.kt index 722e43bcde..c2fca92113 100644 --- a/cpg-language-jvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguageFrontendTest.kt +++ b/cpg-language-jvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/JVMLanguageFrontendTest.kt @@ -32,6 +32,7 @@ import de.fraunhofer.aisec.cpg.assertLocalName import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberCallExpression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression import de.fraunhofer.aisec.cpg.passes.EdgeCachePass import de.fraunhofer.aisec.cpg.passes.astParent import java.nio.file.Path @@ -147,12 +148,12 @@ class JVMLanguageFrontendTest { } @Test - fun testLiterals() { + fun testLiteralsClass() { // This will be our classpath val topLevel = Path.of("src", "test", "resources", "class", "literals") val tu = TestUtils.analyzeAndGetFirstTU( - // We just need to specify one file to trigger the class byte loader + // We just need to specify one file to trigger the byte code loader listOf(topLevel.resolve("mypackage/Literals.class").toFile()), topLevel, true @@ -167,4 +168,56 @@ class JVMLanguageFrontendTest { println(haveFun.code) } + + @Test + fun testFieldsClass() { + // This will be our classpath + val topLevel = Path.of("src", "test", "resources", "class", "fields") + val tu = + TestUtils.analyzeAndGetFirstTU( + // We just need to specify one file to trigger the byte code loader + listOf(topLevel.resolve("mypackage/Fields.class").toFile()), + topLevel, + true + ) { + it.registerPass() + it.registerLanguage() + } + assertNotNull(tu) + + tu.methods.forEach { println(it.code) } + + val refs = tu.refs.filterIsInstance() + refs.forEach { + val refersTo = it.refersTo + assertNotNull(refersTo, "${it.name} could not be resolved") + assertFalse( + refersTo.isInferred, + "${it.name} should not be resolved to an inferred node" + ) + } + } + + @Disabled + @Test + fun testLiteralsSource() { + // This will be our classpath + val topLevel = Path.of("src", "test", "resources", "class", "literals") + val tu = + TestUtils.analyzeAndGetFirstTU( + // We just need to specify one file to trigger the source code loader + listOf(topLevel.resolve("mypackage/Literals.java").toFile()), + topLevel, + true + ) { + it.registerPass() + it.registerLanguage() + } + assertNotNull(tu) + + val haveFun = tu.methods["haveFunWithLiterals"] + assertNotNull(haveFun) + + println(haveFun.code) + } } diff --git a/cpg-language-jvm/src/test/resources/class/fields/mypackge/Fields.java b/cpg-language-jvm/src/test/resources/class/fields/mypackge/Fields.java new file mode 100644 index 0000000000..3fdd6760bb --- /dev/null +++ b/cpg-language-jvm/src/test/resources/class/fields/mypackge/Fields.java @@ -0,0 +1,18 @@ +package mypackage; + +public class Fields { + + private int a = 2; + + Fields() { + resetA(); + } + + public void setA(int a) { + this.a = a; + } + + private void resetA() { + this.a = 10; + } +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 69d185470f..ab426c03b8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,13 +46,14 @@ nexus-publish-gradle = { module = "io.github.gradle-nexus:publish-plugin", versi sootup-core = { module = "com.github.oxisto.SootUp:sootup.core", version.ref = "sootup" } sootup-java-core = { module = "com.github.oxisto.SootUp:sootup.java.core", version.ref = "sootup" } +sootup-java-sourcecode = { module = "com.github.oxisto.SootUp:sootup.java.sourcecode", version.ref = "sootup" } sootup-java-bytecode = { module = "com.github.oxisto.SootUp:sootup.java.bytecode", version.ref = "sootup" } sootup-jimple-parser = { module = "com.github.oxisto.SootUp:sootup.jimple.parser", version.ref = "sootup" } [bundles] log4j = ["log4j-impl", "log4j-core"] neo4j = ["neo4j-ogm-core", "neo4j-ogm-bolt-driver"] -sootup = ["sootup-core", "sootup-java-core", "sootup-java-bytecode", "sootup-jimple-parser"] +sootup = ["sootup-core", "sootup-java-core", "sootup-java-sourcecode", "sootup-java-bytecode", "sootup-jimple-parser"] [plugins] kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin"}