Kotlin: Implement JvmOverloads annotation

This generates functions that omit parameters with default values, rightmost first, such that Java can achieve a similar experience to Kotlin (which represents calls internally as if the default was supplied explicitly, and/or uses a $default method that supplies the needed arguments).

A complication: combining JvmOverloads with JvmStatic means that both the companion object and the surrounding class get overloads.
This commit is contained in:
Chris Smowton
2022-07-12 19:39:24 +01:00
parent d52d3d7b75
commit d3d3ce843a
23 changed files with 1261 additions and 67 deletions

View File

@@ -0,0 +1,39 @@
public class User {
public static String source() { return "taint"; }
public static void test(Test2 t2, GenericTest<Integer> gt) {
Test.taintSuppliedAsDefault(1, "no taint", 2);
Test.taintSuppliedAsDefault(1, 2);
Test.noTaintByDefault(1, source(), 2, 3);
Test.noTaintByDefault(1, source(), 2);
Test2.taintSuppliedAsDefaultStatic(1, "no taint", 2);
Test2.taintSuppliedAsDefaultStatic(1, 2);
Test2.noTaintByDefaultStatic(1, source(), 2, 3);
Test2.noTaintByDefaultStatic(1, source(), 2);
t2.taintSuppliedAsDefault(1, "no taint", 2);
t2.taintSuppliedAsDefault(1, 2);
t2.noTaintByDefault(1, source(), 2, 3);
t2.noTaintByDefault(1, source(), 2);
gt.taintSuppliedAsDefault(1, "no taint", 2);
gt.taintSuppliedAsDefault(1, 2);
gt.noTaintByDefault(1, source(), 2, 3);
gt.noTaintByDefault(1, source(), 2);
new ConstructorTaintsByDefault(1, "no taint", 2);
new ConstructorTaintsByDefault(1, 2);
new ConstructorDoesNotTaintByDefault(1, source(), 2, 3);
new ConstructorDoesNotTaintByDefault(1, source(), 2);
new GenericConstructorTaintsByDefault<Integer>(1, "no taint", 2);
new GenericConstructorTaintsByDefault<Integer>(1, 2);
new GenericConstructorDoesNotTaintByDefault<Integer>(1, source(), 2, 3);
new GenericConstructorDoesNotTaintByDefault<Integer>(1, source(), 2);
}
}

View File

@@ -0,0 +1,19 @@
| User.java:9:30:9:37 | source(...) | test.kt:13:97:13:97 | s |
| User.java:10:30:10:37 | source(...) | test.kt:13:97:13:97 | s |
| User.java:14:37:14:44 | source(...) | test.kt:25:105:25:105 | s |
| User.java:15:37:15:44 | source(...) | test.kt:25:105:25:105 | s |
| User.java:19:28:19:35 | source(...) | test.kt:33:97:33:97 | s |
| User.java:20:28:20:35 | source(...) | test.kt:33:97:33:97 | s |
| User.java:24:28:24:35 | source(...) | test.kt:43:93:43:93 | s |
| User.java:25:28:25:35 | source(...) | test.kt:43:93:43:93 | s |
| User.java:29:45:29:52 | source(...) | test.kt:58:10:58:10 | s |
| User.java:30:45:30:52 | source(...) | test.kt:58:10:58:10 | s |
| User.java:34:61:34:68 | source(...) | test.kt:74:10:74:10 | s |
| User.java:35:61:35:68 | source(...) | test.kt:74:10:74:10 | s |
| test.kt:10:55:10:62 | source(...) | test.kt:10:84:10:84 | s |
| test.kt:22:63:22:70 | source(...) | test.kt:22:92:22:92 | s |
| test.kt:22:63:22:70 | source(...) | test.kt:22:92:22:92 | s |
| test.kt:30:55:30:62 | source(...) | test.kt:30:84:30:84 | s |
| test.kt:40:53:40:60 | source(...) | test.kt:40:80:40:80 | s |
| test.kt:47:92:47:99 | source(...) | test.kt:50:10:50:10 | s |
| test.kt:63:100:63:107 | source(...) | test.kt:66:10:66:10 | s |

View File

@@ -0,0 +1,78 @@
fun getString() = "Hello world"
fun source() = "tainted"
fun sink(s: String) { }
object Test {
@JvmOverloads @JvmStatic
fun taintSuppliedAsDefault(before: Int, s: String = source(), after: Int) { sink(s) }
@JvmOverloads @JvmStatic
fun noTaintByDefault(before: Int, s: String = "no taint", after: Int, after2: Int = 1) { sink(s) }
}
public class Test2 {
companion object {
@JvmOverloads @JvmStatic
fun taintSuppliedAsDefaultStatic(before: Int, s: String = source(), after: Int) { sink(s) }
@JvmOverloads @JvmStatic
fun noTaintByDefaultStatic(before: Int, s: String = "no taint", after: Int, after2: Int = 1) { sink(s) }
}
@JvmOverloads
fun taintSuppliedAsDefault(before: Int, s: String = source(), after: Int) { sink(s) }
@JvmOverloads
fun noTaintByDefault(before: Int, s: String = "no taint", after: Int, after2: Int = 1) { sink(s) }
}
public class GenericTest<T> {
@JvmOverloads
fun taintSuppliedAsDefault(before: T, s: String = source(), after: T) { sink(s) }
@JvmOverloads
fun noTaintByDefault(before: T, s: String = "no taint", after: T, after2: Int = 1) { sink(s) }
}
public class ConstructorTaintsByDefault @JvmOverloads constructor(before: Int, s: String = source(), after: Int) {
init {
sink(s)
}
}
public class ConstructorDoesNotTaintByDefault @JvmOverloads constructor(before: Int, s: String = "no taint", after: Int, after2: Int = 1) {
init {
sink(s)
}
}
public class GenericConstructorTaintsByDefault<T> @JvmOverloads constructor(before: T, s: String = source(), after: T) {
init {
sink(s)
}
}
public class GenericConstructorDoesNotTaintByDefault<T> @JvmOverloads constructor(before: T, s: String = "no taint", after: T, after2: T? = null) {
init {
sink(s)
}
}

View File

@@ -0,0 +1,4 @@
from create_database_utils import *
os.mkdir('kbuild')
run_codeql_database_create(["kotlinc test.kt -d kbuild", "javac User.java -cp kbuild"], lang="java")

View File

@@ -0,0 +1,18 @@
import java
import semmle.code.java.dataflow.DataFlow
class Config extends DataFlow::Configuration {
Config() { this = "config" }
override predicate isSource(DataFlow::Node n) {
n.asExpr().(MethodAccess).getCallee().getName() = "source"
}
override predicate isSink(DataFlow::Node n) {
n.asExpr().(Argument).getCall().getCallee().getName() = "sink"
}
}
from Config c, DataFlow::Node source, DataFlow::Node sink
where c.hasFlow(source, sink)
select source, sink