Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f4c87cb

Browse files
tamasvajkigfoo
authored andcommitted
Extract function expressions
1 parent b32ac93 commit f4c87cb

17 files changed

Lines changed: 371 additions & 13 deletions

File tree

java/kotlin-extractor/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
2727
sourceSets {
2828
main {
2929
kotlin {
30-
excludes = ["utils/versions/v_1_4/*.kt"]
30+
excludes = ["utils/versions/v_1_4/*.kt", "utils/versions/v_1_6/*.kt"]
3131
// change the above line to the below for building with kotlinVersion=1.4.32:
32-
//excludes = ["utils/versions/default/*.kt"]
32+
//excludes = ["utils/versions/default/*.kt", "utils/versions/v_1_6/*.kt"]
3333
}
3434
}
3535
}

java/kotlin-extractor/build.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,14 @@ def compile(jars, java_jars, dependency_folder, transform_to_embeddable, output,
135135

136136
if version.startswith('1.4'):
137137
shutil.rmtree(tmp_dir + '/main/kotlin/utils/versions/default')
138+
shutil.rmtree(tmp_dir + '/main/kotlin/utils/versions/v_1_6')
138139
else:
139-
shutil.rmtree(tmp_dir + '/main/kotlin/utils/versions/v_1_4')
140+
if version.startswith('1.6'):
141+
shutil.rmtree(tmp_dir + '/main/kotlin/utils/versions/v_1_4')
142+
shutil.rmtree(tmp_dir + '/main/kotlin/utils/versions/default')
143+
else:
144+
shutil.rmtree(tmp_dir + '/main/kotlin/utils/versions/v_1_4')
145+
shutil.rmtree(tmp_dir + '/main/kotlin/utils/versions/v_1_6')
140146

141147
srcs = find_sources(tmp_dir)
142148

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

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
package com.github.codeql
22

3+
import com.github.codeql.utils.versions.functionN
34
import com.semmle.extractor.java.OdasaOutput
45
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
6+
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
57
import org.jetbrains.kotlin.descriptors.ClassKind
68
import org.jetbrains.kotlin.descriptors.Modality
79
import org.jetbrains.kotlin.ir.IrElement
810
import org.jetbrains.kotlin.ir.IrStatement
911
import org.jetbrains.kotlin.ir.declarations.*
1012
import org.jetbrains.kotlin.ir.expressions.*
13+
import org.jetbrains.kotlin.ir.interpreter.toIrConst
1114
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
1215
import org.jetbrains.kotlin.ir.types.*
1316
import org.jetbrains.kotlin.ir.util.*
17+
import org.jetbrains.kotlin.name.FqName
18+
import org.jetbrains.kotlin.util.OperatorNameConventions
1419

1520
open class KotlinFileExtractor(
1621
override val logger: FileLogger,
@@ -254,13 +259,16 @@ open class KotlinFileExtractor(
254259
return FieldResult(instanceId, instanceName)
255260
}
256261

257-
fun extractValueParameter(vp: IrValueParameter, parent: Label<out DbCallable>, idx: Int): TypeResults {
258-
val id = useValueParameter(vp)
259-
val type = useType(vp.type)
260-
val locId = tw.getLocation(vp)
262+
private fun extractValueParameter(vp: IrValueParameter, parent: Label<out DbCallable>, idx: Int): TypeResults {
263+
return extractValueParameter(useValueParameter(vp), vp.type, vp.name.asString(), vp, parent, idx)
264+
}
265+
266+
private fun extractValueParameter(id: Label<out DbParam>, t: IrType, name: String, loc: IrElement, parent: Label<out DbCallable>, idx: Int): TypeResults {
267+
val type = useType(t)
268+
val locId = tw.getLocation(loc)
261269
tw.writeParams(id, type.javaResult.id, type.kotlinResult.id, idx, parent, id)
262270
tw.writeHasLocation(id, locId)
263-
tw.writeParamName(id, vp.name.asString())
271+
tw.writeParamName(id, name)
264272
return type
265273
}
266274

@@ -371,9 +379,10 @@ open class KotlinFileExtractor(
371379
tw.writeConstrs(id as Label<DbConstructor>, shortName, "$shortName$paramsSignature", returnType.javaResult.id, returnType.kotlinResult.id, parentId, id)
372380
} else {
373381
val returnType = useType(f.returnType, TypeContext.RETURN)
374-
val shortName = f.name.asString()
382+
val shortName = getFunctionShortName(f)
375383
@Suppress("UNCHECKED_CAST")
376384
tw.writeMethods(id as Label<DbMethod>, shortName, "$shortName$paramsSignature", returnType.javaResult.id, returnType.kotlinResult.id, parentId, id)
385+
// TODO: fix `sourceId`. It doesn't always match the method ID.
377386
}
378387

379388
tw.writeHasLocation(id, locId)
@@ -1612,6 +1621,85 @@ open class KotlinFileExtractor(
16121621
tw.writeVariableBinding(id, instance.id)
16131622
}
16141623
}
1624+
is IrFunctionExpression -> {
1625+
1626+
val ids = getLocalFunctionLabels(e.function)
1627+
val locId = tw.getLocation(e)
1628+
1629+
val ext = e.function.extensionReceiverParameter
1630+
val parameters = if (ext != null) {
1631+
val l = mutableListOf(ext)
1632+
l.addAll(e.function.valueParameters)
1633+
l
1634+
} else {
1635+
e.function.valueParameters
1636+
}
1637+
1638+
var types = parameters.map { it.type }
1639+
types += e.function.returnType
1640+
1641+
val fnInterface = if (types.size > BuiltInFunctionArity.BIG_ARITY) {
1642+
pluginContext.referenceClass(FqName("kotlin.jvm.functions.FunctionN"))!!.typeWith(e.function.returnType)
1643+
} else {
1644+
functionN(pluginContext)(parameters.size).typeWith(types)
1645+
}
1646+
1647+
extractGeneratedClass(
1648+
e.function, // We're adding this function as a member, and changing its name to `invoke` to implement `kotlin.FunctionX<,,,>.invoke(,,)`
1649+
listOf(
1650+
pluginContext.referenceClass(FqName("kotlin.jvm.internal.Lambda"))!!.typeWith(),
1651+
fnInterface),
1652+
listOf(e.function.valueParameters.size.toIrConst(pluginContext.irBuiltIns.intType, e.startOffset, e.endOffset)))
1653+
1654+
val objectType = useType(pluginContext.irBuiltIns.anyNType).javaResult.id
1655+
1656+
// Only add bridge method if its signature is different from the lambda function
1657+
if (!types.all { useType(it).javaResult.id == objectType } ||
1658+
types.size > BuiltInFunctionArity.BIG_ARITY) {
1659+
1660+
val methodId = tw.getFreshIdLabel<DbMethod>()
1661+
1662+
val paramTypes =
1663+
if (types.size > BuiltInFunctionArity.BIG_ARITY) {
1664+
// signature is `Object invoke(Object[] p)`
1665+
listOf(extractValueParameter(tw.getFreshIdLabel(), pluginContext.irBuiltIns.arrayClass.typeWith(pluginContext.irBuiltIns.anyNType), "p", e, methodId, 0))
1666+
} else {
1667+
// signature is `Object invoke(Object p0, Object p1, ..., Object pN)`
1668+
parameters.mapIndexed { i, _ ->
1669+
extractValueParameter(tw.getFreshIdLabel(), pluginContext.irBuiltIns.anyNType, "p$i", e, methodId, i)
1670+
}
1671+
}
1672+
1673+
val paramsSignature = paramTypes.joinToString(separator = ",", prefix = "(", postfix = ")") { it.javaResult.signature!! }
1674+
1675+
val returnType = useType(pluginContext.irBuiltIns.anyNType, TypeContext.RETURN)
1676+
val shortName = OperatorNameConventions.INVOKE.asString()
1677+
@Suppress("UNCHECKED_CAST")
1678+
tw.writeMethods(methodId, shortName, "$shortName$paramsSignature", returnType.javaResult.id, returnType.kotlinResult.id, ids.type.javaResult.id as Label<out DbReftype>, methodId)
1679+
tw.writeHasLocation(methodId, locId)
1680+
1681+
// TODO:
1682+
// - Add body of bridge method, which calls `e.function`:
1683+
// ```
1684+
// public int invoke(int i, Object j, String k) { return 5; }
1685+
// public Object invoke(Object p0, Object p1, Object p2) {
1686+
// return invoke((int)p0, (Object)p1, (String)p2);
1687+
// or
1688+
// invoke((int)p0, (Object)p1, (String)p2);
1689+
// return kotlin.Unit.INSTANCE
1690+
// }
1691+
// ```
1692+
}
1693+
1694+
val exprParent = parent.expr(e, callable)
1695+
1696+
val idNewexpr = tw.getFreshIdLabel<DbNewexpr>()
1697+
tw.writeExprs_newexpr(idNewexpr, ids.type.javaResult.id, ids.type.kotlinResult.id, exprParent.parent, exprParent.idx)
1698+
tw.writeHasLocation(idNewexpr, locId)
1699+
tw.writeCallableEnclosingExpr(idNewexpr, callable)
1700+
tw.writeStatementEnclosingExpr(idNewexpr, exprParent.enclosingStmt)
1701+
tw.writeCallableBinding(idNewexpr, ids.constructor)
1702+
}
16151703
else -> {
16161704
logger.warnElement(Severity.ErrorSevere, "Unrecognised IrExpression: " + e.javaClass, e)
16171705
}
@@ -1754,7 +1842,7 @@ open class KotlinFileExtractor(
17541842
private val IrType.isAnonymous: Boolean
17551843
get() = ((this as? IrSimpleType)?.classifier?.owner as? IrClass)?.isAnonymousObject ?: false
17561844

1757-
fun extractGeneratedClass(localFunction: IrFunction, superTypes: List<IrType>) : Label<out DbClass> {
1845+
private fun extractGeneratedClass(localFunction: IrFunction, superTypes: List<IrType>, superConstructorArgs: List<IrExpression> = listOf()) : Label<out DbClass> {
17581846
val ids = getLocalFunctionLabels(localFunction)
17591847

17601848
// Write class
@@ -1780,6 +1868,10 @@ open class KotlinFileExtractor(
17801868
// Super call
17811869
val superCallId = tw.getFreshIdLabel<DbSuperconstructorinvocationstmt>()
17821870
tw.writeStmts_superconstructorinvocationstmt(superCallId, constructorBlockId, 0, ids.function)
1871+
for (i in 0 until superConstructorArgs.size) {
1872+
val arg = superConstructorArgs[i]
1873+
extractExpressionExpr(arg, ids.constructor, superCallId, i, superCallId)
1874+
}
17831875

17841876
val baseConstructor = superTypes.first().classOrNull!!.owner.declarations.find { it is IrFunction && it.symbol is IrConstructorSymbol }
17851877
val baseConstructorId = useFunction<DbConstructor>(baseConstructor as IrFunction)

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import org.jetbrains.kotlin.ir.types.*
1313
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
1414
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
1515
import org.jetbrains.kotlin.ir.util.*
16+
import org.jetbrains.kotlin.name.SpecialNames
1617
import org.jetbrains.kotlin.types.Variance
18+
import org.jetbrains.kotlin.util.OperatorNameConventions
1719

1820
open class KotlinUsesExtractor(
1921
open val logger: Logger,
@@ -452,8 +454,17 @@ class X {
452454
}
453455
}
454456

457+
private val IrDeclaration.isAnonymousFunction get() = this is IrSimpleFunction && name == SpecialNames.NO_NAME_PROVIDED
458+
459+
fun getFunctionShortName(f: IrFunction) : String {
460+
if (f.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA || f.isAnonymousFunction)
461+
return OperatorNameConventions.INVOKE.asString()
462+
else
463+
return f.name.asString()
464+
}
465+
455466
fun getFunctionLabel(f: IrFunction) : String {
456-
return getFunctionLabel(f.parent, f.name.asString(), f.valueParameters, f.returnType, f.extensionReceiverParameter)
467+
return getFunctionLabel(f.parent, getFunctionShortName(f), f.valueParameters, f.returnType, f.extensionReceiverParameter)
457468
}
458469

459470
fun getFunctionLabel(
@@ -477,8 +488,7 @@ class X {
477488
}
478489

479490
protected fun IrFunction.isLocalFunction(): Boolean {
480-
return this.visibility == DescriptorVisibilities.LOCAL &&
481-
this.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
491+
return this.visibility == DescriptorVisibilities.LOCAL
482492
}
483493

484494
private val generatedLocalFunctionTypeMapping: MutableMap<IrFunction, LocalFunctionLabels> = mutableMapOf()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.github.codeql.utils.versions
2+
3+
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
4+
import org.jetbrains.kotlin.ir.declarations.IrClass
5+
6+
fun functionN(pluginContext: IrPluginContext): (Int) -> IrClass {
7+
return { i -> pluginContext.irBuiltIns.functionFactory.functionN(i) }
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.github.codeql.utils.versions
2+
3+
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
4+
import org.jetbrains.kotlin.ir.declarations.IrClass
5+
6+
fun functionN(pluginContext: IrPluginContext): (Int) -> IrClass {
7+
return { i -> pluginContext.irBuiltIns.functionFactory.functionN(i) }
8+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.github.codeql.utils.versions
2+
3+
import org.jetbrains.kotlin.ir.IrFileEntry
4+
5+
typealias FileEntry = IrFileEntry
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.github.codeql.utils.versions
2+
3+
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
4+
5+
fun functionN(pluginContext: IrPluginContext) = pluginContext.irBuiltIns::functionN
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.github.codeql.utils.versions
2+
3+
import com.intellij.psi.PsiElement
4+
import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager
5+
import org.jetbrains.kotlin.backend.jvm.ir.getKtFile
6+
import org.jetbrains.kotlin.ir.IrElement
7+
import org.jetbrains.kotlin.ir.declarations.IrFile
8+
import org.jetbrains.kotlin.psi.KtFile
9+
10+
class Psi2Ir: Psi2IrFacade {
11+
override fun getKtFile(irFile: IrFile): KtFile? {
12+
return irFile.getKtFile()
13+
}
14+
15+
override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? {
16+
return PsiSourceManager.findPsiElement(irElement, irFile)
17+
}
18+
}

java/ql/test/kotlin/library-tests/arrays/test.expected

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@ sourceSignatures
1010
| arrayCreations.kt:3:1:29:1 | hashCode | hashCode() |
1111
| arrayCreations.kt:3:1:29:1 | toString | toString() |
1212
| arrayCreations.kt:4:3:28:3 | test1 | test1() |
13+
| arrayCreations.kt:22:29:22:33 | | |
14+
| arrayCreations.kt:22:29:22:33 | invoke | invoke(int) |
15+
| arrayCreations.kt:22:29:22:33 | invoke | invoke(java.lang.Object) |
16+
| arrayCreations.kt:23:24:23:28 | | |
17+
| arrayCreations.kt:23:24:23:28 | invoke | invoke(int) |
18+
| arrayCreations.kt:23:24:23:28 | invoke | invoke(java.lang.Object) |
19+
| arrayCreations.kt:25:27:25:31 | | |
20+
| arrayCreations.kt:25:27:25:31 | invoke | invoke(int) |
21+
| arrayCreations.kt:25:27:25:31 | invoke | invoke(java.lang.Object) |
22+
| arrayCreations.kt:26:27:26:36 | | |
23+
| arrayCreations.kt:26:27:26:36 | invoke | invoke(int) |
24+
| arrayCreations.kt:26:27:26:36 | invoke | invoke(java.lang.Object) |
25+
| arrayCreations.kt:27:24:27:38 | | |
26+
| arrayCreations.kt:27:24:27:38 | invoke | invoke(int) |
27+
| arrayCreations.kt:27:24:27:38 | invoke | invoke(java.lang.Object) |
1328
| primitiveArrays.kt:3:1:7:1 | <obinit> | <obinit>() |
1429
| primitiveArrays.kt:3:1:7:1 | Test | Test() |
1530
| primitiveArrays.kt:3:1:7:1 | equals | equals(java.lang.Object) |

0 commit comments

Comments
 (0)