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)
This commit is contained in:
Denis Zharkov
2016-11-08 12:23:09 +03:00
parent 020bab1d7c
commit 50a5c08603
4 changed files with 47 additions and 14 deletions
@@ -3044,18 +3044,41 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> 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<InstructionAdapter, Unit>() {
@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);
@@ -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)
@@ -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)) {
@@ -17,10 +17,12 @@
package kotlin.jvm.internal
abstract class CoroutineImpl(arity: Int) : Lambda(arity), Continuation<Any?> {
@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),