diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index e7aa52773f9..6b85a63e023 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -28,10 +28,7 @@ import org.jetbrains.kotlin.resolve.inline.isInlineOnly import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource -import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext -import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral -import org.jetbrains.kotlin.types.expressions.LabelResolver import org.jetbrains.kotlin.types.model.TypeParameterMarker import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Opcodes @@ -82,7 +79,6 @@ abstract class InlineCodegen( } } - protected fun throwCompilationException( nodeAndSmap: SMAPAndMethodNode?, e: Exception, generateNodeText: Boolean ): CompilationException { @@ -150,8 +146,7 @@ abstract class InlineCodegen( val insns = methodNode.instructions.toArray() for (i in insns.indices) { - val insn = insns[i] - when (insn) { + when (val insn = insns[i]) { is JumpInsnNode -> if (isBackwardJump(i, insn.label)) return false @@ -406,12 +401,10 @@ abstract class InlineCodegen( if (kind !== ValueKind.DEFAULT_MASK && kind !== ValueKind.METHOD_HANDLE_IN_DEFAULT) { return false } - assert(value is StackValue.Constant) { - "Additional default method argument should be constant, but " + value - } + assert(value is StackValue.Constant) { "Additional default method argument should be constant, but $value" } val constantValue = (value as StackValue.Constant).value if (kind === ValueKind.DEFAULT_MASK) { - assert(constantValue is Int) { "Mask should be of Integer type, but " + constantValue } + assert(constantValue is Int) { "Mask should be of Integer type, but $constantValue" } maskValues.add(constantValue as Int) if (maskStartIndex == -1) { maskStartIndex = invocationParamBuilder.listAllParams().sumOf { @@ -510,8 +503,7 @@ abstract class InlineCodegen( doCreateMethodNodeFromCompiled(directMember, state, jvmSignature.asmMethod) else null - result ?: - throw IllegalStateException("Couldn't obtain compiled function body for $functionDescriptor") + result ?: throw IllegalStateException("Couldn't obtain compiled function body for $functionDescriptor") } return SMAPAndMethodNode(cloneMethodNode(resultInCache.node), resultInCache.classSMAP) @@ -592,25 +584,5 @@ abstract class InlineCodegen( InlineUtil.isInlineParameter(field.descriptor) && InlineUtil.isInline(field.descriptor.containingDeclaration) } - - fun getDeclarationLabels(lambdaOrFun: PsiElement?, descriptor: DeclarationDescriptor): Set { - val result = HashSet() - - if (lambdaOrFun != null) { - val label = LabelResolver.getLabelNameIfAny(lambdaOrFun) - if (label != null) { - result.add(label.asString()) - } - } - - if (!isFunctionLiteral(descriptor)) { - if (!descriptor.name.isSpecial) { - result.add(descriptor.name.asString()) - } - result.add(FIRST_FUN_LABEL) - } - return result - } } - } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt index fb00a0f220b..3a87f6639b1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt @@ -290,7 +290,7 @@ class PsiExpressionLambda( closure = bindingContext.get(CodegenBinding.CLOSURE, classDescriptor) ?: throw AssertionError("null closure for lambda ${expression.text}") - returnLabels = InlineCodegen.getDeclarationLabels(expression, invokeMethodDescriptor).associateWith { null } + returnLabels = getDeclarationLabels(expression, invokeMethodDescriptor).associateWith { null } invokeMethod = typeMapper.mapAsmMethod(invokeMethodDescriptor) isSuspend = invokeMethodDescriptor.isSuspend } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiSourceCompilerForInline.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiSourceCompilerForInline.kt new file mode 100644 index 00000000000..e6cd279bedd --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiSourceCompilerForInline.kt @@ -0,0 +1,404 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen.inline + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.backend.common.CodegenUtil +import org.jetbrains.kotlin.codegen.* +import org.jetbrains.kotlin.codegen.context.* +import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.config.isReleaseCoroutines +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.incremental.KotlinLookupLocation +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert +import org.jetbrains.kotlin.resolve.isInlineClass +import org.jetbrains.kotlin.resolve.jvm.annotations.isCallableMemberCompiledToJvmDefault +import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils +import org.jetbrains.kotlin.types.expressions.LabelResolver +import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.MethodVisitor +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.Method +import org.jetbrains.org.objectweb.asm.tree.MethodNode +import kotlin.properties.Delegates + +class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, override val callElement: KtElement) : + SourceCompilerForInline { + + override val state = codegen.state + + private var context by Delegates.notNull>() + + private var additionalInnerClasses = mutableListOf() + + override val lookupLocation = KotlinLookupLocation(callElement) + + + override val callElementText: String by lazy { + callElement.text + } + + override val callsiteFile by lazy { + callElement.containingFile + } + + override val contextKind + get () = context.contextKind + + override val inlineCallSiteInfo: InlineCallSiteInfo + get() { + var context = codegen.getContext() + var parentCodegen = codegen.parentCodegen + while (context is InlineLambdaContext) { + val closureContext = context.getParentContext() + assert(closureContext is ClosureContext) { "Parent context of inline lambda should be closure context" } + assert(closureContext.parentContext is MethodContext) { "Closure context should appear in method context" } + context = closureContext.parentContext as MethodContext + assert(parentCodegen is FakeMemberCodegen) { "Parent codegen of inlined lambda should be FakeMemberCodegen" } + parentCodegen = (parentCodegen as FakeMemberCodegen).delegate + } + + val signature = codegen.state.typeMapper.mapSignatureSkipGeneric(context.functionDescriptor, context.contextKind) + return InlineCallSiteInfo( + parentCodegen.className, + signature.asmMethod.name, + signature.asmMethod.descriptor, + compilationContextFunctionDescriptor.isInlineOrInsideInline(), + compilationContextFunctionDescriptor.isSuspend, + CodegenUtil.getLineNumberForElement(callElement, false) ?: 0 + ) + } + + override val lazySourceMapper + get() = codegen.parentCodegen.orCreateSourceMapper + + override fun generateLambdaBody(lambdaInfo: ExpressionLambda, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode { + lambdaInfo as? PsiExpressionLambda ?: error("TODO") + val invokeMethodDescriptor = lambdaInfo.invokeMethodDescriptor + val jvmMethodSignature = state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor) + val asmMethod = jvmMethodSignature.asmMethod + val methodNode = MethodNode( + Opcodes.API_VERSION, DescriptorAsmUtil.getMethodAsmFlags(invokeMethodDescriptor, OwnerKind.IMPLEMENTATION, state), + asmMethod.name, asmMethod.descriptor, null, null + ) + val adapter = wrapWithMaxLocalCalc(methodNode) + val closureContext = when { + lambdaInfo.isPropertyReference -> + codegen.getContext().intoAnonymousClass(lambdaInfo.classDescriptor, codegen, OwnerKind.IMPLEMENTATION) + invokeMethodDescriptor.isSuspend -> + codegen.getContext().intoCoroutineClosure( + getOrCreateJvmSuspendFunctionView(invokeMethodDescriptor, state), invokeMethodDescriptor, codegen, state.typeMapper + ) + else -> codegen.getContext().intoClosure(invokeMethodDescriptor, codegen, state.typeMapper) + } + val context = closureContext.intoInlinedLambda(invokeMethodDescriptor, lambdaInfo.isCrossInline, lambdaInfo.isPropertyReference) + val smap = generateMethodBody( + adapter, invokeMethodDescriptor, context, + lambdaInfo.functionWithBodyOrCallableReference, + jvmMethodSignature, lambdaInfo + ) + adapter.visitMaxs(-1, -1) + return SMAPAndMethodNode(methodNode, smap) + } + + private fun generateMethodBody( + adapter: MethodVisitor, + descriptor: FunctionDescriptor, + context: MethodContext, + expression: KtExpression, + jvmMethodSignature: JvmMethodSignature, + lambdaInfo: PsiExpressionLambda? + ): SMAP { + val isLambda = lambdaInfo != null + + // Wrapping for preventing marking actual parent codegen as containing reified markers + val parentCodegen = FakeMemberCodegen( + codegen.parentCodegen, expression, context.parentContext as FieldOwnerContext<*>, + if (isLambda) + codegen.parentCodegen.className + else + state.typeMapper.mapImplementationOwner(descriptor).internalName, + if (isLambda) emptyList() else additionalInnerClasses, + isLambda + ) + + val strategy = when (expression) { + is KtCallableReferenceExpression -> { + val resolvedCall = expression.callableReference.getResolvedCallWithAssert(state.bindingContext) + val receiverKotlinType = JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)?.type + val receiverType = receiverKotlinType?.let(state.typeMapper::mapType) + val boundReceiverJvmKotlinType = receiverType?.let { JvmKotlinType(receiverType, receiverKotlinType) } + + if (isLambda && lambdaInfo!!.isPropertyReference) { + val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor) + val info = lambdaInfo.propertyReferenceInfo + PropertyReferenceCodegen.PropertyReferenceGenerationStrategy( + true, info!!.getFunction, info.target, asmType, + boundReceiverJvmKotlinType, + lambdaInfo.functionWithBodyOrCallableReference, state, true + ) + } else { + FunctionReferenceGenerationStrategy(state, descriptor, resolvedCall, boundReceiverJvmKotlinType, null, true) + } + } + is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody) + else -> FunctionGenerationStrategy.FunctionDefault(state, expression as KtDeclarationWithBody) + } + + FunctionCodegen.generateMethodBody( + adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen, state.jvmDefaultMode, + state.languageVersionSettings.isReleaseCoroutines() + ) + + if (isLambda) { + codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages) + } + + return SMAP(parentCodegen.orCreateSourceMapper.resultMappings) + } + + @Suppress("UNCHECKED_CAST") + private class FakeMemberCodegen( + val delegate: MemberCodegen<*>, + declaration: KtElement, + codegenContext: FieldOwnerContext<*>, + private val className: String, + private val parentAsInnerClasses: List, + private val isInlineLambdaCodegen: Boolean + ) : MemberCodegen(delegate as MemberCodegen, declaration, codegenContext) { + + override fun generateDeclaration() { + throw IllegalStateException() + } + + override fun generateBody() { + throw IllegalStateException() + } + + override fun generateKotlinMetadataAnnotation() { + throw IllegalStateException() + } + + override fun getInlineNameGenerator(): NameGenerator { + return delegate.inlineNameGenerator + } + + override //TODO: obtain name from context + fun getClassName(): String { + return className + } + + override fun addParentsToInnerClassesIfNeeded(innerClasses: MutableCollection) { + if (isInlineLambdaCodegen) { + super.addParentsToInnerClassesIfNeeded(innerClasses) + } else { + innerClasses.addAll(parentAsInnerClasses) + } + } + + override fun generateAssertField() { + delegate.generateAssertField() + } + } + + override fun doCreateMethodNodeFromSource( + callableDescriptor: FunctionDescriptor, + jvmSignature: JvmMethodSignature, + callDefault: Boolean, + asmMethod: Method + ): SMAPAndMethodNode { + val element = DescriptorToSourceUtils.descriptorToDeclaration(callableDescriptor) + + if (!(element is KtNamedFunction || element is KtPropertyAccessor)) { + throw IllegalStateException("Couldn't find declaration for function $callableDescriptor") + } + val inliningFunction = element as KtDeclarationWithBody? + + val node = MethodNode( + Opcodes.API_VERSION, + DescriptorAsmUtil.getMethodAsmFlags(callableDescriptor, context.contextKind, state) or if (callDefault) Opcodes.ACC_STATIC else 0, + asmMethod.name, + asmMethod.descriptor, null, null + ) + + //for maxLocals calculation + val maxCalcAdapter = wrapWithMaxLocalCalc(node) + val parentContext = context.parentContext ?: error("Context has no parent: $context") + val methodContext = parentContext.intoFunction(callableDescriptor) + + val smap = if (callDefault) { + val implementationOwner = state.typeMapper.mapImplementationOwner(callableDescriptor) + val parentCodegen = FakeMemberCodegen( + codegen.parentCodegen, inliningFunction!!, methodContext.parentContext as FieldOwnerContext<*>, + implementationOwner.internalName, + additionalInnerClasses, + false + ) + if (element !is KtNamedFunction) { + throw IllegalStateException("Property accessors with default parameters not supported $callableDescriptor") + } + FunctionCodegen.generateDefaultImplBody( + methodContext, callableDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT, + inliningFunction as KtNamedFunction?, parentCodegen, asmMethod + ) + SMAP(parentCodegen.orCreateSourceMapper.resultMappings) + } else { + generateMethodBody(maxCalcAdapter, callableDescriptor, methodContext, inliningFunction!!, jvmSignature, null) + } + maxCalcAdapter.visitMaxs(-1, -1) + maxCalcAdapter.visitEnd() + + return SMAPAndMethodNode(node, smap) + } + + override fun hasFinallyBlocks() = codegen.hasFinallyBlocks() + + override fun generateFinallyBlocksIfNeeded(codegen: BaseExpressionCodegen, returnType: Type, afterReturnLabel: Label, target: Label?) { + // TODO use the target label for non-local break/continue + require(codegen is ExpressionCodegen) + codegen.generateFinallyBlocksIfNeeded(returnType, null, afterReturnLabel) + } + + override fun createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(finallyNode: MethodNode, curFinallyDepth: Int) = + ExpressionCodegen( + finallyNode, codegen.frameMap, codegen.returnType, + codegen.getContext(), codegen.state, codegen.parentCodegen + ).also { + it.addBlockStackElementsForNonLocalReturns(codegen.blockStackElements, curFinallyDepth) + } + + override fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor): Boolean { + return JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), codegen.state.outDirectory) + } + + override fun isFinallyMarkerRequired(): Boolean = isFinallyMarkerRequired(codegen.getContext()) + + + override val compilationContextDescriptor + get() = codegen.getContext().contextDescriptor + + override val compilationContextFunctionDescriptor + get() = codegen.getContext().functionDescriptor + + override fun getContextLabels(): Map { + val context = codegen.getContext() + val parentContext = context.parentContext + val descriptor = if (parentContext is ClosureContext && parentContext.originalSuspendLambdaDescriptor != null) { + parentContext.originalSuspendLambdaDescriptor!! + } else context.contextDescriptor + + val labels = getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor) + return labels.associateWith { null } // TODO add break/continue labels + } + + fun initializeInlineFunctionContext(functionDescriptor: FunctionDescriptor) { + context = getContext( + functionDescriptor, + functionDescriptor, + state, + DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)?.containingFile as? KtFile, + additionalInnerClasses + ) + } + + override fun reportSuspensionPointInsideMonitor(stackTraceElement: String) { + org.jetbrains.kotlin.codegen.coroutines.reportSuspensionPointInsideMonitor(callElement, state, stackTraceElement) + } + + companion object { + fun getContext( + descriptor: DeclarationDescriptor, + innerDescriptor: DeclarationDescriptor, + state: GenerationState, + sourceFile: KtFile?, + additionalInners: MutableList + ): CodegenContext<*> { + if (descriptor is PackageFragmentDescriptor) { + //no inners + return PackageContext(descriptor, state.rootContext, null, sourceFile) + } + + val container = descriptor.containingDeclaration ?: error("No container for descriptor: $descriptor") + val containerContext = getContext( + container, + descriptor, + state, + sourceFile, + additionalInners + ) + + return when (descriptor) { + is ScriptDescriptor -> { + val earlierScripts = state.scriptSpecific.earlierScriptsForReplInterpreter + containerContext.intoScript( + descriptor, + earlierScripts ?: emptyList(), + descriptor as ClassDescriptor, state.typeMapper + ) + } + is ClassDescriptor -> { + val kind = + when { + DescriptorUtils.isInterface(descriptor) && + innerDescriptor !is ClassDescriptor && + !innerDescriptor.isCallableMemberCompiledToJvmDefault(state.jvmDefaultMode) -> + OwnerKind.DEFAULT_IMPLS + else -> + OwnerKind.IMPLEMENTATION + } + + additionalInners.addIfNotNull( + InnerClassConsumer.classForInnerClassRecord(descriptor, kind == OwnerKind.DEFAULT_IMPLS) + ) + + if (descriptor.isInlineClass()) { + containerContext.intoClass(descriptor, OwnerKind.IMPLEMENTATION, state) + .intoClass(descriptor, OwnerKind.ERASED_INLINE_CLASS, state) + } else { + containerContext.intoClass(descriptor, kind, state) + } + } + is FunctionDescriptor -> { + containerContext.intoFunction(descriptor) + } + else -> { + throw IllegalStateException("Couldn't build context for $descriptor") + } + } + + } + } +} + +fun DeclarationDescriptor.isInlineOrInsideInline(): Boolean = + if (this is FunctionDescriptor && isInline) true + else containingDeclaration?.isInlineOrInsideInline() == true + +fun getDeclarationLabels(lambdaOrFun: PsiElement?, descriptor: DeclarationDescriptor): Set { + val result = HashSet() + + if (lambdaOrFun != null) { + val label = LabelResolver.getLabelNameIfAny(lambdaOrFun) + if (label != null) { + result.add(label.asString()) + } + } + + if (!ExpressionTypingUtils.isFunctionLiteral(descriptor)) { + if (!descriptor.name.isSpecial) { + result.add(descriptor.name.asString()) + } + result.add(FIRST_FUN_LABEL) + } + return result +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt index 1a02dc3c89d..f54910ee6a1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt @@ -6,30 +6,17 @@ package org.jetbrains.kotlin.codegen.inline import com.intellij.psi.PsiFile -import org.jetbrains.kotlin.backend.common.CodegenUtil -import org.jetbrains.kotlin.codegen.* -import org.jetbrains.kotlin.codegen.context.* -import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView +import org.jetbrains.kotlin.codegen.BaseExpressionCodegen +import org.jetbrains.kotlin.codegen.OwnerKind import org.jetbrains.kotlin.codegen.state.GenerationState -import org.jetbrains.kotlin.config.isReleaseCoroutines -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.incremental.KotlinLookupLocation +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.incremental.components.LookupLocation -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert -import org.jetbrains.kotlin.resolve.isInlineClass -import org.jetbrains.kotlin.resolve.jvm.annotations.isCallableMemberCompiledToJvmDefault import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature -import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.org.objectweb.asm.Label -import org.jetbrains.org.objectweb.asm.MethodVisitor -import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.tree.MethodNode -import kotlin.properties.Delegates interface SourceCompilerForInline { val state: GenerationState @@ -78,356 +65,3 @@ interface SourceCompilerForInline { fun reportSuspensionPointInsideMonitor(stackTraceElement: String) } - - -class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, override val callElement: KtElement) : - SourceCompilerForInline { - - override val state = codegen.state - - private var context by Delegates.notNull>() - - private var additionalInnerClasses = mutableListOf() - - override val lookupLocation = KotlinLookupLocation(callElement) - - - override val callElementText: String by lazy { - callElement.text - } - - override val callsiteFile by lazy { - callElement.containingFile - } - - override val contextKind - get () = context.contextKind - - override val inlineCallSiteInfo: InlineCallSiteInfo - get() { - var context = codegen.getContext() - var parentCodegen = codegen.parentCodegen - while (context is InlineLambdaContext) { - val closureContext = context.getParentContext() - assert(closureContext is ClosureContext) { "Parent context of inline lambda should be closure context" } - assert(closureContext.parentContext is MethodContext) { "Closure context should appear in method context" } - context = closureContext.parentContext as MethodContext - assert(parentCodegen is FakeMemberCodegen) { "Parent codegen of inlined lambda should be FakeMemberCodegen" } - parentCodegen = (parentCodegen as FakeMemberCodegen).delegate - } - - val signature = codegen.state.typeMapper.mapSignatureSkipGeneric(context.functionDescriptor, context.contextKind) - return InlineCallSiteInfo( - parentCodegen.className, - signature.asmMethod.name, - signature.asmMethod.descriptor, - compilationContextFunctionDescriptor.isInlineOrInsideInline(), - compilationContextFunctionDescriptor.isSuspend, - CodegenUtil.getLineNumberForElement(callElement, false) ?: 0 - ) - } - - override val lazySourceMapper - get() = codegen.parentCodegen.orCreateSourceMapper - - override fun generateLambdaBody(lambdaInfo: ExpressionLambda, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode { - lambdaInfo as? PsiExpressionLambda ?: error("TODO") - val invokeMethodDescriptor = lambdaInfo.invokeMethodDescriptor - val jvmMethodSignature = state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor) - val asmMethod = jvmMethodSignature.asmMethod - val methodNode = MethodNode( - Opcodes.API_VERSION, DescriptorAsmUtil.getMethodAsmFlags(invokeMethodDescriptor, OwnerKind.IMPLEMENTATION, state), - asmMethod.name, asmMethod.descriptor, null, null - ) - val adapter = wrapWithMaxLocalCalc(methodNode) - val closureContext = when { - lambdaInfo.isPropertyReference -> - codegen.getContext().intoAnonymousClass(lambdaInfo.classDescriptor, codegen, OwnerKind.IMPLEMENTATION) - invokeMethodDescriptor.isSuspend -> - codegen.getContext().intoCoroutineClosure( - getOrCreateJvmSuspendFunctionView(invokeMethodDescriptor, state), invokeMethodDescriptor, codegen, state.typeMapper - ) - else -> codegen.getContext().intoClosure(invokeMethodDescriptor, codegen, state.typeMapper) - } - val context = closureContext.intoInlinedLambda(invokeMethodDescriptor, lambdaInfo.isCrossInline, lambdaInfo.isPropertyReference) - val smap = generateMethodBody( - adapter, invokeMethodDescriptor, context, - lambdaInfo.functionWithBodyOrCallableReference, - jvmMethodSignature, lambdaInfo - ) - adapter.visitMaxs(-1, -1) - return SMAPAndMethodNode(methodNode, smap) - } - - private fun generateMethodBody( - adapter: MethodVisitor, - descriptor: FunctionDescriptor, - context: MethodContext, - expression: KtExpression, - jvmMethodSignature: JvmMethodSignature, - lambdaInfo: PsiExpressionLambda? - ): SMAP { - val isLambda = lambdaInfo != null - - // Wrapping for preventing marking actual parent codegen as containing reified markers - val parentCodegen = FakeMemberCodegen( - codegen.parentCodegen, expression, context.parentContext as FieldOwnerContext<*>, - if (isLambda) - codegen.parentCodegen.className - else - state.typeMapper.mapImplementationOwner(descriptor).internalName, - if (isLambda) emptyList() else additionalInnerClasses, - isLambda - ) - - val strategy = when (expression) { - is KtCallableReferenceExpression -> { - val resolvedCall = expression.callableReference.getResolvedCallWithAssert(state.bindingContext) - val receiverKotlinType = JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)?.type - val receiverType = receiverKotlinType?.let(state.typeMapper::mapType) - val boundReceiverJvmKotlinType = receiverType?.let { JvmKotlinType(receiverType, receiverKotlinType) } - - if (isLambda && lambdaInfo!!.isPropertyReference) { - val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor) - val info = lambdaInfo.propertyReferenceInfo - PropertyReferenceCodegen.PropertyReferenceGenerationStrategy( - true, info!!.getFunction, info.target, asmType, - boundReceiverJvmKotlinType, - lambdaInfo.functionWithBodyOrCallableReference, state, true - ) - } else { - FunctionReferenceGenerationStrategy(state, descriptor, resolvedCall, boundReceiverJvmKotlinType, null, true) - } - } - is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody) - else -> FunctionGenerationStrategy.FunctionDefault(state, expression as KtDeclarationWithBody) - } - - FunctionCodegen.generateMethodBody( - adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen, state.jvmDefaultMode, - state.languageVersionSettings.isReleaseCoroutines() - ) - - if (isLambda) { - codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages) - } - - return SMAP(parentCodegen.orCreateSourceMapper.resultMappings) - } - - @Suppress("UNCHECKED_CAST") - private class FakeMemberCodegen( - internal val delegate: MemberCodegen<*>, - declaration: KtElement, - codegenContext: FieldOwnerContext<*>, - private val className: String, - private val parentAsInnerClasses: List, - private val isInlineLambdaCodegen: Boolean - ) : MemberCodegen(delegate as MemberCodegen, declaration, codegenContext) { - - override fun generateDeclaration() { - throw IllegalStateException() - } - - override fun generateBody() { - throw IllegalStateException() - } - - override fun generateKotlinMetadataAnnotation() { - throw IllegalStateException() - } - - override fun getInlineNameGenerator(): NameGenerator { - return delegate.inlineNameGenerator - } - - override //TODO: obtain name from context - fun getClassName(): String { - return className - } - - override fun addParentsToInnerClassesIfNeeded(innerClasses: MutableCollection) { - if (isInlineLambdaCodegen) { - super.addParentsToInnerClassesIfNeeded(innerClasses) - } else { - innerClasses.addAll(parentAsInnerClasses) - } - } - - override fun generateAssertField() { - delegate.generateAssertField() - } - } - - override fun doCreateMethodNodeFromSource( - callableDescriptor: FunctionDescriptor, - jvmSignature: JvmMethodSignature, - callDefault: Boolean, - asmMethod: Method - ): SMAPAndMethodNode { - val element = DescriptorToSourceUtils.descriptorToDeclaration(callableDescriptor) - - if (!(element is KtNamedFunction || element is KtPropertyAccessor)) { - throw IllegalStateException("Couldn't find declaration for function $callableDescriptor") - } - val inliningFunction = element as KtDeclarationWithBody? - - val node = MethodNode( - Opcodes.API_VERSION, - DescriptorAsmUtil.getMethodAsmFlags(callableDescriptor, context.contextKind, state) or if (callDefault) Opcodes.ACC_STATIC else 0, - asmMethod.name, - asmMethod.descriptor, null, null - ) - - //for maxLocals calculation - val maxCalcAdapter = wrapWithMaxLocalCalc(node) - val parentContext = context.parentContext ?: error("Context has no parent: " + context) - val methodContext = parentContext.intoFunction(callableDescriptor) - - val smap = if (callDefault) { - val implementationOwner = state.typeMapper.mapImplementationOwner(callableDescriptor) - val parentCodegen = FakeMemberCodegen( - codegen.parentCodegen, inliningFunction!!, methodContext.parentContext as FieldOwnerContext<*>, - implementationOwner.internalName, - additionalInnerClasses, - false - ) - if (element !is KtNamedFunction) { - throw IllegalStateException("Property accessors with default parameters not supported $callableDescriptor") - } - FunctionCodegen.generateDefaultImplBody( - methodContext, callableDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT, - inliningFunction as KtNamedFunction?, parentCodegen, asmMethod - ) - SMAP(parentCodegen.orCreateSourceMapper.resultMappings) - } else { - generateMethodBody(maxCalcAdapter, callableDescriptor, methodContext, inliningFunction!!, jvmSignature, null) - } - maxCalcAdapter.visitMaxs(-1, -1) - maxCalcAdapter.visitEnd() - - return SMAPAndMethodNode(node, smap) - } - - override fun hasFinallyBlocks() = codegen.hasFinallyBlocks() - - override fun generateFinallyBlocksIfNeeded(codegen: BaseExpressionCodegen, returnType: Type, afterReturnLabel: Label, target: Label?) { - // TODO use the target label for non-local break/continue - require(codegen is ExpressionCodegen) - codegen.generateFinallyBlocksIfNeeded(returnType, null, afterReturnLabel) - } - - override fun createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(finallyNode: MethodNode, curFinallyDepth: Int) = - ExpressionCodegen( - finallyNode, codegen.frameMap, codegen.returnType, - codegen.getContext(), codegen.state, codegen.parentCodegen - ).also { - it.addBlockStackElementsForNonLocalReturns(codegen.blockStackElements, curFinallyDepth) - } - - override fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor): Boolean { - return JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), codegen.state.outDirectory) - } - - override fun isFinallyMarkerRequired(): Boolean = isFinallyMarkerRequired(codegen.getContext()) - - - override val compilationContextDescriptor - get() = codegen.getContext().contextDescriptor - - override val compilationContextFunctionDescriptor - get() = codegen.getContext().functionDescriptor - - override fun getContextLabels(): Map { - val context = codegen.getContext() - val parentContext = context.parentContext - val descriptor = if (parentContext is ClosureContext && parentContext.originalSuspendLambdaDescriptor != null) { - parentContext.originalSuspendLambdaDescriptor!! - } else context.contextDescriptor - - val labels = InlineCodegen.getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor) - return labels.associateWith { null } // TODO add break/continue labels - } - - fun initializeInlineFunctionContext(functionDescriptor: FunctionDescriptor) { - context = getContext( - functionDescriptor, - functionDescriptor, - state, - DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)?.containingFile as? KtFile, - additionalInnerClasses - ) - } - - override fun reportSuspensionPointInsideMonitor(stackTraceElement: String) { - org.jetbrains.kotlin.codegen.coroutines.reportSuspensionPointInsideMonitor(callElement, state, stackTraceElement) - } - - companion object { - fun getContext( - descriptor: DeclarationDescriptor, - innerDescriptor: DeclarationDescriptor, - state: GenerationState, - sourceFile: KtFile?, - additionalInners: MutableList - ): CodegenContext<*> { - if (descriptor is PackageFragmentDescriptor) { - //no inners - return PackageContext(descriptor, state.rootContext, null, sourceFile) - } - - val container = descriptor.containingDeclaration ?: error("No container for descriptor: $descriptor") - val containerContext = getContext( - container, - descriptor, - state, - sourceFile, - additionalInners - ) - - return when (descriptor) { - is ScriptDescriptor -> { - val earlierScripts = state.scriptSpecific.earlierScriptsForReplInterpreter - containerContext.intoScript( - descriptor, - earlierScripts ?: emptyList(), - descriptor as ClassDescriptor, state.typeMapper - ) - } - is ClassDescriptor -> { - val kind = - when { - DescriptorUtils.isInterface(descriptor) && - innerDescriptor !is ClassDescriptor && - !innerDescriptor.isCallableMemberCompiledToJvmDefault(state.jvmDefaultMode) -> - OwnerKind.DEFAULT_IMPLS - else -> - OwnerKind.IMPLEMENTATION - } - - additionalInners.addIfNotNull( - InnerClassConsumer.classForInnerClassRecord(descriptor, kind == OwnerKind.DEFAULT_IMPLS) - ) - - if (descriptor.isInlineClass()) { - containerContext.intoClass(descriptor, OwnerKind.IMPLEMENTATION, state) - .intoClass(descriptor, OwnerKind.ERASED_INLINE_CLASS, state) - } else { - containerContext.intoClass(descriptor, kind, state) - } - } - is FunctionDescriptor -> { - containerContext.intoFunction(descriptor) - } - else -> { - throw IllegalStateException("Couldn't build context for $descriptor") - } - } - - } - } -} - -fun DeclarationDescriptor.isInlineOrInsideInline(): Boolean = - if (this is FunctionDescriptor && isInline) true - else containingDeclaration?.isInlineOrInsideInline() == true