Skip to content

Commit

Permalink
JVM language frontend based on SootUp
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Jun 13, 2024
1 parent e8a6f10 commit 497e3c5
Show file tree
Hide file tree
Showing 40 changed files with 1,655 additions and 3 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ out
.data/
logs
/lsp/*.log
*.class

*.dylib
*.so
Expand Down
6 changes: 6 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,9 @@ val enableRubyFrontend: Boolean by extra {
enableRubyFrontend.toBoolean()
}
project.logger.lifecycle("Ruby frontend is ${if (enableRubyFrontend) "enabled" else "disabled"}")

val enableJVMFrontend: Boolean by extra {
val enableJVMFrontend: String? by project
enableJVMFrontend.toBoolean()
}
project.logger.lifecycle("JVM frontend is ${if (enableJVMFrontend) "enabled" else "disabled"}")
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ val enablePythonFrontend: Boolean by rootProject.extra
val enableLLVMFrontend: Boolean by rootProject.extra
val enableTypeScriptFrontend: Boolean by rootProject.extra
val enableRubyFrontend: Boolean by rootProject.extra
val enableJVMFrontend: Boolean by rootProject.extra

dependencies {
if (enableJavaFrontend) {
api(project(":cpg-language-java"))
kover(project(":cpg-language-java"))
}
if (enableJVMFrontend) {
api(project(":cpg-language-jvm"))
kover(project(":cpg-language-jvm"))
}
if (enableCXXFrontend) {
api(project(":cpg-language-cxx"))
kover(project(":cpg-language-cxx"))
Expand Down
2 changes: 2 additions & 0 deletions configure_frontends.sh
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ answerTypescript=$(ask "Do you want to enable the TypeScript frontend? (currentl
setProperty "enableTypeScriptFrontend" $answerTypescript
answerRuby=$(ask "Do you want to enable the Ruby frontend? (currently $(getProperty "enableRubyFrontend"))")
setProperty "enableRubyFrontend" $answerRuby
answerJVM=$(ask "Do you want to enable the JVM frontend? (currently $(getProperty "enableJVMFrontend"))")
setProperty "enableJVMFrontend" $answerJVM
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,10 @@ val Node?.returns: List<ReturnStatement>
val Node?.assigns: List<AssignExpression>
get() = this.allChildren()

/** Returns all [ProblemNode] children in this graph, starting with this [Node]. */
val Node?.problems: List<ProblemNode>
get() = this.allChildren()

/** Returns all [Assignment] child edges in this graph, starting with this [Node]. */
val Node?.assignments: List<Assignment>
get() {
Expand Down
50 changes: 50 additions & 0 deletions cpg-language-jvm/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2021, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
plugins {
id("cpg.frontend-conventions")
}

publishing {
publications {
named<MavenPublication>("cpg-language-jvm") {
pom {
artifactId = "cpg-language-jvm"
name.set("Code Property Graph - JVM bytecode Frontend")
description.set("A JVM bytecode frontend for the CPG")
}
}
}
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions {
freeCompilerArgs = listOf("-Xcontext-receivers")
}
}

dependencies {
api(libs.bundles.sootup)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2024, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.frontends.jvm

import de.fraunhofer.aisec.cpg.frontends.Handler
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

class DeclarationHandler(frontend: JVMLanguageFrontend) :
Handler<Declaration, Any, JVMLanguageFrontend>(::ProblemDeclaration, frontend) {
init {
map.put(SootClass::class.java) { handleClass(it as SootClass) }
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) }
}

private fun handleClass(sootClass: SootClass): RecordDeclaration {
val record =
newRecordDeclaration(
sootClass.getName(),
if (sootClass.isInterface()) {
"interface"
} else {
"class"
},
rawNode = sootClass
)

// Collect super class
val o = sootClass.superclass
if (o.isPresent) {
record.addSuperClass(frontend.typeOf(o.get()))
}

// Collect implemented interfaces
for (i in sootClass.interfaces) {
record.implementedInterfaces += frontend.typeOf(i)
}

// Enter the class scope
frontend.scopeManager.enterScope(record)

// Loop through all fields
for (sootField in sootClass.fields) {
val field = handle(sootField)
frontend.scopeManager.addDeclaration(field)
}

// Loop through all methods
for (sootMethod in sootClass.methods) {
val method = handle(sootMethod)
frontend.scopeManager.addDeclaration(method)
}

// Leave the class scope
frontend.scopeManager.leaveScope(record)

return record
}

private fun handleMethod(sootMethod: SootMethod): MethodDeclaration {
val record = frontend.scopeManager.currentRecord

val method =
if (sootMethod.name == "<init>") {
newConstructorDeclaration(sootMethod.name, record, rawNode = sootMethod)
} else {
newMethodDeclaration(
sootMethod.name,
sootMethod.isStatic,
frontend.scopeManager.currentRecord,
rawNode = sootMethod,
)
}

// Enter method scope
frontend.scopeManager.enterScope(method)

// Add "@this" as the receiver
method.receiver =
newVariableDeclaration("@this", method.recordDeclaration?.toType() ?: unknownType())
.implicit("@this")
frontend.scopeManager.addDeclaration(method.receiver)

// Add method parameters
for ((index, type) in sootMethod.parameterTypes.withIndex()) {
val param = newParameterDeclaration("@parameter${index}", frontend.typeOf(type))
frontend.scopeManager.addDeclaration(param)
}

if (sootMethod.isConcrete) {
// Handle method body
method.body = frontend.statementHandler.handle(sootMethod.body)
}

// Leave method scope
frontend.scopeManager.leaveScope(method)

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)
}
}
Loading

0 comments on commit 497e3c5

Please sign in to comment.