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

Skip to content

Commit 3cd2583

Browse files
tamasvajkigfoo
authored andcommitted
Handle large arity lambdas, and add missing type access for some constructor calls (needed for anonymous classes)
1 parent f4c87cb commit 3cd2583

4 files changed

Lines changed: 410 additions & 122 deletions

File tree

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

Lines changed: 191 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ open class KotlinFileExtractor(
178178
val parentId =
179179
if (parent.isAnonymousObject) {
180180
@Suppress("UNCHECKED_CAST")
181-
useAnonymousClass(c).javaResult.id as Label<out DbClass>
181+
useAnonymousClass(parent).javaResult.id as Label<out DbClass>
182182
} else {
183183
useClassInstance(parent, listOf()).typeResult.id
184184
}
@@ -260,12 +260,11 @@ open class KotlinFileExtractor(
260260
}
261261

262262
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)
263+
return extractValueParameter(useValueParameter(vp), vp.type, vp.name.asString(), tw.getLocation(vp), parent, idx)
264264
}
265265

266-
private fun extractValueParameter(id: Label<out DbParam>, t: IrType, name: String, loc: IrElement, parent: Label<out DbCallable>, idx: Int): TypeResults {
266+
private fun extractValueParameter(id: Label<out DbParam>, t: IrType, name: String, locId: Label<DbLocation>, parent: Label<out DbCallable>, idx: Int): TypeResults {
267267
val type = useType(t)
268-
val locId = tw.getLocation(loc)
269268
tw.writeParams(id, type.javaResult.id, type.kotlinResult.id, idx, parent, id)
270269
tw.writeHasLocation(id, locId)
271270
tw.writeParamName(id, name)
@@ -721,6 +720,15 @@ open class KotlinFileExtractor(
721720
tw.writeStatementEnclosingExpr(idNewexpr, enclosingStmt)
722721
tw.writeCallableBinding(idNewexpr, ids.constructor)
723722

723+
@Suppress("UNCHECKED_CAST")
724+
tw.writeIsAnonymClass(ids.type.javaResult.id as Label<DbClass>, idNewexpr)
725+
726+
val typeAccessId = tw.getFreshIdLabel<DbUnannotatedtypeaccess>()
727+
val anyType = useType(pluginContext.irBuiltIns.anyType)
728+
tw.writeExprs_unannotatedtypeaccess(typeAccessId, anyType.javaResult.id, anyType.kotlinResult.id, idNewexpr, -3)
729+
tw.writeCallableEnclosingExpr(typeAccessId, callable)
730+
tw.writeStatementEnclosingExpr(typeAccessId, enclosingStmt)
731+
724732
} else {
725733
val methodId = useFunction<DbMethod>(callTarget)
726734
tw.writeCallableBinding(id, methodId)
@@ -1644,68 +1652,166 @@ open class KotlinFileExtractor(
16441652
functionN(pluginContext)(parameters.size).typeWith(types)
16451653
}
16461654

1655+
val lambdaType = pluginContext.referenceClass(FqName("kotlin.jvm.internal.Lambda"))!!.typeWith()
1656+
/*
1657+
* Extract generated class:
1658+
* ```
1659+
* class C : kotlin.jvm.internal.Lambda, kotlin.FunctionI<T0,T1, ... TI, R> {
1660+
* constructor() { super(I); }
1661+
* fun invoke(a0:T0, a1:T1, ... aI: TI): R { ... }
1662+
* }
1663+
* ```
1664+
* or in case of big arity lambdas
1665+
* ```
1666+
* class C : kotlin.jvm.internal.Lambda, kotlin.FunctionN<R> {
1667+
* constructor() { super(I); }
1668+
* fun invoke(a0:T0, a1:T1, ... aI: TI): R { ... }
1669+
* fun invoke(vararg args: Any?): R {
1670+
* return invoke(args[0] as T0, args[1] as T1, ..., args[I] as TI)
1671+
* }
1672+
* }
1673+
* ```
1674+
* */
16471675
extractGeneratedClass(
16481676
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),
1677+
listOf(lambdaType, fnInterface),
16521678
listOf(e.function.valueParameters.size.toIrConst(pluginContext.irBuiltIns.intType, e.startOffset, e.endOffset)))
16531679

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-
}
1680+
if (types.size > BuiltInFunctionArity.BIG_ARITY) {
1681+
implementFunctionNInvoke(e.function, ids, locId, parameters)
16721682

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-
// ```
1683+
// todo: which method should be returned in `LambdaExpr.asMethod()`?
16921684
}
16931685

16941686
val exprParent = parent.expr(e, callable)
1687+
val idLambdaExpr = tw.getFreshIdLabel<DbLambdaexpr>()
1688+
tw.writeExprs_lambdaexpr(idLambdaExpr, ids.type.javaResult.id, ids.type.kotlinResult.id, exprParent.parent, exprParent.idx)
1689+
tw.writeHasLocation(idLambdaExpr, locId)
1690+
tw.writeCallableEnclosingExpr(idLambdaExpr, callable)
1691+
tw.writeStatementEnclosingExpr(idLambdaExpr, exprParent.enclosingStmt)
1692+
tw.writeCallableBinding(idLambdaExpr, ids.constructor)
1693+
1694+
val typeAccessId = tw.getFreshIdLabel<DbUnannotatedtypeaccess>()
1695+
// todo: in Java, we're accessing the base functional interface type.
1696+
val typeAccessType = useType(lambdaType)
1697+
tw.writeExprs_unannotatedtypeaccess(typeAccessId, typeAccessType.javaResult.id, typeAccessType.kotlinResult.id, idLambdaExpr, -3)
1698+
tw.writeCallableEnclosingExpr(typeAccessId, callable)
1699+
tw.writeStatementEnclosingExpr(typeAccessId, exprParent.enclosingStmt)
1700+
1701+
// todo: fix hard coded block body of lambda
1702+
tw.writeLambdaKind(idLambdaExpr, 1)
16951703

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)
1704+
@Suppress("UNCHECKED_CAST")
1705+
tw.writeIsAnonymClass(ids.type.javaResult.id as Label<DbClass>, idLambdaExpr)
17021706
}
17031707
else -> {
17041708
logger.warnElement(Severity.ErrorSevere, "Unrecognised IrExpression: " + e.javaClass, e)
17051709
}
17061710
}
17071711
}
17081712

