Skip to content

Commit

Permalink
Added basic inheritence test. Asserts are still missing
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Jan 3, 2024
1 parent a83714b commit c6bd4fc
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ package de.fraunhofer.aisec.cpg.frontends
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.graph.types.FunctionType
import de.fraunhofer.aisec.cpg.passes.SymbolResolver
import sootup.core.jimple.basic.Local
import sootup.core.jimple.basic.Value
import sootup.core.jimple.common.constant.*
Expand All @@ -54,6 +55,9 @@ class ExpressionHandler(frontend: JVMLanguageFrontend) :
map.put(JVirtualInvokeExpr::class.java) {
handleVirtualInvokeExpr(it as JVirtualInvokeExpr)
}
map.put(JDynamicInvokeExpr::class.java) {
handleAbstractInvokeExpr(it as JDynamicInvokeExpr)
}
map.put(JSpecialInvokeExpr::class.java) { handleSpecialInvoke(it as JSpecialInvokeExpr) }
map.put(JStaticInvokeExpr::class.java) { handleStaticInvoke(it as JStaticInvokeExpr) }
map.put(JNewExpr::class.java) { handleNewExpr(it as JNewExpr) }
Expand Down Expand Up @@ -89,14 +93,7 @@ class ExpressionHandler(frontend: JVMLanguageFrontend) :
map.put(JXorExpr::class.java) { handleAbstractBinopExpr(it as AbstractBinopExpr) }

// Unary operator
map.put(JNegExpr::class.java) {
handleAbstractUnopExpr(
it as AbstractUnopExpr,
postfix = false,
prefix = true,
opCode = "-"
)
}
map.put(JNegExpr::class.java) { handleNegExpr(it as JNegExpr) }

// Constants
map.put(BooleanConstant::class.java) { handleBooleanConstant(it as BooleanConstant) }
Expand Down Expand Up @@ -179,8 +176,17 @@ class ExpressionHandler(frontend: JVMLanguageFrontend) :
return call
}

/**
* The difference between [JSpecialInvokeExpr] and a regular [JVirtualInvokeExpr] is that the
* invoked function is not part of the declared class, but rather it is a function of its base
* class(es).
*
* We currently can only model this as a regular call and hope that the [SymbolResolver] will
* pick the correct function. Maybe we can supply some kind of hint to the resolver to make this
* better.
*/
private fun handleSpecialInvoke(specialInvokeExpr: JSpecialInvokeExpr): Expression {
// This is probably a constructor call or another corner case
// This is probably a constructor call
return if (specialInvokeExpr.methodSignature.name == "<init>") {
val type = frontend.typeOf(specialInvokeExpr.methodSignature.declClassType)
val construct = newConstructExpression(rawNode = specialInvokeExpr)
Expand All @@ -191,10 +197,22 @@ class ExpressionHandler(frontend: JVMLanguageFrontend) :

construct
} else {
newProblemExpression("specialinvoke with something unknown")
// Just a normal call
return handleAbstractInvokeExpr(specialInvokeExpr)
}
}

private fun handleAbstractInvokeExpr(dynamicInvokeExpr: AbstractInvokeExpr): CallExpression {
// Model this as a static call to the method. Not sure if this is really that good or if we
// want to somehow "call" the underlying bootstrap method
val callee = dynamicInvokeExpr.methodSignature.toStaticRef()
val call = newCallExpression(callee, rawNode = dynamicInvokeExpr)
call.arguments = dynamicInvokeExpr.args.mapNotNull { handle(it) }
call.type = frontend.typeOf(dynamicInvokeExpr.methodSignature.type)

return call
}

private fun handleStaticInvoke(staticInvokeExpr: JStaticInvokeExpr): CallExpression {
val ref = staticInvokeExpr.methodSignature.toStaticRef()

Expand Down Expand Up @@ -238,13 +256,8 @@ class ExpressionHandler(frontend: JVMLanguageFrontend) :
return op
}

