From 50a5c08603974a66fe22adfb9b64d7d1fa178a99 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 8 Nov 2016 12:23:09 +0300 Subject: [PATCH] Optimize visibility for controller field in CoroutineImpl While it's impossible to declare a property with protected field and public getter we split these entities - `_controller` field is used inside coroutines - `controller` getter is used for accesing controller from noinline lambdas (also it can be used to obtain controller from continuation by explicit cast) --- .../kotlin/codegen/ExpressionCodegen.java | 35 +++++++++++++++---- .../coroutines/coroutineCodegenUtil.kt | 3 +- .../kotlin/resolve/inline/InlineUtil.java | 17 ++++++--- .../src/kotlin/jvm/internal/CoroutineImpl.kt | 6 ++-- 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 5ebdcba6188..cec4ce8054c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -3044,18 +3044,41 @@ public class ExpressionCodegen extends KtVisitor impleme ClassDescriptor classDescriptor = bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, descriptor); assert classDescriptor != null : "class descriptor for coroutine " + descriptor + " should not be null"; - StackValue coroutineReceiver = StackValue.thisOrOuter(this, classDescriptor, /* isSuper =*/ false, /* castReceiver */ false); - return StackValue.coercion( - StackValue.field( + final StackValue coroutineReceiver = StackValue.thisOrOuter(this, classDescriptor, /* isSuper =*/ false, /* castReceiver */ false); + + StackValue controllerValue; + if (InlineUtil.checkNonLocalReturnUsage( + context.getFunctionDescriptor(), + descriptor, DescriptorToSourceUtils.descriptorToDeclaration(descriptor), bindingContext + )) { + // This branch should work for major part of cases and it's needed mostly for optimizations purpose + // else branch must have just the same semantics + controllerValue = StackValue.field( FieldInfo.createForHiddenField( AsmTypes.COROUTINE_IMPL, AsmTypes.OBJECT_TYPE, CoroutineCodegenUtilKt.COROUTINE_CONTROLLER_FIELD_NAME ), coroutineReceiver - ), - typeMapper.mapType(coroutineControllerType) - ); + ); + } + else { + controllerValue = StackValue.functionCall(AsmTypes.OBJECT_TYPE, new Function1() { + @Override + public Unit invoke(InstructionAdapter adapter) { + coroutineReceiver.put(AsmTypes.COROUTINE_IMPL, adapter); + adapter.invokevirtual( + AsmTypes.COROUTINE_IMPL.getInternalName(), + CoroutineCodegenUtilKt.COROUTINE_CONTROLLER_GETTER_NAME, + "()" + AsmTypes.OBJECT_TYPE, + false + ); + return Unit.INSTANCE; + } + }); + } + + return StackValue.coercion(controllerValue, typeMapper.mapType(coroutineControllerType)); } return context.generateReceiver(descriptor, state, false); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt index 1ed609da940..406613d3899 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt @@ -47,7 +47,8 @@ const val AFTER_SUSPENSION_POINT_MARKER_NAME = "afterSuspensionPoint" const val HANDLE_EXCEPTION_MARKER_NAME = "handleException" const val HANDLE_EXCEPTION_ARGUMENT_MARKER_NAME = "handleExceptionArgument" -const val COROUTINE_CONTROLLER_FIELD_NAME = "controller" +const val COROUTINE_CONTROLLER_FIELD_NAME = "_controller" +const val COROUTINE_CONTROLLER_GETTER_NAME = "getController" const val COROUTINE_LABEL_FIELD_NAME = "label" data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val fakeThisExpression: KtExpression) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java index a7369056c57..171c5581a11 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java @@ -87,12 +87,19 @@ public class InlineUtil { return false; } - DeclarationDescriptor containingFunctionDescriptor = context.trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, containingFunction); - if (containingFunctionDescriptor == null) { - return false; - } + return checkNonLocalReturnUsage( + fromFunction, context.trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, containingFunction), containingFunction, + context.trace.getBindingContext() + ); + } - BindingContext bindingContext = context.trace.getBindingContext(); + public static boolean checkNonLocalReturnUsage( + @NotNull DeclarationDescriptor fromFunction, + @Nullable DeclarationDescriptor containingFunctionDescriptor, + @Nullable PsiElement containingFunction, + @NotNull BindingContext bindingContext + ) { + if (containingFunctionDescriptor == null) return false; while (canBeInlineArgument(containingFunction) && fromFunction != containingFunctionDescriptor) { if (!isInlinedArgument((KtFunction) containingFunction, bindingContext, true)) { diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt b/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt index fdec0e5d527..aa48c58249c 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt +++ b/core/runtime.jvm/src/kotlin/jvm/internal/CoroutineImpl.kt @@ -17,10 +17,12 @@ package kotlin.jvm.internal abstract class CoroutineImpl(arity: Int) : Lambda(arity), Continuation { + @JvmField + protected var _controller: Any? = null + // It's not protected because can be used from noinline lambdas inside coroutine (when calling non-suspend functions) // Also there might be needed a way to access a controller by Continuation instance when it's inherited from CoroutineImpl - @JvmField - var controller: Any? = null + val controller: Any? get() = _controller // Any label state less then zero indicates that coroutine is not run and can't be resumed in any way. // Specific values do not matter by now, but currently -2 used for uninitialized coroutine (no controller is assigned),