Support crossinline suspend lambda as parameter of inline function

Use fake continuation instead of ALOAD 0 while inlining
Do not generate state machine for inner lambdas and inner objects,
which capture crossinline suspend lambda.

 #KT-19159: Fixed
This commit is contained in:
Ilmir Usmanov
2018-01-18 15:21:19 +03:00
parent 042ca55be7
commit 6854135077
80 changed files with 5214 additions and 144 deletions
@@ -2343,7 +2343,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@NotNull CallGenerator callGenerator,
@NotNull ArgumentGenerator argumentGenerator
) {
boolean isSuspendNoInlineCall = CoroutineCodegenUtilKt.isSuspendNoInlineCall(resolvedCall);
boolean isSuspendNoInlineCall = CoroutineCodegenUtilKt.isSuspendNoInlineCall(resolvedCall, this);
boolean isConstructor = resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor;
if (!(callableMethod instanceof IntrinsicWithSpecialReceiver)) {
putReceiverAndInlineMarkerIfNeeded(callableMethod, resolvedCall, receiver, isSuspendNoInlineCall, isConstructor);
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature;
import org.jetbrains.kotlin.load.java.JvmAbi;
@@ -61,10 +62,7 @@ import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.*;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
@@ -642,6 +640,10 @@ public class FunctionCodegen {
mv.visitLabel(methodEnd);
Type thisType = getThisTypeForFunction(functionDescriptor, context, typeMapper);
if (functionDescriptor instanceof AnonymousFunctionDescriptor && functionDescriptor.isSuspend()) {
functionDescriptor = CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(functionDescriptor, typeMapper.getBindingContext());
}
generateLocalVariableTable(
mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind(), typeMapper,
(functionFakeIndex >= 0 ? 1 : 0) + (lambdaFakeIndex >= 0 ? 1 : 0)
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. 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.binding;
@@ -25,6 +14,7 @@ import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.ReflectionTypes;
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor;
import org.jetbrains.kotlin.cfg.WhenChecker;
import org.jetbrains.kotlin.codegen.*;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
@@ -51,6 +41,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.EnumValue;
import org.jetbrains.kotlin.resolve.constants.NullValue;
@@ -314,8 +305,25 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
nameStack.push(name);
if (CoroutineUtilKt.isSuspendLambda(functionDescriptor)) {
SimpleFunctionDescriptor jvmSuspendFunctionView =
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
(SimpleFunctionDescriptor) functionDescriptor
);
bindingTrace.record(
CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW,
functionDescriptor,
jvmSuspendFunctionView
);
closure.setSuspend(true);
closure.setSuspendLambda();
if (capturesCrossinlineSuspendLambda(functionLiteral)) {
bindingTrace.record(
CodegenBinding.CAPTURES_CROSSINLINE_SUSPEND_LAMBDA,
functionDescriptor,
true
);
}
}
super.visitLambdaExpression(lambdaExpression);
@@ -323,6 +331,35 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
classStack.pop();
}
// If inner lambda captures crossinline suspend lambda, it is unsafe to generate state machine for it.
private boolean capturesCrossinlineSuspendLambda(@NotNull PsiElement psiNode) {
PsiElement[] children = psiNode.getChildren();
boolean res = false;
for (PsiElement child : children) {
if (child instanceof KtCallElement) {
KtExpression callee = ((KtCallElement) child).getCalleeExpression();
Call call = bindingContext.get(CALL, callee);
ResolvedCall<?> resolvedCall = bindingContext.get(RESOLVED_CALL, call);
if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
VariableAsFunctionResolvedCall variableAsFunction = (VariableAsFunctionResolvedCall) resolvedCall;
VariableDescriptor variableDescriptor = variableAsFunction.getVariableCall().getResultingDescriptor();
CallableDescriptor callableDescriptor = ((ResolvedCall) variableAsFunction).getResultingDescriptor();
if (variableDescriptor instanceof ValueParameterDescriptor &&
((ValueParameterDescriptor) variableDescriptor).isCrossinline() &&
callableDescriptor instanceof FunctionDescriptor &&
((FunctionDescriptor) callableDescriptor).isSuspend()) {
FunctionDescriptor enclosingDescriptor =
bindingContext.get(ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.getCall());
assert(enclosingDescriptor != null);
return true;
}
}
}
res = res || capturesCrossinlineSuspendLambda(child);
}
return res;
}
@Override
public void visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression) {
ResolvedCall<?> referencedFunction = CallUtilKt.getResolvedCall(expression.getCallableReference(), bindingContext);
@@ -537,6 +574,14 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
MutableClosure closure = recordClosure(classDescriptor, name);
closure.setSuspend(true);
if (capturesCrossinlineSuspendLambda(function)) {
bindingTrace.record(
CodegenBinding.CAPTURES_CROSSINLINE_SUSPEND_LAMBDA,
functionDescriptor,
true
);
}
super.visitNamedFunction(function);
if (nameForClassOrPackageMember != null) {
@@ -52,6 +52,9 @@ public class CodegenBinding {
public static final WritableSlice<FunctionDescriptor, FunctionDescriptor> SUSPEND_FUNCTION_TO_JVM_VIEW =
Slices.createSimpleSlice();
public static final WritableSlice<FunctionDescriptor, Boolean> CAPTURES_CROSSINLINE_SUSPEND_LAMBDA =
Slices.createSimpleSlice();
public static final WritableSlice<ValueParameterDescriptor, ValueParameterDescriptor> PARAMETER_SYNONYM =
Slices.createSimpleSlice();
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.codegen.context.PackageContext
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
import org.jetbrains.kotlin.codegen.inline.ReificationArgument
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
@@ -50,6 +51,11 @@ import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.util.Textifier
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.io.StringWriter
import java.io.PrintWriter
import java.util.*
fun generateIsCheck(
@@ -424,3 +430,13 @@ inline fun FrameMap.evaluateOnce(
leaveTemp(asType)
}
}
// Handy debugging routine. Print all instructions from methodNode.
fun MethodNode.textifyMethodNode(): String {
val text = Textifier()
val tmv = TraceMethodVisitor(text)
this.instructions.asSequence().forEach { it.accept(tmv) }
val sw = StringWriter()
text.print(PrintWriter(sw))
return "$sw"
}
@@ -25,7 +25,7 @@ class InlineLambdaContext(
contextKind: OwnerKind,
parentContext: CodegenContext<*>,
closure: MutableClosure?,
private val isCrossInline: Boolean,
val isCrossInline: Boolean,
private val isPropertyReference: Boolean
) : MethodContext(functionDescriptor, contextKind, parentContext, closure, false) {
@@ -6,8 +6,10 @@
package org.jetbrains.kotlin.codegen.coroutines
import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.CAPTURES_CROSSINLINE_SUSPEND_LAMBDA
import org.jetbrains.kotlin.codegen.context.ClosureContext
import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
@@ -127,7 +129,8 @@ class CoroutineCodegenForLambda private constructor(
element: KtElement,
private val closureContext: ClosureContext,
classBuilder: ClassBuilder,
private val originalSuspendFunctionDescriptor: FunctionDescriptor
private val originalSuspendFunctionDescriptor: FunctionDescriptor,
private val forInline: Boolean
) : AbstractCoroutineCodegen(
outerExpressionCodegen, element, closureContext, classBuilder,
userDataForDoResume = mapOf(INITIAL_SUSPEND_DESCRIPTOR_FOR_DO_RESUME to originalSuspendFunctionDescriptor)
@@ -300,13 +303,14 @@ class CoroutineCodegenForLambda private constructor(
object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) {
override fun wrapMethodVisitor(mv: MethodVisitor, access: Int, name: String, desc: String): MethodVisitor {
if (forInline) return super.wrapMethodVisitor(mv, access, name, desc)
return CoroutineTransformerMethodVisitor(
mv, access, name, desc, null, null,
obtainClassBuilderForCoroutineState = { v },
element = element,
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = v.thisName,
isForNamedFunction = false
mv, access, name, desc, null, null,
obtainClassBuilderForCoroutineState = { v },
lineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0,
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = v.thisName,
isForNamedFunction = false
)
}
@@ -336,7 +340,9 @@ class CoroutineCodegenForLambda private constructor(
originalSuspendLambdaDescriptor, expressionCodegen, expressionCodegen.state.typeMapper
),
classBuilder,
originalSuspendLambdaDescriptor
originalSuspendLambdaDescriptor,
// Local suspend lambdas, which call crossinline suspend parameters of containing functions must be generated after inlining
expressionCodegen.bindingContext[CAPTURES_CROSSINLINE_SUSPEND_LAMBDA, originalSuspendLambdaDescriptor] == true
)
}
}
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.codegen.coroutines
import com.intellij.util.containers.Stack
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.StackValue
@@ -28,7 +27,6 @@ import org.jetbrains.kotlin.codegen.optimization.common.*
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.utils.sure
@@ -53,7 +51,7 @@ class CoroutineTransformerMethodVisitor(
obtainClassBuilderForCoroutineState: () -> ClassBuilder,
private val isForNamedFunction: Boolean,
private val shouldPreserveClassInitialization: Boolean,
private val element: KtElement,
private val lineNumber: Int,
// It's only matters for named functions, may differ from '!isStatic(access)' in case of DefaultImpls
private val needDispatchReceiver: Boolean = false,
// May differ from containingClassInternalName in case of DefaultImpls
@@ -67,6 +65,8 @@ class CoroutineTransformerMethodVisitor(
private var exceptionIndex = if (isForNamedFunction) -1 else 2
override fun performTransformations(methodNode: MethodNode) {
removeFakeContinuationConstructorCall(methodNode)
val suspensionPoints = collectSuspensionPoints(methodNode)
// First instruction in the method node may change in case of named function
@@ -78,11 +78,7 @@ class CoroutineTransformerMethodVisitor(
ReturnUnitMethodTransformer.transform(containingClassInternalName, methodNode)
if (allSuspensionPointsAreTailCalls(containingClassInternalName, methodNode, suspensionPoints)) {
continuationIndex =
if (isStatic(methodNode.access))
Type.getArgumentTypes(methodNode.desc).size - 1
else
Type.getArgumentTypes(methodNode.desc).size
continuationIndex = getLastParameterIndex(methodNode.desc, methodNode.access)
replaceFakeContinuationsWithRealOnes(methodNode, continuationIndex)
dropSuspensionMarkers(methodNode, suspensionPoints)
@@ -125,7 +121,6 @@ class CoroutineTransformerMethodVisitor(
val startLabel = LabelNode()
val defaultLabel = LabelNode()
val tableSwitchLabel = LabelNode()
val lineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0
// tableswitch(this.label)
insertBefore(
@@ -161,12 +156,15 @@ class CoroutineTransformerMethodVisitor(
methodNode.removeEmptyCatchBlocks()
}
private fun replaceFakeContinuationsWithRealOnes(methodNode: MethodNode, continuationIndex: Int) {
val fakeContinuations = methodNode.instructions.asSequence().filter(::isFakeContinuationMarker)
for (fakeContinuation in fakeContinuations) {
methodNode.instructions.removeAll(listOf(fakeContinuation.previous.previous, fakeContinuation.previous))
methodNode.instructions.set(fakeContinuation, VarInsnNode(Opcodes.ALOAD, continuationIndex))
private fun removeFakeContinuationConstructorCall(methodNode: MethodNode) {
val seq = methodNode.instructions.asSequence()
val first = seq.firstOrNull(::isBeforeFakeContinuationConstructorCallMarker)?.previous ?: return
val last = seq.firstOrNull(::isAfterFakeContinuationConstructorCallMarker).sure {
"BeforeFakeContinuationConstructorCallMarker without AfterFakeContinuationConstructorCallMarker"
}
val toRemove = InsnSequence(first, last).toList()
methodNode.instructions.removeAll(toRemove)
methodNode.instructions.set(last, InsnNode(Opcodes.ACONST_NULL))
}
private fun createInsnForReadingLabel() =
@@ -285,30 +283,13 @@ class CoroutineTransformerMethodVisitor(
visitLabel(createStateInstance)
anew(objectTypeForState)
dup()
val parameterTypesAndIndices =
getParameterTypesIndicesForCoroutineConstructor(
methodNode.desc,
methodNode.access,
needDispatchReceiver, internalNameForDispatchReceiver ?: containingClassInternalName
)
for ((type, index) in parameterTypesAndIndices) {
load(index, type)
}
invokespecial(
classBuilderForCoroutineState.thisName,
"<init>",
Type.getMethodDescriptor(
Type.VOID_TYPE,
*getParameterTypesForCoroutineConstructor(
methodNode.desc, needDispatchReceiver,
internalNameForDispatchReceiver ?: containingClassInternalName
)
),
false
generateContinuationConstructorCall(
objectTypeForState,
methodNode,
needDispatchReceiver,
internalNameForDispatchReceiver,
containingClassInternalName,
classBuilderForCoroutineState
)
visitVarInsn(Opcodes.ASTORE, continuationIndex)
@@ -501,7 +482,7 @@ class CoroutineTransformerMethodVisitor(
): LabelNode {
val continuationLabel = LabelNode()
val continuationLabelAfterLoadedResult = LabelNode()
val suspendElementLineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0
val suspendElementLineNumber = lineNumber
val nextLineNumberNode = suspension.suspensionCallEnd.findNextOrNull { it is LineNumberNode } as? LineNumberNode
with(methodNode.instructions) {
// Save state
@@ -616,6 +597,41 @@ class CoroutineTransformerMethodVisitor(
}
}
internal fun InstructionAdapter.generateContinuationConstructorCall(
objectTypeForState: Type?,
methodNode: MethodNode,
needDispatchReceiver: Boolean,
internalNameForDispatchReceiver: String?,
containingClassInternalName: String,
classBuilderForCoroutineState: ClassBuilder
) {
anew(objectTypeForState)
dup()
val parameterTypesAndIndices =
getParameterTypesIndicesForCoroutineConstructor(
methodNode.desc,
methodNode.access,
needDispatchReceiver, internalNameForDispatchReceiver ?: containingClassInternalName
)
for ((type, index) in parameterTypesAndIndices) {
load(index, type)
}
invokespecial(
classBuilderForCoroutineState.thisName,
"<init>",
Type.getMethodDescriptor(
Type.VOID_TYPE,
*getParameterTypesForCoroutineConstructor(
methodNode.desc, needDispatchReceiver,
internalNameForDispatchReceiver ?: containingClassInternalName
)
),
false
)
}
private fun InstructionAdapter.generateResumeWithExceptionCheck(exceptionIndex: Int) {
// Check if resumeWithException has been called
load(exceptionIndex, AsmTypes.OBJECT_TYPE)
@@ -803,3 +819,11 @@ private fun AbstractInsnNode?.isInvisibleInDebugVarInsn(methodNode: MethodNode):
private val SAFE_OPCODES =
((Opcodes.DUP..Opcodes.DUP2_X2) + Opcodes.NOP + Opcodes.POP + Opcodes.POP2 + (Opcodes.IFEQ..Opcodes.GOTO)).toSet()
internal fun replaceFakeContinuationsWithRealOnes(methodNode: MethodNode, continuationIndex: Int) {
val fakeContinuations = methodNode.instructions.asSequence().filter(::isFakeContinuationMarker).toList()
for (fakeContinuation in fakeContinuations) {
methodNode.instructions.removeAll(listOf(fakeContinuation.previous.previous, fakeContinuation.previous))
methodNode.instructions.set(fakeContinuation, VarInsnNode(Opcodes.ALOAD, continuationIndex))
}
}
@@ -16,9 +16,13 @@
package org.jetbrains.kotlin.codegen.coroutines
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.inline.addFakeContinuationConstructorCallMarker
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode
import org.jetbrains.kotlin.descriptors.ClassDescriptor
@@ -30,6 +34,8 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
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.tree.MethodNode
class SuspendFunctionGenerationStrategy(
state: GenerationState,
@@ -39,7 +45,6 @@ class SuspendFunctionGenerationStrategy(
private val constructorCallNormalizationMode: JVMConstructorCallNormalizationMode
) : FunctionGenerationStrategy.CodegenBased(state) {
private lateinit var transformer: CoroutineTransformerMethodVisitor
private lateinit var codegen: ExpressionCodegen
private val classBuilderForCoroutineState by lazy {
@@ -57,16 +62,22 @@ class SuspendFunctionGenerationStrategy(
override fun wrapMethodVisitor(mv: MethodVisitor, access: Int, name: String, desc: String): MethodVisitor {
if (access and Opcodes.ACC_ABSTRACT != 0) return mv
return CoroutineTransformerMethodVisitor(
mv, access, name, desc, null, null, containingClassInternalName, this::classBuilderForCoroutineState,
isForNamedFunction = true,
element = declaration,
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
needDispatchReceiver = originalSuspendDescriptor.dispatchReceiverParameter != null,
internalNameForDispatchReceiver = containingClassInternalNameOrNull()
).also {
transformer = it
if (state.bindingContext[CodegenBinding.CAPTURES_CROSSINLINE_SUSPEND_LAMBDA, originalSuspendDescriptor] == true) {
return AddConstructorCallForCoroutineRegeneration(
mv, access, name, desc, null, null, this::classBuilderForCoroutineState,
containingClassInternalName,
originalSuspendDescriptor.dispatchReceiverParameter != null,
containingClassInternalNameOrNull()
)
}
return CoroutineTransformerMethodVisitor(
mv, access, name, desc, null, null, containingClassInternalName, this::classBuilderForCoroutineState,
isForNamedFunction = true,
lineNumber = CodegenUtil.getLineNumberForElement(declaration, false) ?: 0,
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
needDispatchReceiver = originalSuspendDescriptor.dispatchReceiverParameter != null,
internalNameForDispatchReceiver = containingClassInternalNameOrNull()
)
}
private fun containingClassInternalNameOrNull() =
@@ -76,4 +87,39 @@ class SuspendFunctionGenerationStrategy(
this.codegen = codegen
codegen.returnExpression(declaration.bodyExpression ?: error("Function has no body: " + declaration.getElementTextWithContext()))
}
// When we generate named suspend function for the use as inline site, we do not generate state machine.
// So, there will be no way to remember the name of generated continuation in such case.
// In order to keep generated continuation for named suspend function, we just generate construction call, which is going to be
// removed during inlining.
// The continuation itself will be regenerated and used as a container for the coroutine's locals.
private class AddConstructorCallForCoroutineRegeneration(
delegate: MethodVisitor,
access: Int,
name: String,
desc: String,
signature: String?,
exceptions: Array<out String>?,
obtainClassBuilderForCoroutineState: () -> ClassBuilder,
private val containingClassInternalName: String,
private val needDispatchReceiver: Boolean,
private val internalNameForDispatchReceiver: String?
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
private val classBuilderForCoroutineState: ClassBuilder by lazy(obtainClassBuilderForCoroutineState)
override fun performTransformations(methodNode: MethodNode) {
val objectTypeForState = Type.getObjectType(classBuilderForCoroutineState.thisName)
methodNode.instructions.insert(withInstructionAdapter {
addFakeContinuationConstructorCallMarker(this, true)
generateContinuationConstructorCall(
objectTypeForState,
methodNode,
needDispatchReceiver,
internalNameForDispatchReceiver,
containingClassInternalName,
classBuilderForCoroutineState
)
addFakeContinuationConstructorCallMarker(this, false)
})
}
}
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.coroutines
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.inline.addFakeContinuationMarker
@@ -175,13 +176,18 @@ private fun NewResolvedCallImpl<VariableDescriptor>.asDummyOldResolvedCall(bindi
)
}
fun ResolvedCall<*>.isSuspendNoInlineCall(): Boolean {
val isCrossinline =
safeAs<VariableAsFunctionResolvedCall>()?.variableCall?.resultingDescriptor?.safeAs<ValueParameterDescriptor>()?.isCrossinline
?: false
fun ResolvedCall<*>.isSuspendNoInlineCall(codegen: ExpressionCodegen): Boolean {
var isCrossinline = false
var isInlineLambda = false
if (this is VariableAsFunctionResolvedCall) {
variableCall.resultingDescriptor.safeAs<ValueParameterDescriptor>()?.let {
isCrossinline = it.isCrossinline
isInlineLambda = !isCrossinline && !it.isNoinline && codegen.context.functionDescriptor.isInline
}
}
return resultingDescriptor.safeAs<FunctionDescriptor>()
?.let {
val inline = it.isInline || isCrossinline
val inline = it.isInline || isCrossinline || isInlineLambda
it.isSuspend && (!inline || it.isBuiltInSuspendCoroutineOrReturnInJvm() || it.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm())
} == true
}
@@ -21,15 +21,17 @@ import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.coroutines.COROUTINE_IMPL_ASM_TYPE
import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
import org.jetbrains.kotlin.codegen.serialization.JvmCodegenStringTable
import org.jetbrains.kotlin.codegen.writeKotlinMetadata
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.load.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor
import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN
@@ -41,7 +43,8 @@ import java.util.*
class AnonymousObjectTransformer(
transformationInfo: AnonymousObjectTransformationInfo,
private val inliningContext: InliningContext,
private val isSameModule: Boolean
private val isSameModule: Boolean,
private val continuationClassName: String?
) : ObjectTransformer<AnonymousObjectTransformationInfo>(transformationInfo, inliningContext.state) {
private val oldObjectType = Type.getObjectType(transformationInfo.oldClassName)
@@ -138,12 +141,39 @@ class AnonymousObjectTransformer(
val additionalFakeParams = extractParametersMappingAndPatchConstructor(
constructor!!, allCapturedParamBuilder, constructorParamBuilder,transformationInfo, parentRemapper
)
val capturesCrossinlineSuspend = (!inliningContext.isInliningLambda || inliningContext.isContinuation) &&
inliningContext.expressionMap.values.any { lambda ->
lambda is PsiExpressionLambda && lambda.isCrossInline && lambda.invokeMethodDescriptor.isSuspend
}
val deferringMethods = ArrayList<DeferredMethodVisitor>()
generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, parentRemapper, additionalFakeParams)
val isLambdaAlreadyGeneratedAndNotGoingToBeInlined = transformationInfo.oldClassName.contains("\$\$special\$\$inlined")
val hasLambdasToInline =
((parentRemapper is RegeneratedLambdaFieldRemapper) && parentRemapper.recapturedLambdas.isNotEmpty()) || transformationInfo.capturedLambdasToInline.isNotEmpty()
for (next in methodsToTransform) {
val deferringVisitor = newMethod(classBuilder, next)
// Generate state machine for
// 1) doResume method of suspend lambda
// 2) Suspend named function
// Iff it captures crossinline suspend lambda
val generateStateMachineForLambda =
next.name == "doResume" && capturesCrossinlineSuspend && inliningContext.isContinuation &&
!isLambdaAlreadyGeneratedAndNotGoingToBeInlined && hasLambdasToInline
val continuationClassName = findFakeContinuationConstructorClassName(next)
val generateStateMachineForNamedFunction =
capturesCrossinlineSuspend && !inliningContext.isContinuation && continuationClassName != null
val deferringVisitor =
when {
generateStateMachineForLambda -> newStateMachineForLambda(classBuilder, next)
generateStateMachineForNamedFunction -> newStateMachineForNamedFunction(classBuilder, next, continuationClassName!!)
else -> newMethod(classBuilder, next)
}
val funResult = inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false)
val returnType = Type.getReturnType(next.desc)
@@ -160,6 +190,21 @@ class AnonymousObjectTransformer(
deferringMethods.forEach { method ->
removeFinallyMarkers(method.intermediate)
method.visitEnd()
// During regeneration of named suspend functions, which capture crossinline suspend lambda, we need to spill the variables
// into continuation object.
// In order to do this, we reuse class builder, which regenerates continuation object.
if (capturesCrossinlineSuspend &&
!inliningContext.isContinuation &&
inliningContext is RegeneratedClassContext
) {
val continuationClassName = findFakeContinuationConstructorClassName(method.intermediate)
if (continuationClassName != null) {
inliningContext.continuationBuilders
.remove(continuationClassName)
?.let(ClassBuilder::done)
}
}
}
SourceMapper.flushToClassBuilder(sourceMapper, classBuilder)
@@ -177,7 +222,12 @@ class AnonymousObjectTransformer(
writeOuterInfo(visitor)
classBuilder.done()
if (continuationClassName == transformationInfo.oldClassName) {
assert(inliningContext.parent?.parent is RegeneratedClassContext)
(inliningContext.parent?.parent as RegeneratedClassContext).continuationBuilders[continuationClassName] = classBuilder
} else {
classBuilder.done()
}
return transformationResult
}
@@ -390,6 +440,55 @@ class AnonymousObjectTransformer(
}
}
private fun newStateMachineForLambda(builder: ClassBuilder, original: MethodNode): DeferredMethodVisitor {
return DeferredMethodVisitor(
MethodNode(
original.access, original.name, original.desc, original.signature,
ArrayUtil.toStringArray(original.exceptions)
)
) {
CoroutineTransformerMethodVisitor(
builder.newMethod(
NO_ORIGIN, original.access, original.name, original.desc, original.signature,
ArrayUtil.toStringArray(original.exceptions)
), original.access, original.name, original.desc, null, null,
obtainClassBuilderForCoroutineState = { builder },
lineNumber = 0, // <- TODO
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = builder.thisName,
isForNamedFunction = false
)
}
}
private fun newStateMachineForNamedFunction(
builder: ClassBuilder,
original: MethodNode,
continuationClassName: String
): DeferredMethodVisitor {
assert(inliningContext is RegeneratedClassContext)
return DeferredMethodVisitor(
MethodNode(
original.access, original.name, original.desc, original.signature,
ArrayUtil.toStringArray(original.exceptions)
)
) {
CoroutineTransformerMethodVisitor(
builder.newMethod(
NO_ORIGIN, original.access, original.name, original.desc, original.signature,
ArrayUtil.toStringArray(original.exceptions)
), original.access, original.name, original.desc, null, null,
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
lineNumber = 0, // <- TODO
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = builder.thisName,
isForNamedFunction = true,
needDispatchReceiver = true,
internalNameForDispatchReceiver = builder.thisName
)
}
}
private fun extractParametersMappingAndPatchConstructor(
constructor: MethodNode,
capturedParamBuilder: ParametersBuilder,
@@ -535,3 +634,10 @@ class AnonymousObjectTransformer(
private fun isFirstDeclSiteLambdaFieldRemapper(parentRemapper: FieldRemapper): Boolean =
parentRemapper !is RegeneratedLambdaFieldRemapper && parentRemapper !is InlinedLambdaRemapper
}
internal fun findFakeContinuationConstructorClassName(node: MethodNode): String? {
val marker = node.instructions.asSequence().firstOrNull(::isBeforeFakeContinuationConstructorCallMarker) ?: return null
val new = marker.next
assert(new?.opcode == Opcodes.NEW)
return (new as TypeInsnNode).desc
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.psi.KtElement
@@ -41,11 +42,13 @@ class RegeneratedClassContext(
override val callSiteInfo: InlineCallSiteInfo
) : InliningContext(
parent, expressionMap, state, nameGenerator, typeRemapper, lambdaInfo, true
)
) {
val continuationBuilders: MutableMap<String, ClassBuilder> = hashMapOf()
}
open class InliningContext(
val parent: InliningContext?,
private val expressionMap: Map<Int, LambdaInfo>,
val expressionMap: Map<Int, LambdaInfo>,
val state: GenerationState,
val nameGenerator: NameGenerator,
val typeRemapper: TypeRemapper,
@@ -55,7 +58,7 @@ open class InliningContext(
val isInliningLambda = lambdaInfo != null
val internalNameToAnonymousObjectTransformationInfo = hashMapOf<String, AnonymousObjectTransformationInfo>()
private val internalNameToAnonymousObjectTransformationInfo = hashMapOf<String, AnonymousObjectTransformationInfo>()
var isContinuation: Boolean = false
@@ -64,6 +67,14 @@ open class InliningContext(
val root: RootInliningContext
get() = if (isRoot) this as RootInliningContext else parent!!.root
fun findAnonymousObjectTransformationInfo(internalName: String, searchInParent: Boolean = true): AnonymousObjectTransformationInfo? =
internalNameToAnonymousObjectTransformationInfo[internalName]
?: if (searchInParent) parent?.findAnonymousObjectTransformationInfo(internalName, searchInParent) else null
fun recordIfNotPresent(internalName: String, info: AnonymousObjectTransformationInfo) {
internalNameToAnonymousObjectTransformationInfo.putIfAbsent(internalName, info)
}
fun subInlineLambda(lambdaInfo: LambdaInfo): InliningContext =
subInline(
nameGenerator.subGenerator("lambda"),
@@ -104,8 +115,4 @@ open class InliningContext(
get() {
return parent!!.callSiteInfo
}
fun findAnonymousObjectTransformationInfo(internalName: String): AnonymousObjectTransformationInfo? {
return root.internalNameToAnonymousObjectTransformationInfo[internalName]
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. 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
@@ -9,18 +9,23 @@ import org.jetbrains.kotlin.backend.jvm.codegen.IrExpressionLambda
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ClosureCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_ASM_TYPE
import org.jetbrains.kotlin.codegen.coroutines.replaceFakeContinuationsWithRealOnes
import org.jetbrains.kotlin.codegen.inline.FieldRemapper.Companion.foldName
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
import org.jetbrains.kotlin.resolve.isInlineClassType
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
@@ -159,7 +164,11 @@ class MethodInliner(
currentTypeMapping,
inlineCallSiteInfo
)
val transformer = transformationInfo!!.createTransformer(childInliningContext, isSameModule)
val transformer = transformationInfo!!.createTransformer(
childInliningContext,
isSameModule,
findFakeContinuationConstructorClassName(node)
)
val transformResult = transformer.doTransform(nodeRemapper)
result.merge(transformResult)
@@ -308,7 +317,7 @@ class MethodInliner(
//TODO: add new inner class also for other contexts
if (inliningContext.parent is RegeneratedClassContext) {
inliningContext.parent.typeRemapper.addAdditionalMappings(
transformationInfo!!.oldClassName, transformationInfo!!.newClassName
transformationInfo!!.oldClassName, transformationInfo!!.newClassName
)
}
@@ -319,7 +328,7 @@ class MethodInliner(
}
}
else if ((!inliningContext.isInliningLambda || isDefaultLambdaWithReification(inliningContext.lambdaInfo!!)) &&
ReifiedTypeInliner.isNeedClassReificationMarker(MethodInsnNode(opcode, owner, name, desc, false))) {
ReifiedTypeInliner.isNeedClassReificationMarker(MethodInsnNode(opcode, owner, name, desc, false))) {
//we shouldn't process here content of inlining lambda it should be reified at external level except default lambdas
}
else {
@@ -443,6 +452,8 @@ class MethodInliner(
preprocessNodeBeforeInline(processingNode, labelOwner)
replaceContinuationAccessesWithFakeContinuationsIfNeeded(processingNode)
val sources = analyzeMethodNodeBeforeInline(processingNode)
val toDelete = SmartSet.create<AbstractInsnNode>()
@@ -584,6 +595,67 @@ class MethodInliner(
return processingNode
}
// Replace ALOAD 0
// with
// ICONST fakeContinuationMarker
// INVOKESTATIC InlineMarker.mark
// ACONST_NULL
// iff this ALOAD 0 is continuation and one of the following conditions is met
// 1) it is passed as the last parameter to suspending function
// 2) it is ASTORE'd right after
// 3) it is passed to invoke of lambda
private fun replaceContinuationAccessesWithFakeContinuationsIfNeeded(processingNode: MethodNode) {
val lambdaInfo = inliningContext.lambdaInfo ?: return
if (!lambdaInfo.invokeMethodDescriptor.isSuspend) return
val aload0s = processingNode.instructions.asSequence().filter { it.opcode == Opcodes.ALOAD && it.safeAs<VarInsnNode>()?.`var` == 0 }
// Expected pattern here:
// ALOAD 0
// ICONST_0
// INVOKESTATIC InlineMarker.mark
// INVOKE* suspendingFunction(..., Continuation;)Ljava/lang/Object;
val continuationAsParameterAload0s =
aload0s.filter { it.next?.next?.let(::isBeforeSuspendMarker) == true && isSuspendCall(it.next?.next?.next) }
replaceContinuationsWithFakeOnes(continuationAsParameterAload0s, processingNode)
// Expected pattern here:
// ALOAD 0
// ASTORE N
// This pattern may occur after multiple inlines
val continuationToStoreAload0s = aload0s.filter { it.next?.opcode == Opcodes.ASTORE }
replaceContinuationsWithFakeOnes(continuationToStoreAload0s, processingNode)
// Expected pattern here:
// ALOAD 0
// INVOKEINTERFACE kotlin/jvm/functions/FunctionN.invoke (...,Ljava/lang/Object;)Ljava/lang/Object;
val continuationAsLambdaParameterAload0s = aload0s.filter { isLambdaCall(it.next) }
replaceContinuationsWithFakeOnes(continuationAsLambdaParameterAload0s, processingNode)
}
private fun isLambdaCall(invoke: AbstractInsnNode?): Boolean {
if (invoke?.opcode != Opcodes.INVOKEINTERFACE) return false
invoke as MethodInsnNode
if (!invoke.owner.startsWith("kotlin/jvm/functions/Function")) return false
if (invoke.name != "invoke") return false
if (Type.getReturnType(invoke.desc) != OBJECT_TYPE) return false
return Type.getArgumentTypes(invoke.desc).let { it.isNotEmpty() && it.last() == OBJECT_TYPE }
}
private fun replaceContinuationsWithFakeOnes(
continuations: Sequence<AbstractInsnNode>,
node: MethodNode
) {
for (toReplace in continuations) {
insertNodeBefore(createFakeContinuationMethodNodeForInline(), node, toReplace)
node.instructions.remove(toReplace)
}
}
private fun isSuspendCall(invoke: AbstractInsnNode?): Boolean {
if (invoke !is MethodInsnNode) return false
// We can't have suspending constructors.
assert(invoke.opcode != Opcodes.INVOKESPECIAL)
if (Type.getReturnType(invoke.desc) != OBJECT_TYPE) return false
return Type.getArgumentTypes(invoke.desc).let { it.isNotEmpty() && it.last() == CONTINUATION_ASM_TYPE }
}
private fun preprocessNodeBeforeInline(node: MethodNode, labelOwner: LabelOwner) {
try {
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
@@ -635,7 +707,6 @@ class MethodInliner(
needReification: Boolean,
capturesAnonymousObjectThatMustBeRegenerated: Boolean
): AnonymousObjectTransformationInfo {
val memoizeAnonymousObject = inliningContext.findAnonymousObjectTransformationInfo(anonymousType) == null
val info = AnonymousObjectTransformationInfo(
anonymousType, needReification, lambdaMapping,
@@ -647,8 +718,16 @@ class MethodInliner(
capturesAnonymousObjectThatMustBeRegenerated
)
if (memoizeAnonymousObject) {
inliningContext.root.internalNameToAnonymousObjectTransformationInfo.put(anonymousType, info)
val memoizeAnonymousObject = inliningContext.findAnonymousObjectTransformationInfo(anonymousType)
if (memoizeAnonymousObject == null ||
//anonymous object could be inlined in several context without transformation (keeps same class name)
// and on further inlining such code some of such cases would be transformed and some not,
// so we should distinguish one classes from another more clearly
!memoizeAnonymousObject.shouldRegenerate(isSameModule) &&
info.shouldRegenerate(isSameModule)
) {
inliningContext.recordIfNotPresent(anonymousType, info)
}
return info
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2000-2018 JetBrains s.r.o. 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
@@ -43,7 +43,7 @@ class SamWrapperTransformationInfo(override val oldClassName: String, private va
override fun canRemoveAfterTransformation() = false
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean) =
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean, continuationClassName: String?) =
SamWrapperTransformer(this, inliningContext)
}
@@ -34,7 +34,7 @@ interface TransformationInfo {
fun canRemoveAfterTransformation(): Boolean
fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*>
fun createTransformer(inliningContext: InliningContext, sameModule: Boolean, continuationClassName: String?): ObjectTransformer<*>
}
class WhenMappingTransformationInfo(
@@ -52,7 +52,7 @@ class WhenMappingTransformationInfo(
override fun canRemoveAfterTransformation(): Boolean = true
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*> =
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean, continuationClassName: String?): ObjectTransformer<*> =
WhenMappingTransformer(this, inliningContext)
companion object {
@@ -103,6 +103,10 @@ class AnonymousObjectTransformationInfo internal constructor(
return !isStaticOrigin
}
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*> =
AnonymousObjectTransformer(this, inliningContext, sameModule)
override fun createTransformer(
inliningContext: InliningContext,
sameModule: Boolean,
continuationClassName: String?
): ObjectTransformer<*> =
AnonymousObjectTransformer(this, inliningContext, sameModule, continuationClassName)
}
@@ -84,6 +84,8 @@ private const val INLINE_MARKER_BEFORE_SUSPEND_ID = 0
private const val INLINE_MARKER_AFTER_SUSPEND_ID = 1
private const val INLINE_MARKER_RETURNS_UNIT = 2
private const val INLINE_MARKER_FAKE_CONTINUATION = 3
private const val INLINE_MARKER_BEFORE_FAKE_CONTINUATION_CONSTRUCTOR_CALL = 4
private const val INLINE_MARKER_AFTER_FAKE_CONTINUATION_CONSTRUCTOR_CALL = 5
private val INTRINSIC_ARRAY_CONSTRUCTOR_TYPE = AsmUtil.asmTypeByClassId(classId)
internal fun getMethodNode(
@@ -295,6 +297,13 @@ internal fun insertNodeBefore(from: MethodNode, to: MethodNode, beforeNode: Abst
internal fun createEmptyMethodNode() = MethodNode(API, 0, "fake", "()V", null, null)
internal fun createFakeContinuationMethodNodeForInline(): MethodNode {
val methodNode = createEmptyMethodNode()
val v = InstructionAdapter(methodNode)
addFakeContinuationMarker(v)
return methodNode
}
internal fun firstLabelInChain(node: LabelNode): LabelNode {
var curNode = node
while (curNode.previous is LabelNode) {
@@ -425,6 +434,15 @@ internal fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
)
}
internal fun addFakeContinuationConstructorCallMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
v.iconst(if (isStartNotEnd) INLINE_MARKER_BEFORE_FAKE_CONTINUATION_CONSTRUCTOR_CALL else INLINE_MARKER_AFTER_FAKE_CONTINUATION_CONSTRUCTOR_CALL)
v.visitMethodInsn(
Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME,
"mark",
"(I)V", false
)
}
private fun addReturnsUnitMarker(v: InstructionAdapter) {
v.iconst(INLINE_MARKER_RETURNS_UNIT)
v.visitMethodInsn(
@@ -454,6 +472,10 @@ internal fun isAfterSuspendMarker(insn: AbstractInsnNode) = isSuspendMarker(insn
internal fun isReturnsUnitMarker(insn: AbstractInsnNode) = isSuspendMarker(insn, INLINE_MARKER_RETURNS_UNIT)
internal fun isFakeContinuationMarker(insn: AbstractInsnNode) =
insn.previous != null && isSuspendMarker(insn.previous, INLINE_MARKER_FAKE_CONTINUATION) && insn.opcode == Opcodes.ACONST_NULL
internal fun isBeforeFakeContinuationConstructorCallMarker(insn: AbstractInsnNode) =
isSuspendMarker(insn, INLINE_MARKER_BEFORE_FAKE_CONTINUATION_CONSTRUCTOR_CALL)
internal fun isAfterFakeContinuationConstructorCallMarker(insn: AbstractInsnNode) =
isSuspendMarker(insn, INLINE_MARKER_AFTER_FAKE_CONTINUATION_CONSTRUCTOR_CALL)
private fun isSuspendMarker(insn: AbstractInsnNode, id: Int) =
isInlineMarker(insn, "mark") && insn.previous.intConstant == id