Generate fake variables for default lambdas during inline

#KT-51557 Fixed
This commit is contained in:
Mikhael Bogdanov
2022-03-07 13:41:00 +01:00
committed by Space
parent 5e09c3176f
commit 6b00b51e6d
6 changed files with 63 additions and 7 deletions
@@ -77,7 +77,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
.mapTo(mutableSetOf()) { parameters.getDeclarationSlot(it) }
)
for (info in infos) {
val lambda = DefaultLambda(info, sourceCompiler)
val lambda = DefaultLambda(info, sourceCompiler, node.name.substringBeforeLast("\$default"))
parameters.getParameterByDeclarationSlot(info.offset).functionalArgument = lambda
if (info.needReification) {
lambda.reifiedTypeParametersUsages.mergeAll(reifiedTypeInliner.reifyInstructions(lambda.node.node))
@@ -7,13 +7,12 @@ package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.coroutines.isCoroutineSuperClass
import org.jetbrains.kotlin.load.java.JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
import org.jetbrains.org.objectweb.asm.tree.*
interface FunctionalArgument
@@ -75,7 +74,8 @@ abstract class ExpressionLambda : LambdaInfo() {
}
}
class DefaultLambda(info: ExtractedDefaultLambda, sourceCompiler: SourceCompilerForInline) : LambdaInfo() {
class DefaultLambda(info: ExtractedDefaultLambda, sourceCompiler: SourceCompilerForInline, private val functionName: String) :
LambdaInfo() {
val isBoundCallableReference: Boolean
override val lambdaClassType: Type = info.type
@@ -132,7 +132,30 @@ class DefaultLambda(info: ExtractedDefaultLambda, sourceCompiler: SourceCompiler
capturedParamDesc(fieldNode.name, Type.getType(fieldNode.desc), isSuspend = false)
}?.toList() ?: emptyList()
isBoundCallableReference = isReference && capturedVars.isNotEmpty()
node = loadDefaultLambdaBody(classBytes, lambdaClassType, isPropertyReference)
val (originNode, classSmap) = loadDefaultLambdaBody(classBytes, lambdaClassType, isPropertyReference)
node = SMAPAndMethodNode(createNodeWithFakeVariables(originNode), classSmap)
}
private fun createNodeWithFakeVariables(originNode: MethodNode): MethodNode {
val withFakeVariable =
MethodNode(originNode.access, originNode.name, originNode.desc, originNode.signature, originNode.exceptions?.toTypedArray())
val fakeVarIndex = originNode.maxLocals
originNode.accept(withFakeVariable)
val startLabel =
withFakeVariable.instructions.first as? LabelNode ?: LabelNode().apply { withFakeVariable.instructions.insert(this) }
val endLabel = withFakeVariable.instructions.last as? LabelNode ?: LabelNode().apply { withFakeVariable.instructions.add(this) }
withFakeVariable.instructions.insert(startLabel, VarInsnNode(Opcodes.ISTORE, fakeVarIndex))
withFakeVariable.instructions.insert(startLabel, LdcInsnNode(0))
withFakeVariable.localVariables.add(
LocalVariableNode(
"$LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT-$functionName-" + lambdaClassType.internalName.substringAfterLast(
'/'
), Type.INT_TYPE.descriptor, null, startLabel, endLabel, fakeVarIndex
)
)
withFakeVariable.maxLocals = withFakeVariable.maxLocals + 1
return withFakeVariable
}
private companion object {
@@ -3442,6 +3442,12 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/inline"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("defaultLambda.kt")
public void testDefaultLambda() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/inline/defaultLambda.kt");
}
@Test
@TestMetadata("deleteClassOnTransformation.kt")
public void testDeleteClassOnTransformation() throws Exception {
@@ -0,0 +1,15 @@
package test
inline fun <reified T> inlineFun(lambda: () -> String = { T::class.java.simpleName }): String {
return lambda()
}
class OK
fun box(): String {
return inlineFun<OK>()
}
// 1 LOCALVARIABLE \$i\$a\$-inlineFun-DefaultLambdaKt\$inlineFun\$1 I
// inlineFun, inlineFun$default, inlined inlineFun:
// 3 LOCALVARIABLE \$i\$f\$inlineFun
@@ -3172,6 +3172,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/inline"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@Test
@TestMetadata("defaultLambda.kt")
public void testDefaultLambda() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/inline/defaultLambda.kt");
}
@Test
@TestMetadata("deleteClassOnTransformation.kt")
public void testDeleteClassOnTransformation() throws Exception {
@@ -3442,6 +3442,12 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/inline"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("defaultLambda.kt")
public void testDefaultLambda() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/inline/defaultLambda.kt");
}
@Test
@TestMetadata("deleteClassOnTransformation.kt")
public void testDeleteClassOnTransformation() throws Exception {