11package com.github.codeql
22
3+ import com.github.codeql.utils.versions.functionN
34import com.semmle.extractor.java.OdasaOutput
45import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
6+ import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
57import org.jetbrains.kotlin.descriptors.ClassKind
68import org.jetbrains.kotlin.descriptors.Modality
79import org.jetbrains.kotlin.ir.IrElement
810import org.jetbrains.kotlin.ir.IrStatement
911import org.jetbrains.kotlin.ir.declarations.*
1012import org.jetbrains.kotlin.ir.expressions.*
13+ import org.jetbrains.kotlin.ir.interpreter.toIrConst
1114import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
1215import org.jetbrains.kotlin.ir.types.*
1316import org.jetbrains.kotlin.ir.util.*
17+ import org.jetbrains.kotlin.name.FqName
18+ import org.jetbrains.kotlin.util.OperatorNameConventions
1419
1520open 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 )
0 commit comments