1713+
/*
1714+
* This function generates an implementation for `fun kotlin.FunctionN<R>.invoke(vararg args: Any?): R`
1715+
*
1716+
* The following body is added:
1717+
* ```
1718+
* fun invoke(vararg args: Any?): R {
1719+
* return invoke(args[0] as T0, args[1] as T1, ..., args[I] as TI)
1720+
* }
1721+
* ```
1722+
* */
1723+
private fun implementFunctionNInvoke(
1724+
lambda: IrFunction,
1725+
ids: LocalFunctionLabels,
1726+
locId: Label<DbLocation>,
1727+
parameters: List<IrValueParameter>
1728+
) {
1729+
val methodId = tw.getFreshIdLabel<DbMethod>()
1730+
1731+
val argsParamId = tw.getFreshIdLabel<DbParam>()
1732+
val argsParamType = pluginContext.irBuiltIns.arrayClass.typeWith(pluginContext.irBuiltIns.anyNType)
1733+
val paramType = extractValueParameter(argsParamId, argsParamType, "args", locId, methodId, 0)
1734+
1735+
val paramsSignature = "(${paramType.javaResult.signature!!})"
1736+
1737+
val returnType = useType(lambda.returnType, TypeContext.RETURN)
1738+
val shortName = OperatorNameConventions.INVOKE.asString()
1739+
@Suppress("UNCHECKED_CAST")
1740+
tw.writeMethods(methodId, shortName, "$shortName$paramsSignature", returnType.javaResult.id, returnType.kotlinResult.id, ids.type.javaResult.id as Label<out DbReftype>, methodId)
1741+
tw.writeHasLocation(methodId, locId)
1742+
1743+
// Block
1744+
val blockId = tw.getFreshIdLabel<DbBlock>()
1745+
tw.writeStmts_block(blockId, methodId, 0, methodId)
1746+
tw.writeHasLocation(blockId, locId)
1747+
1748+
// Return
1749+
val retId = tw.getFreshIdLabel<DbReturnstmt>()
1750+
tw.writeStmts_returnstmt(retId, blockId, 0, methodId)
1751+
tw.writeHasLocation(retId, locId)
1752+
1753+
fun extractCommonExpr(id: Label<out DbExpr>) {
1754+
tw.writeHasLocation(id, locId)
1755+
tw.writeCallableEnclosingExpr(id, methodId)
1756+
tw.writeStatementEnclosingExpr(id, retId)
1757+
}
1758+
1759+
// Call to original `invoke`:
1760+
val callId = tw.getFreshIdLabel<DbMethodaccess>()
1761+
val callType = useType(lambda.returnType)
1762+
tw.writeExprs_methodaccess(callId, callType.javaResult.id, callType.kotlinResult.id, retId, 0)
1763+
extractCommonExpr(callId)
1764+
val calledMethodId = useFunction<DbMethod>(lambda)
1765+
tw.writeCallableBinding(callId, calledMethodId)
1766+
1767+
// this access
1768+
val thisId = tw.getFreshIdLabel<DbThisaccess>()
1769+
tw.writeExprs_thisaccess(thisId, ids.type.javaResult.id, ids.type.kotlinResult.id, callId, -1)
1770+
extractCommonExpr(thisId)
1771+
1772+
// parameters
1773+
val intType = useType(pluginContext.irBuiltIns.intType)
1774+
val argsType = useType(argsParamType)
1775+
val anyNType = useType(pluginContext.irBuiltIns.anyNType)
1776+
1777+
val func =
1778+
pluginContext.irBuiltIns.arrayClass.owner.declarations.find { it is IrFunction && it.name.asString() == "get" }
1779+
1780+
@Suppress("UNCHECKED_CAST")
1781+
val arrayGetMethodId = useFunction<DbMethod>(func as IrFunction)
1782+
1783+
for ((pIdx, p) in parameters.withIndex()) {
1784+
// `args[i] as Ti` is generated below for each parameter
1785+
1786+
// cast
1787+
val castId = tw.getFreshIdLabel<DbCastexpr>()
1788+
val type = useType(p.type)
1789+
tw.writeExprs_castexpr(castId, type.javaResult.id, type.kotlinResult.id, callId, pIdx)
1790+
extractCommonExpr(castId)
1791+
1792+
// type access
1793+
extractTypeAccess(p.type, locId, methodId, castId, 0, retId)
1794+
1795+
// element access: `args.get(i)`
1796+
val getCallId = tw.getFreshIdLabel<DbMethodaccess>()
1797+
tw.writeExprs_methodaccess(getCallId, anyNType.javaResult.id, anyNType.kotlinResult.id, castId, 1)
1798+
extractCommonExpr(getCallId)
1799+
tw.writeCallableBinding(getCallId, arrayGetMethodId)
1800+
1801+
// parameter access:
1802+
val argsAccessId = tw.getFreshIdLabel<DbVaraccess>()
1803+
tw.writeExprs_varaccess(argsAccessId, argsType.javaResult.id, argsType.kotlinResult.id, getCallId, -1)
1804+
extractCommonExpr(argsAccessId)
1805+
tw.writeVariableBinding(argsAccessId, argsParamId)
1806+
1807+
// index access:
1808+
val indexId = tw.getFreshIdLabel<DbIntegerliteral>()
1809+
tw.writeExprs_integerliteral(indexId, intType.javaResult.id, intType.kotlinResult.id, getCallId, pIdx)
1810+
extractCommonExpr(indexId)
1811+
tw.writeNamestrings(pIdx.toString(), pIdx.toString(), indexId)
1812+
}
1813+
}
1814+
17091815
fun extractVarargElement(e: IrVarargElement, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>) {
17101816
when(e) {
17111817
is IrExpression -> {
@@ -1717,19 +1823,22 @@ open class KotlinFileExtractor(
17171823
}
17181824
}
17191825

1720-
fun extractTypeAccess(t: IrType, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, elementForLocation: IrElement, enclosingStmt: Label<out DbStmt>) {
1826+
private fun extractTypeAccess(t: IrType, location: Label<DbLocation>, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>) {
17211827
// TODO: elementForLocation allows us to give some sort of
17221828
// location, but a proper location for the type access will
17231829
// require upstream changes
17241830
val type = useType(t)
17251831
val id = tw.getFreshIdLabel<DbUnannotatedtypeaccess>()
17261832
tw.writeExprs_unannotatedtypeaccess(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
1727-
val locId = tw.getLocation(elementForLocation)
1728-
tw.writeHasLocation(id, locId)
1833+
tw.writeHasLocation(id, location)
17291834
tw.writeCallableEnclosingExpr(id, callable)
17301835
tw.writeStatementEnclosingExpr(id, enclosingStmt)
17311836
}
17321837

1838+
private fun extractTypeAccess(t: IrType, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, elementForLocation: IrElement, enclosingStmt: Label<out DbStmt>) {
1839+
extractTypeAccess(t, tw.getLocation(elementForLocation), callable, parent, idx, enclosingStmt)
1840+
}
1841+
17331842
fun extractTypeOperatorCall(e: IrTypeOperatorCall, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>) {
17341843
when(e.operator) {
17351844
IrTypeOperator.CAST -> {
@@ -1867,7 +1976,7 @@ open class KotlinFileExtractor(
18671976

18681977
// Super call
18691978
val superCallId = tw.getFreshIdLabel<DbSuperconstructorinvocationstmt>()
1870-
tw.writeStmts_superconstructorinvocationstmt(superCallId, constructorBlockId, 0, ids.function)
1979+
tw.writeStmts_superconstructorinvocationstmt(superCallId, constructorBlockId, 0, ids.constructor)
18711980
for (i in 0 until superConstructorArgs.size) {
18721981
val arg = superConstructorArgs[i]
18731982
extractExpressionExpr(arg, ids.constructor, superCallId, i, superCallId)
@@ -1885,6 +1994,34 @@ open class KotlinFileExtractor(
18851994
addModifiers(id, "public", "static", "final")
18861995
extractClassSupertypes(superTypes, listOf(), id)
18871996

1997+
var parent: IrDeclarationParent? = localFunction.parent
1998+
while (parent != null) {
1999+
// todo: merge this with the implementation in `extractClassSource`
2000+
if (parent is IrClass) {
2001+
val parentId =
2002+
if (parent.isAnonymousObject) {
2003+
@Suppress("UNCHECKED_CAST")
2004+
useAnonymousClass(parent).javaResult.id as Label<out DbClass>
2005+
} else {
2006+
useClassInstance(parent, listOf()).typeResult.id
2007+
}
2008+
tw.writeEnclInReftype(id, parentId)
2009+
2010+
break
2011+
}
2012+
2013+
if (parent is IrFile) {
2014+
if (this is KotlinSourceFileExtractor && this.file == localFunction.fileOrNull) {
2015+
tw.writeEnclInReftype(id, this.fileClass)
2016+
} else {
2017+
logger.warn(Severity.ErrorSevere, "Unexpected file parent found")
2018+
}
2019+
break
2020+
}
2021+
2022+
parent = (parent as? IrDeclaration)?.parent
2023+
}
2024+
18882025
return id
18892026
}
18902027
}

0 commit comments

Comments
 (0)