Implement coroutineContext intrinsic

This intrinsic allows coroutine to access its context without suspention
and, furthermore, disabling tail-call optimization.
KT-17609: Fixed
This commit is contained in:
Ilmir Usmanov
2017-10-23 13:38:23 +03:00
parent 0fffb9fb17
commit 817f79520a
28 changed files with 520 additions and 35 deletions
@@ -17,16 +17,19 @@
package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
val SUSPEND_COROUTINE_OR_RETURN_NAME = Name.identifier("suspendCoroutineOrReturn")
val COROUTINE_SUSPENDED_NAME = Name.identifier("COROUTINE_SUSPENDED")
val COROUTINES_INTRINSICS_PACKAGE_FQ_NAME = DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME.child(Name.identifier("intrinsics"))
val COROUTINE_CONTEXT_FQ_NAME = COROUTINES_INTRINSICS_PACKAGE_FQ_NAME.child(Name.identifier("coroutineContext"))
fun FunctionDescriptor.isBuiltInSuspendCoroutineOrReturn(): Boolean {
if (name != SUSPEND_COROUTINE_OR_RETURN_NAME) return false
@@ -42,3 +45,7 @@ fun FunctionDescriptor.getBuiltInSuspendCoroutineOrReturn() =
module.getPackage(COROUTINES_INTRINSICS_PACKAGE_FQ_NAME).memberScope
.getContributedFunctions(SUSPEND_COROUTINE_OR_RETURN_NAME, NoLookupLocation.FROM_BACKEND)
.singleOrNull()
fun FunctionDescriptor.isBuiltInCoroutineContext() =
(this as? PropertyGetterDescriptor)?.correspondingProperty?.fqNameSafe == COROUTINE_CONTEXT_FQ_NAME
@@ -1236,7 +1236,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
@Nullable
private StackValue genCoroutineInstanceForSuspendLambda(@NotNull FunctionDescriptor suspendFunction) {
public StackValue genCoroutineInstanceForSuspendLambda(@NotNull FunctionDescriptor suspendFunction) {
if (!(suspendFunction instanceof AnonymousFunctionDescriptor)) return null;
ClassDescriptor suspendLambdaClassDescriptor = bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, suspendFunction);
@@ -2209,6 +2209,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
assert enclosingSuspendFunctionJvmView != null : "No JVM view function found for " + enclosingSuspendFunction;
return getContinuationParameterFromEnclosingSuspendFunctionDescriptor(enclosingSuspendFunctionJvmView);
}
@Nullable
public StackValue getContinuationParameterFromEnclosingSuspendFunctionDescriptor(@NotNull FunctionDescriptor enclosingSuspendFunctionJvmView) {
ValueParameterDescriptor continuationParameter =
enclosingSuspendFunctionJvmView.getValueParameters()
.get(enclosingSuspendFunctionJvmView.getValueParameters().size() - 1);
@@ -17,9 +17,7 @@
package org.jetbrains.kotlin.codegen.coroutines
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.common.COROUTINES_INTRINSICS_PACKAGE_FQ_NAME
import org.jetbrains.kotlin.backend.common.COROUTINE_SUSPENDED_NAME
import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineOrReturn
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
@@ -66,6 +64,9 @@ val COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME =
@JvmField
val CONTINUATION_ASM_TYPE = DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME.topLevelClassAsmType()
@JvmField
val COROUTINE_CONTEXT_ASM_TYPE = DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME.child(Name.identifier("CoroutineContext")).topLevelClassAsmType()
@JvmField
val COROUTINE_IMPL_ASM_TYPE = COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("CoroutineImpl")).topLevelClassAsmType()
@@ -76,6 +77,7 @@ private val INTERNAL_COROUTINE_INTRINSICS_OWNER_INTERNAL_NAME =
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("CoroutineIntrinsics")).topLevelClassInternalName()
private val NORMALIZE_CONTINUATION_METHOD_NAME = "normalizeContinuation"
private val GET_CONTEXT_METHOD_NAME = "getContext"
data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val fakeContinuationExpression: KtExpression)
@@ -287,6 +289,35 @@ fun createMethodNodeForSuspendCoroutineOrReturn(
return node
}
fun createMethodNodeForCoroutineContext(functionDescriptor: FunctionDescriptor): MethodNode {
assert(functionDescriptor.isBuiltInCoroutineContext()) {
"functionDescriptor must be kotlin.coroutines.intrinsics.coroutineContext property getter"
}
val node =
MethodNode(
Opcodes.ASM5,
Opcodes.ACC_STATIC,
"fake",
Type.getMethodDescriptor(COROUTINE_CONTEXT_ASM_TYPE, CONTINUATION_ASM_TYPE),
null, null
)
node.visitVarInsn(Opcodes.ALOAD, 0)
node.visitMethodInsn(
Opcodes.INVOKEINTERFACE,
CONTINUATION_ASM_TYPE.internalName,
GET_CONTEXT_METHOD_NAME,
Type.getMethodDescriptor(COROUTINE_CONTEXT_ASM_TYPE),
true
)
node.visitInsn(Opcodes.ARETURN)
node.visitMaxs(1, 1)
return node
}
@Suppress("UNCHECKED_CAST")
fun <D : CallableDescriptor?> D.unwrapInitialDescriptorForSuspendFunction(): D =
this.safeAs<SimpleFunctionDescriptor>()?.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) as D ?: this
@@ -18,12 +18,13 @@ package org.jetbrains.kotlin.codegen.inline
import com.intellij.psi.PsiElement
import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.backend.common.isBuiltInCoroutineContext
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForSuspendCoroutineOrReturn
import org.jetbrains.kotlin.codegen.coroutines.isBuiltInSuspendCoroutineOrReturnInJvm
import org.jetbrains.kotlin.codegen.context.ClosureContext
import org.jetbrains.kotlin.codegen.coroutines.*
import org.jetbrains.kotlin.codegen.intrinsics.bytecode
import org.jetbrains.kotlin.codegen.intrinsics.classId
import org.jetbrains.kotlin.codegen.state.GenerationState
@@ -50,8 +51,6 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral
import org.jetbrains.kotlin.types.expressions.LabelResolver
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -225,6 +224,19 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
return true
}
private fun continuationValue(): StackValue {
assert(codegen is ExpressionCodegen) { "Expected ExpressionCodegen in coroutineContext inlining" }
codegen as ExpressionCodegen
val parentContext = codegen.context.parentContext
return if (parentContext is ClosureContext) {
val originalSuspendLambdaDescriptor = parentContext.originalSuspendLambdaDescriptor ?: error("No original lambda descriptor found")
codegen.genCoroutineInstanceForSuspendLambda(originalSuspendLambdaDescriptor) ?: error("No stack value for coroutine instance of lambda found")
}
else
codegen.getContinuationParameterFromEnclosingSuspendFunctionDescriptor(codegen.context.functionDescriptor) ?: error("No stack value for continuation parameter of suspend function")
}
protected fun inlineCall(nodeAndSmap: SMAPAndMethodNode, callDefault: Boolean): InlineResult {
assert(delayedHiddenWriting == null) { "'putHiddenParamsIntoLocals' should be called after 'processAndPutHiddenParameters(true)'" }
defaultSourceMapper.callSiteMarker = CallSiteMarker(codegen.lastLineNumber)
@@ -252,6 +264,9 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
addInlineMarker(codegen.v, true)
}
if (functionDescriptor.isBuiltInCoroutineContext())
invocationParamBuilder.addNextValueParameter(CONTINUATION_ASM_TYPE, false, continuationValue(), 0)
val parameters = invocationParamBuilder.buildParameters()
val info = RootInliningContext(
@@ -458,24 +473,28 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
state: GenerationState,
sourceCompilerForInline: SourceCompilerForInline
): SMAPAndMethodNode {
if (isSpecialEnumMethod(functionDescriptor)) {
val arguments = resolvedCall!!.typeArguments
when {
isSpecialEnumMethod(functionDescriptor) -> {
val arguments = resolvedCall!!.typeArguments
val node = createSpecialEnumMethodBody(
codegen,
functionDescriptor.name.asString(),
arguments.keys.single().defaultType,
state.typeMapper
)
return SMAPAndMethodNode(node, SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1))
}
else if (functionDescriptor.isBuiltInSuspendCoroutineOrReturnInJvm()) {
return SMAPAndMethodNode(
createMethodNodeForSuspendCoroutineOrReturn(
functionDescriptor, state.typeMapper
),
val node = createSpecialEnumMethodBody(
codegen,
functionDescriptor.name.asString(),
arguments.keys.single().defaultType,
state.typeMapper
)
return SMAPAndMethodNode(node, SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1))
}
functionDescriptor.isBuiltInSuspendCoroutineOrReturnInJvm() ->
return SMAPAndMethodNode(
createMethodNodeForSuspendCoroutineOrReturn(functionDescriptor, state.typeMapper),
SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)
)
)
functionDescriptor.isBuiltInCoroutineContext() ->
return SMAPAndMethodNode(
createMethodNodeForCoroutineContext(functionDescriptor),
SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)
)
}
val asmMethod = if (callDefault)
@@ -981,6 +981,7 @@ public interface Errors {
DiagnosticFactory0<PsiElement> NON_LOCAL_SUSPENSION_POINT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, CallableDescriptor> ILLEGAL_SUSPEND_FUNCTION_CALL = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, CallableDescriptor> ILLEGAL_SUSPEND_PROPERTY_ACCESS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, RenderedDiagnostic<?>> PLUGIN_ERROR = DiagnosticFactory1.create(ERROR);
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.config.LanguageVersion;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.diagnostics.RenderedDiagnostic;
import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.VersionRequirement;
import org.jetbrains.kotlin.types.KotlinTypeKt;
@@ -870,6 +869,7 @@ public class DefaultErrorMessages {
MAP.put(NON_LOCAL_RETURN_IN_DISABLED_INLINE, "Non-local returns are not allowed with inlining disabled");
MAP.put(NON_LOCAL_SUSPENSION_POINT, "Suspension functions can be called only within coroutine body");
MAP.put(ILLEGAL_SUSPEND_FUNCTION_CALL, "Suspend function ''{0}'' should be called only from a coroutine or another suspend function", NAME);
MAP.put(ILLEGAL_SUSPEND_PROPERTY_ACCESS, "Suspend property ''{0}'' should be accessed only from a coroutine or suspend function", NAME);
MAP.put(ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL, "Restricted suspending functions can only invoke member or extension suspending functions on their restricted coroutine scope");
MAP.put(PLUGIN_ERROR, "{0}", (d, c) -> d.getText());
@@ -22,12 +22,15 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.coroutines.hasSuspendFunctionType
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtThisExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.hasRestrictsSuspensionAnnotation
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.scopes.HierarchicalScope
@@ -42,17 +45,21 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object CoroutineSuspendCallChecker : CallChecker {
private val ALLOWED_SCOPE_KINDS = setOf(LexicalScopeKind.FUNCTION_INNER_SCOPE, LexicalScopeKind.FUNCTION_HEADER_FOR_DESTRUCTURING)
private val COROUTINE_CONTEXT_FQ_NAME = FqName("kotlin.coroutines.experimental.intrinsics.coroutineContext")
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val descriptor = resolvedCall.candidateDescriptor as? FunctionDescriptor ?: return
if (!descriptor.isSuspend) return
val descriptor = resolvedCall.candidateDescriptor
when (descriptor) {
is FunctionDescriptor -> if (!descriptor.isSuspend) return
is PropertyDescriptor -> if (descriptor.fqNameSafe != COROUTINE_CONTEXT_FQ_NAME) return
else -> return
}
val enclosingSuspendFunction =
context.scope
.parentsWithSelf.firstOrNull {
it is LexicalScope && it.kind in ALLOWED_SCOPE_KINDS &&
it.ownerDescriptor.safeAs<FunctionDescriptor>()?.isSuspend == true
}?.cast<LexicalScope>()?.ownerDescriptor?.cast<FunctionDescriptor>()
val enclosingSuspendFunction = context.scope
.parentsWithSelf.firstOrNull {
it is LexicalScope && it.kind in ALLOWED_SCOPE_KINDS &&
it.ownerDescriptor.safeAs<FunctionDescriptor>()?.isSuspend == true
}?.cast<LexicalScope>()?.ownerDescriptor?.cast<FunctionDescriptor>()
when {
enclosingSuspendFunction != null -> {
@@ -70,7 +77,10 @@ object CoroutineSuspendCallChecker : CallChecker {
checkRestrictsSuspension(enclosingSuspendFunction, resolvedCall, reportOn, context)
}
else -> {
context.trace.report(Errors.ILLEGAL_SUSPEND_FUNCTION_CALL.on(reportOn, resolvedCall.candidateDescriptor))
when (descriptor) {
is FunctionDescriptor -> context.trace.report(Errors.ILLEGAL_SUSPEND_FUNCTION_CALL.on(reportOn, resolvedCall.candidateDescriptor))
is PropertyDescriptor -> context.trace.report(Errors.ILLEGAL_SUSPEND_PROPERTY_ACCESS.on(reportOn, resolvedCall.candidateDescriptor))
}
}
}
}
@@ -0,0 +1,42 @@
// WITH_RUNTIME
// WITH_COROUTINES
import helpers.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
import kotlin.test.assertEquals
suspend fun suspendHere() = if(coroutineContext != EmptyCoroutineContext) "$coroutineContext != $EmptyCoroutineContext" else "OK"
suspend fun multipleArgs(a: Any, b: Any, c: Any) = if(coroutineContext != EmptyCoroutineContext) "$coroutineContext != $EmptyCoroutineContext" else "OK"
fun builder(c: suspend () -> String): String {
var fromSuspension: String? = null
val continuation = object: Continuation<String> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWithException(exception: Throwable) {
fromSuspension = "Exception: ${exception}"
}
override fun resume(value: String) {
fromSuspension = value
}
}
c.startCoroutine(continuation)
return fromSuspension as String
}
fun box(): String {
var res = builder { suspendHere() }
if (res != "OK") { return "fail 1 $res"}
res = builder { multipleArgs(1,1,1) }
if (res != "OK") { return "fail 2 $res"}
res = builder { if(coroutineContext != EmptyCoroutineContext) "$coroutineContext != $EmptyCoroutineContext" else "OK" }
if (res != "OK") { return "fail 3 $res"}
return "OK"
}
@@ -0,0 +1,43 @@
// WITH_RUNTIME
// WITH_COROUTINES
import helpers.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
import kotlin.test.assertEquals
class Controller {
suspend fun controllerSuspendHere() = if(coroutineContext != EmptyCoroutineContext) "$coroutineContext != $EmptyCoroutineContext" else "OK"
suspend fun controllerMultipleArgs(a: Any, b: Any, c: Any) = if(coroutineContext != EmptyCoroutineContext) "$coroutineContext != $EmptyCoroutineContext" else "OK"
fun builder(c: suspend Controller.() -> String): String {
var fromSuspension: String? = null
c.startCoroutine(this, object: Continuation<String> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWithException(exception: Throwable) {
fromSuspension = "Exception: " + exception.message!!
}
override fun resume(value: String) {
fromSuspension = value
}
})
return fromSuspension as String
}
}
fun box(): String {
val c = Controller()
var res = c.builder { controllerSuspendHere() }
if (res != "OK") { return "fail 1 $res"}
res = c.builder { controllerMultipleArgs(1,1,1) }
if (res != "OK") { return "fail 2 $res"}
res = c.builder { if(coroutineContext != EmptyCoroutineContext) "$coroutineContext != $EmptyCoroutineContext" else "OK" }
if (res != "OK") { return "fail 3 $res"}
return "OK"
}
@@ -0,0 +1,59 @@
// WITH_RUNTIME
// WITH_COROUTINES
import helpers.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
import kotlin.test.assertEquals
class Controller {
val coroutineContext = object : CoroutineContext {
public override fun <E : CoroutineContext.Element> get(key: CoroutineContext.Key<E>): E? = null
public override fun <R> fold(initial: R, operation: (R, CoroutineContext.Element) -> R): R = initial
public override fun plus(context: CoroutineContext): CoroutineContext = context
public override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext = this
public override fun hashCode(): Int = 0
public override fun toString(): String = "AnotherEmptyCoroutineContext"
}
suspend fun controllerSuspendHere() = if(coroutineContext == EmptyCoroutineContext) "$coroutineContext == $EmptyCoroutineContext" else "OK"
suspend fun controllerSuspendHereIntrinsic() = if(kotlin.coroutines.experimental.intrinsics.coroutineContext != EmptyCoroutineContext)
"${kotlin.coroutines.experimental.intrinsics.coroutineContext} != $EmptyCoroutineContext"
else
"OK"
suspend fun controllerMultipleArgs(a: Any, b: Any, c: Any) = if(coroutineContext == EmptyCoroutineContext) "$coroutineContext == $EmptyCoroutineContext" else "OK"
fun builder(c: suspend Controller.() -> String): String {
var fromSuspension: String? = null
c.startCoroutine(this, object: Continuation<String> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWithException(exception: Throwable) {
fromSuspension = "Exception: " + exception.message!!
}
override fun resume(value: String) {
fromSuspension = value
}
})
return fromSuspension as String
}
}
fun box(): String {
val v = Controller()
var res = v.builder { controllerMultipleArgs(1,1,1) }
if (res != "OK") { return "fail 1 $res"}
res = v.builder { if(coroutineContext == EmptyCoroutineContext) "$coroutineContext == $EmptyCoroutineContext" else "OK" }
if (res != "OK") { return "fail 2 $res"}
res = v.builder { controllerSuspendHereIntrinsic() }
if (res != "OK") { return "fail 3 $res"}
res = v.builder { controllerSuspendHere() }
if (res != "OK") { return "fail 4 $res"}
return "OK"
}
@@ -0,0 +1,18 @@
// WITH_RUNTIME
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
suspend fun suspendHere(ctx: CoroutineContext) = suspendCoroutineOrReturn<String> { x ->
if (x.context == ctx) x.resume("OK") else x.resume("FAIL")
}
suspend fun mustBeTailCall(): String {
return suspendHere(coroutineContext)
}
suspend fun retrieveCoroutineContext(): CoroutineContext =
suspendCoroutineOrReturn { cont -> cont.context }
suspend fun notTailCall(): String {
return suspendHere(retrieveCoroutineContext())
}
@@ -0,0 +1,19 @@
@kotlin.Metadata
final class CoroutineContextIntrinsicKt$notTailCall$1 {
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
inner class CoroutineContextIntrinsicKt$notTailCall$1
method <init>(p0: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
public final class CoroutineContextIntrinsicKt {
inner class CoroutineContextIntrinsicKt$notTailCall$1
public final static @org.jetbrains.annotations.Nullable method mustBeTailCall(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method notTailCall(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method retrieveCoroutineContext(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.CoroutineContext, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
}
@@ -0,0 +1,43 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
import kotlin.coroutines.experimental.intrinsics.coroutineContext
import kotlin.coroutines.experimental.CoroutineContext
fun ordinal() {
<!ILLEGAL_SUSPEND_PROPERTY_ACCESS!>coroutineContext<!>
}
suspend fun named() {
coroutineContext
}
class A {
val coroutineContext = kotlin.coroutines.experimental.intrinsics.<!ILLEGAL_SUSPEND_PROPERTY_ACCESS!>coroutineContext<!>
}
class Controller {
fun ordinal() {
<!ILLEGAL_SUSPEND_PROPERTY_ACCESS!>coroutineContext<!>
}
suspend fun named() {
coroutineContext
}
suspend fun severalArgs(s: String, a: Any) {
coroutineContext
}
}
fun builder(c: () -> CoroutineContext) = {}
fun builderSuspend(c: suspend () -> CoroutineContext) = {}
fun builderSeveralArgs(c: (Int, Int, Int) -> CoroutineContext) = {}
fun builderSuspendSeveralArgs(c: suspend (Int, Int, Int) -> CoroutineContext) = {}
fun test() {
builder { <!ILLEGAL_SUSPEND_PROPERTY_ACCESS!>coroutineContext<!> }
builderSuspend { coroutineContext }
builderSeveralArgs {_, _,_ -> <!ILLEGAL_SUSPEND_PROPERTY_ACCESS!>coroutineContext<!> }
builderSuspendSeveralArgs {_, _,_ -> coroutineContext}
}
@@ -0,0 +1,27 @@
package
public fun builder(/*0*/ c: () -> kotlin.coroutines.experimental.CoroutineContext): () -> kotlin.Unit
public fun builderSeveralArgs(/*0*/ c: (kotlin.Int, kotlin.Int, kotlin.Int) -> kotlin.coroutines.experimental.CoroutineContext): () -> kotlin.Unit
public fun builderSuspend(/*0*/ c: suspend () -> kotlin.coroutines.experimental.CoroutineContext): () -> kotlin.Unit
public fun builderSuspendSeveralArgs(/*0*/ c: suspend (kotlin.Int, kotlin.Int, kotlin.Int) -> kotlin.coroutines.experimental.CoroutineContext): () -> kotlin.Unit
public suspend fun named(): kotlin.Unit
public fun ordinal(): kotlin.Unit
public fun test(): kotlin.Unit
public final class A {
public constructor A()
public final val coroutineContext: kotlin.coroutines.experimental.CoroutineContext
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Controller {
public constructor Controller()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public final suspend fun named(): kotlin.Unit
public final fun ordinal(): kotlin.Unit
public final suspend fun severalArgs(/*0*/ s: kotlin.String, /*1*/ a: kotlin.Any): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -5833,6 +5833,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/intrinsicSemantics"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("coroutineContext.kt")
public void testCoroutineContext() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt");
doTest(fileName);
}
@TestMetadata("coroutineContextReceiver.kt")
public void testCoroutineContextReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt");
doTest(fileName);
}
@TestMetadata("coroutineContextReceiverNotIntrinsic.kt")
public void testCoroutineContextReceiverNotIntrinsic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt");
doTest(fileName);
}
@TestMetadata("startCoroutine.kt")
public void testStartCoroutine() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt");
@@ -1271,6 +1271,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
doTest(fileName);
}
@TestMetadata("coroutineContext.kt")
public void testCoroutineContext() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.kt");
doTest(fileName);
}
@TestMetadata("coroutinesDisabled.kt")
public void testCoroutinesDisabled() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutinesDisabled.kt");
@@ -1271,6 +1271,12 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno
doTest(fileName);
}
@TestMetadata("coroutineContext.kt")
public void testCoroutineContext() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.kt");
doTest(fileName);
}
@TestMetadata("coroutinesDisabled.kt")
public void testCoroutinesDisabled() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutinesDisabled.kt");
@@ -5833,6 +5833,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/intrinsicSemantics"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("coroutineContext.kt")
public void testCoroutineContext() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt");
doTest(fileName);
}
@TestMetadata("coroutineContextReceiver.kt")
public void testCoroutineContextReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt");
doTest(fileName);
}
@TestMetadata("coroutineContextReceiverNotIntrinsic.kt")
public void testCoroutineContextReceiverNotIntrinsic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt");
doTest(fileName);
}
@TestMetadata("startCoroutine.kt")
public void testStartCoroutine() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt");
@@ -42,6 +42,12 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
doTest(fileName);
}
@TestMetadata("coroutineContextIntrinsic.kt")
public void testCoroutineContextIntrinsic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/coroutineContextIntrinsic.kt");
doTest(fileName);
}
@TestMetadata("coroutineFields.kt")
public void testCoroutineFields() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/coroutineFields.kt");
@@ -5833,6 +5833,24 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/intrinsicSemantics"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("coroutineContext.kt")
public void testCoroutineContext() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt");
doTest(fileName);
}
@TestMetadata("coroutineContextReceiver.kt")
public void testCoroutineContextReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt");
doTest(fileName);
}
@TestMetadata("coroutineContextReceiverNotIntrinsic.kt")
public void testCoroutineContextReceiverNotIntrinsic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt");
doTest(fileName);
}
@TestMetadata("startCoroutine.kt")
public void testStartCoroutine() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt");
@@ -716,7 +716,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
ExtractionGeneratorOptions(inTempFile = true, allowExpressionBody = false)
).generateDeclaration().declaration.getGeneratedBody()
val virtualContext = body.analyzeFully()
if (virtualContext.diagnostics.all().any { it.factory == Errors.ILLEGAL_SUSPEND_FUNCTION_CALL }) {
if (virtualContext.diagnostics.all().any { it.factory == Errors.ILLEGAL_SUSPEND_FUNCTION_CALL || it.factory == Errors.ILLEGAL_SUSPEND_PROPERTY_ACCESS }) {
descriptor = descriptor.copy(modifiers = listOf(KtTokens.SUSPEND_KEYWORD))
}
@@ -6391,6 +6391,24 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/intrinsicSemantics"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("coroutineContext.kt")
public void testCoroutineContext() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt");
doTest(fileName);
}
@TestMetadata("coroutineContextReceiver.kt")
public void testCoroutineContextReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt");
doTest(fileName);
}
@TestMetadata("coroutineContextReceiverNotIntrinsic.kt")
public void testCoroutineContextReceiverNotIntrinsic() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt");
doTest(fileName);
}
@TestMetadata("startCoroutine.kt")
public void testStartCoroutine() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt");
@@ -51,6 +51,7 @@ public final class FunctionIntrinsics {
register(ThrowableConstructorIntrinsicFactory.INSTANCE);
register(ExceptionPropertyIntrinsicFactory.INSTANCE);
register(AsDynamicFIF.INSTANCE);
register(CoroutineContextFIF.INSTANCE);
}
private void register(@NotNull FunctionIntrinsicFactory instance) {
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2017 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.
*/
package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories
import org.jetbrains.kotlin.backend.common.isBuiltInCoroutineContext
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
import org.jetbrains.kotlin.js.backend.ast.metadata.inlineStrategy
import org.jetbrains.kotlin.js.descriptorUtils.isCoroutineLambda
import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
object CoroutineContextFIF : FunctionIntrinsicFactory {
override fun getIntrinsic(descriptor: FunctionDescriptor): FunctionIntrinsic? {
if (!descriptor.isBuiltInCoroutineContext()) return null
return Intrinsic
}
object Intrinsic : FunctionIntrinsic() {
override fun apply(callInfo: CallInfo, arguments: List<JsExpression>, context: TranslationContext): JsExpression {
val continuation = context.continuationParameterDescriptor ?: error("coroutineContext called from outside of coroutine")
val continuationDescriptor = continuation.type.constructor.declarationDescriptor as? ClassDescriptor ?: error("Continuation is not a class")
val contContext = DescriptorUtils.getPropertyByName(continuationDescriptor.unsubstitutedMemberScope, Name.identifier("context"))
val res = JsNameRef(
context.getNameForDescriptor(contContext),
if (context.declarationDescriptor?.isCoroutineLambda == true) JsAstUtils.stateMachineReceiver()
else context.getNameForDescriptor(continuation).makeRef()
)
res.inlineStrategy = InlineStrategy.NOT_INLINE
return res
}
}
}
@@ -81,6 +81,7 @@ fun setInlineCallMetadata(
descriptor: CallableDescriptor,
context: TranslationContext
) {
if (nameRef.inlineStrategy != null) return
nameRef.descriptor = descriptor
nameRef.inlineStrategy = InlineStrategy.IN_PLACE
nameRef.psiElement = psiElement
@@ -45,6 +45,19 @@ import kotlin.coroutines.experimental.processBareContinuationResume
public inline suspend fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T =
throw NotImplementedError("Implementation is intrinsic")
/**
* Continuation context of current coroutine.
*
* This allows the user code to not pass an extra [CoroutineContext] parameter in basic coroutine builders like [launch] and [async],
* but still provide easy access to coroutine context.
*/
@SinceKotlin("1.2")
@Suppress("WRONG_MODIFIER_TARGET")
public inline suspend val coroutineContext: CoroutineContext
get() {
throw NotImplementedError("Implemented as intrinsic")
}
/**
* This value is used as a return value of [suspendCoroutineOrReturn] `block` argument to state that
* the execution was suspended and will not return any result immediately.
@@ -2057,6 +2057,7 @@ public final class kotlin/coroutines/experimental/intrinsics/IntrinsicsKt {
public static final fun createCoroutineUnchecked (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/experimental/Continuation;)Lkotlin/coroutines/experimental/Continuation;
public static final fun createCoroutineUnchecked (Lkotlin/jvm/functions/Function2;Ljava/lang/Object;Lkotlin/coroutines/experimental/Continuation;)Lkotlin/coroutines/experimental/Continuation;
public static final fun getCOROUTINE_SUSPENDED ()Ljava/lang/Object;
public static final fun getCoroutineContext ()Lkotlin/coroutines/experimental/CoroutineContext;
}
public abstract class kotlin/coroutines/experimental/jvm/internal/CoroutineImpl : kotlin/jvm/internal/Lambda, kotlin/coroutines/experimental/Continuation {
@@ -1876,6 +1876,7 @@ public final class kotlin/coroutines/experimental/intrinsics/IntrinsicsKt {
public static final fun createCoroutineUnchecked (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/experimental/Continuation;)Lkotlin/coroutines/experimental/Continuation;
public static final fun createCoroutineUnchecked (Lkotlin/jvm/functions/Function2;Ljava/lang/Object;Lkotlin/coroutines/experimental/Continuation;)Lkotlin/coroutines/experimental/Continuation;
public static final fun getCOROUTINE_SUSPENDED ()Ljava/lang/Object;
public static final fun getCoroutineContext ()Lkotlin/coroutines/experimental/CoroutineContext;
}
public abstract class kotlin/coroutines/experimental/jvm/internal/CoroutineImpl : kotlin/jvm/internal/Lambda, kotlin/coroutines/experimental/Continuation {