JVM: move PsiSourceCompilerForInline to a separate file
This commit is contained in:
@@ -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<out T : BaseExpressionCodegen>(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected fun throwCompilationException(
|
||||
nodeAndSmap: SMAPAndMethodNode?, e: Exception, generateNodeText: Boolean
|
||||
): CompilationException {
|
||||
@@ -150,8 +146,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
|
||||
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<out T : BaseExpressionCodegen>(
|
||||
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<out T : BaseExpressionCodegen>(
|
||||
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<out T : BaseExpressionCodegen>(
|
||||
InlineUtil.isInlineParameter(field.descriptor) &&
|
||||
InlineUtil.isInline(field.descriptor.containingDeclaration)
|
||||
}
|
||||
|
||||
fun getDeclarationLabels(lambdaOrFun: PsiElement?, descriptor: DeclarationDescriptor): Set<String> {
|
||||
val result = HashSet<String>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+404
@@ -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<CodegenContext<*>>()
|
||||
|
||||
private var additionalInnerClasses = mutableListOf<ClassDescriptor>()
|
||||
|
||||
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<ClassDescriptor>,
|
||||
private val isInlineLambdaCodegen: Boolean
|
||||
) : MemberCodegen<KtPureElement>(delegate as MemberCodegen<KtPureElement>, 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<ClassDescriptor>) {
|
||||
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<String, Label?> {
|
||||
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<ClassDescriptor>
|
||||
): 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<String> {
|
||||
val result = HashSet<String>()
|
||||
|
||||
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
|
||||
}
|
||||
+4
-370
@@ -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<CodegenContext<*>>()
|
||||
|
||||
private var additionalInnerClasses = mutableListOf<ClassDescriptor>()
|
||||
|
||||
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<ClassDescriptor>() 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<ClassDescriptor>,
|
||||
private val isInlineLambdaCodegen: Boolean
|
||||
) : MemberCodegen<KtPureElement>(delegate as MemberCodegen<KtPureElement>, 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<ClassDescriptor>) {
|
||||
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<String, Label?> {
|
||||
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<ClassDescriptor>
|
||||
): 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
|
||||
|
||||
Reference in New Issue
Block a user