diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java index 8d688fe96dc..871991b52d8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -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 diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenUtil.java index b57fee6df37..cda41e2bdaf 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenUtil.java @@ -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; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt index c8ec89ff5d2..fa8614cdb07 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt @@ -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, val parameterDescriptor: ValueParameterDescriptor, - val initInstuctions: List, 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 - get() = TODO("not implemented") + + override val invokeMethodDescriptor: FunctionDescriptor = + parameterDescriptor.type.memberScope.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND).single() + + + override lateinit var capturedVars: List + 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, + "", + descriptor, + lambdaClassType.internalName)?.node + + assert(constructor != null || capturedArgs.isEmpty()) { + "Can't find non-default constructor $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)!! } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt index e21168836e5..c1bfd7b143a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -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) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt index 817152ac0cf..a2c5ad30260 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInlinerUtil.kt @@ -90,14 +90,11 @@ private fun MethodInliner.getLambdaIfExistsAndMarkInstructions( return null } -fun parameterOffsets(valueParameters: List): Array { - var offset = 0 +fun parameterOffsets(isStatic: Boolean, valueParameters: List): Array { + 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 } } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/defaultMethodUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/defaultMethodUtil.kt index 0758272b5bb..ba9bbd9c47e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/defaultMethodUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/defaultMethodUtil.kt @@ -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 { 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() + val toInsert = arrayListOf>() + + val defaultLambdasInfo = extractDefaultLambdasInfo(conditions, defaultLambdas, toDelete, toInsert) val indexToVarNode = node.localVariables?.filter { it.index < maskStartIndex }?.associateBy { it.index } ?: emptyMap() - val toDelete = arrayListOf() 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, defaultLambdas: Map): List { +private fun extractDefaultLambdasInfo( + conditions: List, + defaultLambdas: Map, + toDelete: MutableList, + toInsert: MutableList> +): List { 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 == "") { "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(), - 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() + } + 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, lambdaOffset: Int): MethodInsnNode { + return MethodInsnNode( + Opcodes.INVOKESTATIC, + DEFAULT_LAMBDA_FAKE_CALL, + DEFAULT_LAMBDA_FAKE_CALL + lambdaOffset, + Type.getMethodDescriptor(Type.VOID_TYPE, *args), + false + ) } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index 7dbb729db8e..a7d36d64f85 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt @@ -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 ->