Skip to content

Commit e29be41

Browse files
authored
Merge pull request #9811 from smowton/smowton/feature/kotlin-jvmoverloads-annotation
Kotlin: Implement JvmOverloads annotation
2 parents 1253657 + 58cb544 commit e29be41

File tree

25 files changed

+1427
-226
lines changed

25 files changed

+1427
-226
lines changed

java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
8484
// file information if needed:
8585
val ftw = tw.makeFileTrapWriter(binaryPath, irDecl is IrClass)
8686

87-
val fileExtractor = KotlinFileExtractor(logger, ftw, binaryPath, manager, this, primitiveTypeMapping, pluginContext, globalExtensionState)
87+
val fileExtractor = KotlinFileExtractor(logger, ftw, binaryPath, manager, this, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
8888

8989
if (irDecl is IrClass) {
9090
// Populate a location and compilation-unit package for the file. This is similar to

java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ private fun doFile(
322322
// file information
323323
val sftw = tw.makeSourceFileTrapWriter(srcFile, true)
324324
val externalDeclExtractor = ExternalDeclExtractor(logger, invocationTrapFile, srcFilePath, primitiveTypeMapping, pluginContext, globalExtensionState, fileTrapWriter)
325-
val fileExtractor = KotlinFileExtractor(logger, sftw, srcFilePath, null, externalDeclExtractor, primitiveTypeMapping, pluginContext, globalExtensionState)
325+
val fileExtractor = KotlinFileExtractor(logger, sftw, srcFilePath, null, externalDeclExtractor, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
326326

327327
fileExtractor.extractFileContents(srcFile, sftw.fileId)
328328
externalDeclExtractor.extractExternalClasses()

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 167 additions & 55 deletions
Large diffs are not rendered by default.

java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ open class KotlinUsesExtractor(
3838
val pluginContext: IrPluginContext,
3939
val globalExtensionState: KotlinExtractorGlobalState
4040
) {
41-
4241
val javaLangObject by lazy {
4342
val result = pluginContext.referenceClass(FqName("java.lang.Object"))?.owner
4443
result?.let { extractExternalClassLater(it) }
@@ -128,18 +127,24 @@ open class KotlinUsesExtractor(
128127
return this
129128
}
130129

130+
val newDeclarationStack =
131+
if (this is KotlinFileExtractor)
132+
this.declarationStack
133+
else
134+
KotlinFileExtractor.DeclarationStack()
135+
131136
if (clsFile == null || isExternalDeclaration(cls)) {
132137
val filePath = getIrClassBinaryPath(cls)
133138
val newTrapWriter = tw.makeFileTrapWriter(filePath, true)
134139
val newLoggerTrapWriter = logger.tw.makeFileTrapWriter(filePath, false)
135140
val newLogger = FileLogger(logger.loggerBase, newLoggerTrapWriter)
136-
return KotlinFileExtractor(newLogger, newTrapWriter, filePath, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, globalExtensionState)
141+
return KotlinFileExtractor(newLogger, newTrapWriter, filePath, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, newDeclarationStack, globalExtensionState)
137142
}
138143

139144
val newTrapWriter = tw.makeSourceFileTrapWriter(clsFile, true)
140145
val newLoggerTrapWriter = logger.tw.makeSourceFileTrapWriter(clsFile, false)
141146
val newLogger = FileLogger(logger.loggerBase, newLoggerTrapWriter)
142-
return KotlinFileExtractor(newLogger, newTrapWriter, clsFile.path, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, globalExtensionState)
147+
return KotlinFileExtractor(newLogger, newTrapWriter, clsFile.path, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, newDeclarationStack, globalExtensionState)
143148
}
144149

145150
// The Kotlin compiler internal representation of Outer<T>.Inner<S>.InnerInner<R> is InnerInner<R, S, T>. This function returns just `R`.
@@ -1027,14 +1032,18 @@ open class KotlinUsesExtractor(
10271032
* looked up the parent ID ourselves, we would get as ID for
10281033
* `java.lang.Throwable`, which isn't what we want. So we have to
10291034
* allow it to be passed in.
1030-
*/
1035+
*
1036+
* `maybeParameterList` can be supplied to override the function's
1037+
* value parameters; this is used for generating labels of overloads
1038+
* that omit one or more parameters that has a default value specified.
1039+
*/
10311040
@OptIn(ObsoleteDescriptorBasedAPI::class)
1032-
fun getFunctionLabel(f: IrFunction, maybeParentId: Label<out DbElement>?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) =
1041+
fun getFunctionLabel(f: IrFunction, maybeParentId: Label<out DbElement>?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?, maybeParameterList: List<IrValueParameter>? = null) =
10331042
getFunctionLabel(
10341043
f.parent,
10351044
maybeParentId,
10361045
getFunctionShortName(f).nameInDB,
1037-
f.valueParameters,
1046+
maybeParameterList ?: f.valueParameters,
10381047
getAdjustedReturnType(f),
10391048
f.extensionReceiverParameter,
10401049
getFunctionTypeParameters(f),
@@ -1437,6 +1446,12 @@ open class KotlinUsesExtractor(
14371446

14381447
fun getTypeParameterParentLabel(param: IrTypeParameter) =
14391448
param.parent.let {
1449+
(it as? IrFunction)?.let { fn ->
1450+
if (this is KotlinFileExtractor)
1451+
this.declarationStack.findOverriddenAttributes(fn)?.id
1452+
else
1453+
null
1454+
} ?:
14401455
when (it) {
14411456
is IrClass -> useClassSource(it)
14421457
is IrFunction -> useFunction(it, noReplace = true)
@@ -1574,13 +1589,23 @@ open class KotlinUsesExtractor(
15741589
*/
15751590
fun getValueParameterLabel(vp: IrValueParameter, parent: Label<out DbCallable>?): String {
15761591
val declarationParent = vp.parent
1577-
val parentId = parent ?: useDeclarationParent(declarationParent, false)
1592+
val overriddenParentAttributes = (declarationParent as? IrFunction)?.let {
1593+
// Note the check 'vp.fileOrNull?.path == this.filePath' should never actually do anything, since references
1594+
// to a value parameter should always come from within the same .kt file.
1595+
if (this is KotlinFileExtractor && vp.fileOrNull?.path == this.filePath)
1596+
this.declarationStack.findOverriddenAttributes(it)
1597+
else
1598+
null
1599+
}
1600+
val parentId = parent ?: overriddenParentAttributes?.id ?: useDeclarationParent(declarationParent, false)
15781601

1579-
val idx = if (declarationParent is IrFunction && declarationParent.extensionReceiverParameter != null)
1602+
val idxBase = overriddenParentAttributes?.valueParameters?.indexOf(vp) ?: vp.index
1603+
val idxOffset = if (declarationParent is IrFunction && declarationParent.extensionReceiverParameter != null)
15801604
// For extension functions increase the index to match what the java extractor sees:
1581-
vp.index + 1
1605+
1
15821606
else
1583-
vp.index
1607+
0
1608+
val idx = idxBase + idxOffset
15841609

15851610
if (idx < 0) {
15861611
// We're not extracting this and this@TYPE parameters of functions:
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
public class User {
2+
3+
public static String source() { return "taint"; }
4+
5+
public static void test(Test2 t2, GenericTest<Integer> gt) {
6+
7+
Test.taintSuppliedAsDefault(1, "no taint", 2);
8+
Test.taintSuppliedAsDefault(1, 2);
9+
Test.noTaintByDefault(1, source(), 2, 3);
10+
Test.noTaintByDefault(1, source(), 2);
11+
12+
Test2.taintSuppliedAsDefaultStatic(1, "no taint", 2);
13+
Test2.taintSuppliedAsDefaultStatic(1, 2);
14+
Test2.noTaintByDefaultStatic(1, source(), 2, 3);
15+
Test2.noTaintByDefaultStatic(1, source(), 2);
16+
17+
t2.taintSuppliedAsDefault(1, "no taint", 2);
18+
t2.taintSuppliedAsDefault(1, 2);
19+
t2.noTaintByDefault(1, source(), 2, 3);
20+
t2.noTaintByDefault(1, source(), 2);
21+
22+
gt.taintSuppliedAsDefault(1, "no taint", 2);
23+
gt.taintSuppliedAsDefault(1, 2);
24+
gt.noTaintByDefault(1, source(), 2, 3);
25+
gt.noTaintByDefault(1, source(), 2);
26+
27+
new ConstructorTaintsByDefault(1, "no taint", 2);
28+
new ConstructorTaintsByDefault(1, 2);
29+
new ConstructorDoesNotTaintByDefault(1, source(), 2, 3);
30+
new ConstructorDoesNotTaintByDefault(1, source(), 2);
31+
32+
new GenericConstructorTaintsByDefault<Integer>(1, "no taint", 2);
33+
new GenericConstructorTaintsByDefault<Integer>(1, 2);
34+
new GenericConstructorDoesNotTaintByDefault<Integer>(1, source(), 2, 3);
35+
new GenericConstructorDoesNotTaintByDefault<Integer>(1, source(), 2);
36+
37+
}
38+
39+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
| User.java:9:30:9:37 | source(...) | test.kt:13:97:13:97 | s |
2+
| User.java:10:30:10:37 | source(...) | test.kt:13:97:13:97 | s |
3+
| User.java:14:37:14:44 | source(...) | test.kt:25:105:25:105 | s |
4+
| User.java:15:37:15:44 | source(...) | test.kt:25:105:25:105 | s |
5+
| User.java:19:28:19:35 | source(...) | test.kt:33:97:33:97 | s |
6+
| User.java:20:28:20:35 | source(...) | test.kt:33:97:33:97 | s |
7+
| User.java:24:28:24:35 | source(...) | test.kt:43:93:43:93 | s |
8+
| User.java:25:28:25:35 | source(...) | test.kt:43:93:43:93 | s |
9+
| User.java:29:45:29:52 | source(...) | test.kt:58:10:58:10 | s |
10+
| User.java:30:45:30:52 | source(...) | test.kt:58:10:58:10 | s |
11+
| User.java:34:61:34:68 | source(...) | test.kt:74:10:74:10 | s |
12+
| User.java:35:61:35:68 | source(...) | test.kt:74:10:74:10 | s |
13+
| test.kt:10:55:10:62 | source(...) | test.kt:10:84:10:84 | s |
14+
| test.kt:22:63:22:70 | source(...) | test.kt:22:92:22:92 | s |
15+
| test.kt:22:63:22:70 | source(...) | test.kt:22:92:22:92 | s |
16+
| test.kt:30:55:30:62 | source(...) | test.kt:30:84:30:84 | s |
17+
| test.kt:40:53:40:60 | source(...) | test.kt:40:80:40:80 | s |
18+
| test.kt:47:92:47:99 | source(...) | test.kt:50:10:50:10 | s |
19+
| test.kt:63:100:63:107 | source(...) | test.kt:66:10:66:10 | s |
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
fun getString() = "Hello world"
2+
3+
fun source() = "tainted"
4+
5+
fun sink(s: String) { }
6+
7+
object Test {
8+
9+
@JvmOverloads @JvmStatic
10+
fun taintSuppliedAsDefault(before: Int, s: String = source(), after: Int) { sink(s) }
11+
12+
@JvmOverloads @JvmStatic
13+
fun noTaintByDefault(before: Int, s: String = "no taint", after: Int, after2: Int = 1) { sink(s) }
14+
15+
}
16+
17+
public class Test2 {
18+
19+
companion object {
20+
21+
@JvmOverloads @JvmStatic
22+
fun taintSuppliedAsDefaultStatic(before: Int, s: String = source(), after: Int) { sink(s) }
23+
24+
@JvmOverloads @JvmStatic
25+
fun noTaintByDefaultStatic(before: Int, s: String = "no taint", after: Int, after2: Int = 1) { sink(s) }
26+
27+
}
28+
29+
@JvmOverloads
30+
fun taintSuppliedAsDefault(before: Int, s: String = source(), after: Int) { sink(s) }
31+
32+
@JvmOverloads
33+
fun noTaintByDefault(before: Int, s: String = "no taint", after: Int, after2: Int = 1) { sink(s) }
34+
35+
}
36+
37+
public class GenericTest<T> {
38+
39+
@JvmOverloads
40+
fun taintSuppliedAsDefault(before: T, s: String = source(), after: T) { sink(s) }
41+
42+
@JvmOverloads
43+
fun noTaintByDefault(before: T, s: String = "no taint", after: T, after2: Int = 1) { sink(s) }
44+
45+
}
46+
47+
public class ConstructorTaintsByDefault @JvmOverloads constructor(before: Int, s: String = source(), after: Int) {
48+
49+
init {
50+
sink(s)
51+
}
52+
53+
}
54+
55+
public class ConstructorDoesNotTaintByDefault @JvmOverloads constructor(before: Int, s: String = "no taint", after: Int, after2: Int = 1) {
56+
57+
init {
58+
sink(s)
59+
}
60+
61+
}
62+
63+
public class GenericConstructorTaintsByDefault<T> @JvmOverloads constructor(before: T, s: String = source(), after: T) {
64+
65+
init {
66+
sink(s)
67+
}
68+
69+
}
70+
71+
public class GenericConstructorDoesNotTaintByDefault<T> @JvmOverloads constructor(before: T, s: String = "no taint", after: T, after2: T? = null) {
72+
73+
init {
74+
sink(s)
75+
}
76+
77+
}
78+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from create_database_utils import *
2+
3+
os.mkdir('kbuild')
4+
run_codeql_database_create(["kotlinc test.kt -d kbuild", "javac User.java -cp kbuild"], lang="java")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import java
2+
import semmle.code.java.dataflow.DataFlow
3+
4+
class Config extends DataFlow::Configuration {
5+
Config() { this = "config" }
6+
7+
override predicate isSource(DataFlow::Node n) {
8+
n.asExpr().(MethodAccess).getCallee().getName() = "source"
9+
}
10+
11+
override predicate isSink(DataFlow::Node n) {
12+
n.asExpr().(Argument).getCall().getCallee().getName() = "sink"
13+
}
14+
}
15+
16+
from Config c, DataFlow::Node source, DataFlow::Node sink
17+
where c.hasFlow(source, sink)
18+
select source, sink

java/ql/lib/semmle/code/java/Element.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ class Element extends @element, Top {
6363
i = 7 and result = "Setter for a Kotlin delegated property"
6464
or
6565
i = 8 and result = "Proxy static method for a @JvmStatic-annotated function or property"
66+
or
67+
i = 9 and result = "Forwarder for a @JvmOverloads-annotated function"
6668
)
6769
}
6870
}

java/ql/lib/semmle/code/java/PrintAst.qll

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -534,12 +534,17 @@ final class ClassInterfaceNode extends ElementNode {
534534
or
535535
childIndex >= 0 and
536536
result.(ElementNode).getElement() =
537-
rank[childIndex](Element e, string file, int line, int column, string childStr |
537+
rank[childIndex](Element e, string file, int line, int column, string childStr, int argCount |
538538
e = this.getADeclaration() and
539539
locationSortKeys(e, file, line, column) and
540-
childStr = e.toString()
540+
childStr = e.toString() and
541+
(
542+
if e instanceof Callable
543+
then argCount = e.(Callable).getNumberOfParameters()
544+
else argCount = 0
545+
)
541546
|
542-
e order by file, line, column, childStr
547+
e order by file, line, column, childStr, argCount
543548
)
544549
}
545550
}

0 commit comments

Comments
 (0)