Add proper DefaultLambda initialization,
patch lambda related instructions in default method Remove default lambda linked instructions from default method
This commit is contained in:
@@ -316,7 +316,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
ClassId classId = IntrinsicArrayConstructorsKt.getClassId();
|
||||
byte[] bytes =
|
||||
InlineCacheKt.getOrPut(state.getInlineCache().getClassBytes(), classId, IntrinsicArrayConstructorsKt::getBytecode);
|
||||
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), classId);
|
||||
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), classId.asString());
|
||||
}
|
||||
|
||||
assert callableDescriptor instanceof DeserializedCallableMemberDescriptor : "Not a deserialized function or proper: " + callableDescriptor;
|
||||
@@ -339,7 +339,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
}
|
||||
});
|
||||
|
||||
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), containerId);
|
||||
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), containerId.asString());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -88,12 +88,14 @@ public class InlineCodegenUtil {
|
||||
public static final String INLINE_FUN_THIS_0_SUFFIX = "$inline_fun";
|
||||
public static final String INLINE_FUN_VAR_SUFFIX = "$iv";
|
||||
|
||||
public static final String DEFAULT_LAMBDA_FAKE_CALL = "$$$DEFAULT_LAMBDA_FAKE_CALL$$$";
|
||||
|
||||
@Nullable
|
||||
public static SMAPAndMethodNode getMethodNode(
|
||||
byte[] classData,
|
||||
String methodName,
|
||||
String methodDescriptor,
|
||||
ClassId classId
|
||||
@NotNull String methodName,
|
||||
@NotNull String methodDescriptor,
|
||||
@NotNull String classInternalName
|
||||
) {
|
||||
ClassReader cr = new ClassReader(classData);
|
||||
MethodNode[] node = new MethodNode[1];
|
||||
@@ -138,12 +140,12 @@ public class InlineCodegenUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (classId.equals(IntrinsicArrayConstructorsKt.getClassId())) {
|
||||
if (IntrinsicArrayConstructorsKt.getClassId().asString().equals(classInternalName)) {
|
||||
// Don't load source map for intrinsic array constructors
|
||||
debugInfo[0] = null;
|
||||
}
|
||||
|
||||
SMAP smap = SMAPParser.parseOrCreateDefault(debugInfo[1], debugInfo[0], classId.asString(), lines[0], lines[1]);
|
||||
SMAP smap = SMAPParser.parseOrCreateDefault(debugInfo[1], debugInfo[0], classInternalName, lines[0], lines[1]);
|
||||
return new SMAPAndMethodNode(node[0], smap);
|
||||
}
|
||||
|
||||
@@ -169,7 +171,7 @@ public class InlineCodegenUtil {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static VirtualFile findVirtualFileImprecise(@NotNull GenerationState state, @NotNull String internalClassName) {
|
||||
public static VirtualFile findVirtualFileImprecise(@NotNull GenerationState state, @NotNull String internalClassName) {
|
||||
FqName packageFqName = JvmClassName.byInternalName(internalClassName).getPackageFqName();
|
||||
String classNameWithDollars = StringsKt.substringAfterLast(internalClassName, "/", internalClassName);
|
||||
//TODO: we cannot construct proper classId at this point, we need to read InnerClasses info from class file
|
||||
@@ -444,7 +446,6 @@ public class InlineCodegenUtil {
|
||||
|
||||
public static int getConstant(@NotNull AbstractInsnNode ins) {
|
||||
int opcode = ins.getOpcode();
|
||||
Integer value;
|
||||
if (opcode >= Opcodes.ICONST_0 && opcode <= Opcodes.ICONST_5) {
|
||||
return opcode - Opcodes.ICONST_0;
|
||||
}
|
||||
|
||||
@@ -25,15 +25,16 @@ import org.jetbrains.kotlin.codegen.binding.MutableClosure
|
||||
import org.jetbrains.kotlin.codegen.context.EnclosedValueDescriptor
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
@@ -66,7 +67,11 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean, @JvmField val is
|
||||
|
||||
companion object {
|
||||
fun LambdaInfo.getCapturedParamInfo(descriptor: EnclosedValueDescriptor): CapturedParamDesc {
|
||||
return CapturedParamDesc(lambdaClassType, descriptor.fieldName, descriptor.type)
|
||||
return capturedParamDesc(descriptor.fieldName, descriptor.type)
|
||||
}
|
||||
|
||||
fun LambdaInfo.capturedParamDesc(fieldName: String, fieldType: Type): CapturedParamDesc {
|
||||
return CapturedParamDesc(lambdaClassType, fieldName, fieldType)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,22 +81,52 @@ class DefaultLambda(
|
||||
override val lambdaClassType: Type,
|
||||
val capturedArgs: Array<Type>,
|
||||
val parameterDescriptor: ValueParameterDescriptor,
|
||||
val initInstuctions: List<AbstractInsnNode>,
|
||||
val offset: Int
|
||||
) : LambdaInfo(false, false) {
|
||||
override val invokeMethod: Method
|
||||
get() = TODO("not implemented")
|
||||
) : LambdaInfo(parameterDescriptor.isCrossinline, false) {
|
||||
|
||||
override val invokeMethodDescriptor: FunctionDescriptor
|
||||
get() = TODO("not implemented")
|
||||
override lateinit var invokeMethod: Method
|
||||
private set
|
||||
|
||||
override val capturedVars: List<CapturedParamDesc>
|
||||
get() = TODO("not implemented")
|
||||
|
||||
override val invokeMethodDescriptor: FunctionDescriptor =
|
||||
parameterDescriptor.type.memberScope.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND).single()
|
||||
|
||||
|
||||
override lateinit var capturedVars: List<CapturedParamDesc>
|
||||
private set
|
||||
|
||||
override fun isMyLabel(name: String): Boolean = false
|
||||
|
||||
override fun generateLambdaBody(codegen: ExpressionCodegen) {
|
||||
TODO("not implemented")
|
||||
val classReader = InlineCodegenUtil.buildClassReaderByInternalName(codegen.state, lambdaClassType.internalName)
|
||||
|
||||
val descriptor = Type.getMethodDescriptor(Type.VOID_TYPE, *capturedArgs)
|
||||
val constructor = InlineCodegenUtil.getMethodNode(
|
||||
classReader.b,
|
||||
"<init>",
|
||||
descriptor,
|
||||
lambdaClassType.internalName)?.node
|
||||
|
||||
assert(constructor != null || capturedArgs.isEmpty()) {
|
||||
"Can't find non-default constructor <init>$descriptor for default lambda $lambdaClassType"
|
||||
}
|
||||
|
||||
capturedVars = constructor?.findCapturedFieldAssignmentInstructions()?.map {
|
||||
fieldNode ->
|
||||
capturedParamDesc(fieldNode.name, Type.getType(fieldNode.desc))
|
||||
}?.toList() ?: emptyList()
|
||||
|
||||
|
||||
invokeMethod = Method(
|
||||
OperatorNameConventions.INVOKE.asString(),
|
||||
codegen.state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor).asmMethod.descriptor
|
||||
)
|
||||
|
||||
node = InlineCodegenUtil.getMethodNode(
|
||||
classReader.b,
|
||||
invokeMethod.name,
|
||||
invokeMethod.descriptor,
|
||||
lambdaClassType.internalName)!!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,8 +189,7 @@ class MethodInliner(
|
||||
val valueParamShift = Math.max(nextLocalIndex, markerShift)//NB: don't inline cause it changes
|
||||
putStackValuesIntoLocals(listOf(*info.invokeMethod.argumentTypes), valueParamShift, this, desc)
|
||||
|
||||
|
||||
if (invokeCall.lambdaInfo.invokeMethodDescriptor.getValueParameters().isEmpty()) {
|
||||
if (invokeCall.lambdaInfo.invokeMethodDescriptor.valueParameters.isEmpty()) {
|
||||
// There won't be no parameters processing and line call can be left without actual instructions.
|
||||
// Note: if function is called on the line with other instructions like 1 + foo(), 'nop' will still be generated.
|
||||
visitInsn(Opcodes.NOP)
|
||||
|
||||
@@ -90,14 +90,11 @@ private fun MethodInliner.getLambdaIfExistsAndMarkInstructions(
|
||||
return null
|
||||
}
|
||||
|
||||
fun parameterOffsets(valueParameters: List<JvmMethodParameterSignature>): Array<Int> {
|
||||
var offset = 0
|
||||
fun parameterOffsets(isStatic: Boolean, valueParameters: List<JvmMethodParameterSignature>): Array<Int> {
|
||||
var nextOffset = if (isStatic) 0 else 1
|
||||
return Array(valueParameters.size) { index ->
|
||||
if (index == 0) {
|
||||
0
|
||||
}
|
||||
else offset.apply {
|
||||
offset += valueParameters[index - 1].asmType.size
|
||||
nextOffset.also {
|
||||
nextOffset += valueParameters[index].asmType.size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.getMemberOwnerKind
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.DEFAULT_LAMBDA_FAKE_CALL
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getConstant
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.insnText
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
@@ -39,7 +46,14 @@ private data class Condition(
|
||||
|
||||
fun extractDefaultLambdaOffsetAndDescriptor(jvmSignature: JvmMethodSignature, functionDescriptor: FunctionDescriptor): Map<Int, ValueParameterDescriptor> {
|
||||
val valueParameters = jvmSignature.valueParameters
|
||||
val parameterOffsets = parameterOffsets(valueParameters)
|
||||
val containingDeclaration = functionDescriptor.containingDeclaration
|
||||
val kind = containingDeclaration.getMemberOwnerKind().let {
|
||||
if (DescriptorUtils.isInterface(containingDeclaration)) {
|
||||
OwnerKind.DEFAULT_IMPLS
|
||||
}
|
||||
else it
|
||||
}
|
||||
val parameterOffsets = parameterOffsets(AsmUtil.isStaticMethod(kind, functionDescriptor), valueParameters)
|
||||
val valueParameterOffset = valueParameters.takeWhile { it.kind != JvmMethodParameterKind.VALUE }.size
|
||||
|
||||
return functionDescriptor.valueParameters.filter {
|
||||
@@ -81,7 +95,7 @@ fun expandMaskConditionsAndUpdateVariableNodes(
|
||||
val jumpInstruction = it.next?.next?.next as JumpInsnNode
|
||||
Condition(
|
||||
masks[it.`var` - maskStartIndex],
|
||||
InlineCodegenUtil.getConstant(it.next),
|
||||
getConstant(it.next),
|
||||
it,
|
||||
jumpInstruction,
|
||||
jumpInstruction.label.previous as VarInsnNode
|
||||
@@ -97,10 +111,12 @@ fun expandMaskConditionsAndUpdateVariableNodes(
|
||||
else null
|
||||
}.toList()
|
||||
|
||||
val defaultLambdasInfo = extractDefaultLambdasInfo(conditions, defaultLambdas)
|
||||
val toDelete = arrayListOf<AbstractInsnNode>()
|
||||
val toInsert = arrayListOf<Pair<AbstractInsnNode, AbstractInsnNode>>()
|
||||
|
||||
val defaultLambdasInfo = extractDefaultLambdasInfo(conditions, defaultLambdas, toDelete, toInsert)
|
||||
|
||||
val indexToVarNode = node.localVariables?.filter { it.index < maskStartIndex }?.associateBy { it.index } ?: emptyMap()
|
||||
val toDelete = arrayListOf<AbstractInsnNode>()
|
||||
conditions.forEach {
|
||||
val jumpInstruction = it.jumpInstruction
|
||||
InsnSequence(it.maskInstruction, (if (it.expandNotDelete) jumpInstruction.next else jumpInstruction.label)).forEach {
|
||||
@@ -113,51 +129,72 @@ fun expandMaskConditionsAndUpdateVariableNodes(
|
||||
}
|
||||
}
|
||||
|
||||
toDelete.forEach {
|
||||
node.instructions.remove(it)
|
||||
toInsert.forEach { (position, newInsn) ->
|
||||
node.instructions.insert(position, newInsn)
|
||||
}
|
||||
|
||||
node.remove(toDelete)
|
||||
|
||||
return defaultLambdasInfo
|
||||
}
|
||||
|
||||
|
||||
private fun extractDefaultLambdasInfo(conditions: List<Condition>, defaultLambdas: Map<Int, ValueParameterDescriptor>): List<DefaultLambda> {
|
||||
private fun extractDefaultLambdasInfo(
|
||||
conditions: List<Condition>,
|
||||
defaultLambdas: Map<Int, ValueParameterDescriptor>,
|
||||
toDelete: MutableList<AbstractInsnNode>,
|
||||
toInsert: MutableList<Pair<AbstractInsnNode, AbstractInsnNode>>
|
||||
): List<DefaultLambda> {
|
||||
val defaultLambdaConditions = conditions.filter { it.expandNotDelete && defaultLambdas.contains(it.varIndex) }
|
||||
|
||||
return defaultLambdaConditions.map {
|
||||
val varAssignmentInstruction = it.varInsNode!!
|
||||
var instanceInstuction = varAssignmentInstruction.previous
|
||||
if (instanceInstuction is TypeInsnNode && instanceInstuction.opcode == Opcodes.CHECKCAST) {
|
||||
instanceInstuction = instanceInstuction.previous
|
||||
}
|
||||
when (instanceInstuction) {
|
||||
|
||||
val (owner, argTypes) = when (instanceInstuction) {
|
||||
is MethodInsnNode -> {
|
||||
assert(instanceInstuction.name == "<init>") { "Expected constructor call for default lambda, but $instanceInstuction" }
|
||||
val ownerInternalName = instanceInstuction.owner
|
||||
val instanceCreation = InsnSequence(it.jumpInstruction, it.jumpInstruction.label).filter {
|
||||
it.opcode == Opcodes.NEW && (it as TypeInsnNode).desc == ownerInternalName
|
||||
}.single()
|
||||
|
||||
assert(instanceCreation.next?.opcode == Opcodes.DUP) {
|
||||
"Dup should follow default lambda instanceInstuction creation but ${instanceCreation.next}"
|
||||
"Dup should follow default lambda instanceInstruction creation but ${instanceCreation.next}"
|
||||
}
|
||||
|
||||
DefaultLambda(
|
||||
Type.getObjectType(instanceInstuction.owner),
|
||||
Type.getArgumentTypes(instanceInstuction.desc),
|
||||
defaultLambdas[it.varIndex]!!,
|
||||
listOf(instanceCreation, instanceCreation.next) + InsnSequence(instanceInstuction, varAssignmentInstruction).toList(),
|
||||
it.varIndex
|
||||
)
|
||||
toDelete.apply {
|
||||
addAll(listOf(instanceCreation, instanceCreation.next))
|
||||
addAll(InsnSequence(instanceInstuction, varAssignmentInstruction.next).toList())
|
||||
}
|
||||
|
||||
Type.getObjectType(instanceInstuction.owner) to Type.getArgumentTypes(instanceInstuction.desc)
|
||||
}
|
||||
|
||||
is FieldInsnNode -> DefaultLambda(
|
||||
Type.getObjectType(instanceInstuction.owner),
|
||||
emptyArray<Type>(),
|
||||
defaultLambdas[it.varIndex]!!,
|
||||
InsnSequence(instanceInstuction, varAssignmentInstruction).toList(),
|
||||
it.varIndex
|
||||
)
|
||||
else -> throw RuntimeException("Can't extract default lambda info $it.\n Unknown instruction: $instanceInstuction")
|
||||
is FieldInsnNode -> {
|
||||
toDelete.addAll(InsnSequence(instanceInstuction, varAssignmentInstruction.next).toList())
|
||||
|
||||
Type.getObjectType(instanceInstuction.owner) to emptyArray<Type>()
|
||||
}
|
||||
else -> throw RuntimeException("Can't extract default lambda info $it.\n Unknown instruction: ${instanceInstuction.insnText}")
|
||||
}
|
||||
|
||||
toInsert.add(varAssignmentInstruction to defaultLambdaFakeCallStub(argTypes, it.varIndex))
|
||||
|
||||
DefaultLambda(owner, argTypes, defaultLambdas[it.varIndex]!!, it.varIndex)
|
||||
}
|
||||
}
|
||||
|
||||
//marker that removes captured parameters from stack
|
||||
//at inlining it would be substituted with parameters store
|
||||
private fun defaultLambdaFakeCallStub(args: Array<Type>, lambdaOffset: Int): MethodInsnNode {
|
||||
return MethodInsnNode(
|
||||
Opcodes.INVOKESTATIC,
|
||||
DEFAULT_LAMBDA_FAKE_CALL,
|
||||
DEFAULT_LAMBDA_FAKE_CALL + lambdaOffset,
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, *args),
|
||||
false
|
||||
)
|
||||
}
|
||||
+2
-2
@@ -270,9 +270,9 @@ val IrFunction.OtherOrigin: JvmDeclarationOrigin
|
||||
get() = OtherOrigin(descriptor.psiElement, this.descriptor)
|
||||
|
||||
fun DeclarationDescriptor.getMemberOwnerKind(): OwnerKind = when (this) {
|
||||
is FileClassDescriptor ->
|
||||
is FileClassDescriptor,
|
||||
is PackageFragmentDescriptor ->
|
||||
OwnerKind.PACKAGE
|
||||
is PackageFragmentDescriptor,
|
||||
is ClassDescriptor ->
|
||||
OwnerKind.IMPLEMENTATION
|
||||
else ->
|
||||
|
||||
Reference in New Issue
Block a user