private fun handleAbstractUnopExpr(
expr: AbstractUnopExpr,
postfix: Boolean,
prefix: Boolean,
opCode: String
): UnaryOperator {
val op = newUnaryOperator(opCode, postfix = postfix, prefix = prefix, rawNode = expr)
private fun handleNegExpr(expr: AbstractUnopExpr): UnaryOperator {
val op = newUnaryOperator("-", postfix = false, prefix = true, rawNode = expr)
op.input = handle(expr.op) ?: newProblemExpression("missing input")
op.type = frontend.typeOf(expr.type)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import kotlin.reflect.KClass

class JVMLanguage : Language<JVMLanguageFrontend>() {
override val fileExtensions: List<String>
get() = listOf("class", "java", "jimple")
get() = listOf("class", "java", "jimple", "jar")

override val namespaceDelimiter: String
get() = "."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,25 @@ class JVMLanguageFrontend(
)
JavaView(inputLocation)
}
"jar" -> {
val inputLocation: AnalysisInputLocation<JavaSootClass> =
JavaClassPathAnalysisInputLocation(
file.path,
SourceType.Library,
listOf(
NopEliminator(),
CastAndReturnInliner(),
UnreachableCodeEliminator(),
Aggregator(),
CopyPropagator(),
// ConditionalBranchFolder(),
EmptySwitchEliminator(),
TypeAssigner(),
LocalNameStandardizer()
)
)
JavaView(inputLocation)
}
"java" -> {
val inputLocation: AnalysisInputLocation<JavaSootClass> =
JavaSourcePathAnalysisInputLocation(ctx.config.topLevel!!.path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,44 @@ class JVMLanguageFrontendTest {
println(haveFun.code)
}

@Test
fun testLiteralsJar() {
// This will be our classpath
val topLevel = Path.of("src", "test", "resources", "jar", "literals")
val tu =
TestUtils.analyzeAndGetFirstTU(
// In case of a jar, the jar is directly used as a class path
listOf(topLevel.resolve("literals.jar").toFile()),
topLevel,
true
) {
it.registerPass<EdgeCachePass>()
it.registerLanguage<JVMLanguage>()
}
assertNotNull(tu)
assertEquals(0, tu.problems.size)
tu.methods.forEach { println(it.code) }
}

@Test
fun testInheritenceClass() {
// This will be our classpath
val topLevel = Path.of("src", "test", "resources", "class", "inheritence")
val tu =
TestUtils.analyzeAndGetFirstTU(
// In case of a jar, the jar is directly used as a class path
listOf(topLevel.resolve("mypackage/Application.class").toFile()),
topLevel,
true
) {
it.registerPass<EdgeCachePass>()
it.registerLanguage<JVMLanguage>()
}
assertNotNull(tu)
tu.methods.forEach { println(it.code) }
assertEquals(0, tu.problems.size)
}

@Test
fun testFieldsClass() {
// This will be our classpath
Expand All @@ -189,7 +227,6 @@ class JVMLanguageFrontendTest {
}
assertNotNull(tu)
assertEquals(0, tu.problems.size)

tu.methods.forEach { println(it.code) }

val refs = tu.refs.filterIsInstance<MemberExpression>()
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package mypackage;

public class AnotherExtendedClass extends BaseClass {
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package mypackage;

public class Application {

public void init() {
var extended = new ExtendedClass();
extended.setMyProperty(10);

BaseClass base;
if(Math.random() == 1.0) {
base = (BaseClass) extended;
} else {
base = new AnotherExtendedClass();
}
base.setMyProperty(10);
}

}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package mypackage;

public class BaseClass {

public int getMyProperty() {
return myProperty;
}

public void setMyProperty(int myProperty) {
this.myProperty = myProperty;
}

protected int myProperty = 5;
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package mypackage;

public class ExtendedClass extends BaseClass {

public void setMyProperty(int myProperty) {
informSomebody(this.myProperty, myProperty);
super.setMyProperty(myProperty);
}

private void informSomebody(int oldValue, int newValue) {
System.out.println("We changed the value from " + oldValue + " to " + newValue);
}

}
Binary file not shown.

0 comments on commit c6bd4fc

Please sign in to comment.