diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/commonCoroutineCodegenUtil.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/commonCoroutineCodegenUtil.kt index 2cce6692c02..c417567114f 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/commonCoroutineCodegenUtil.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/commonCoroutineCodegenUtil.kt @@ -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 + diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 65bc5ee9729..99e44d73e80 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1236,7 +1236,7 @@ public class ExpressionCodegen extends KtVisitor 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 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); 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 8e0b625251d..e7c22b636e5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt @@ -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.unwrapInitialDescriptorForSuspendFunction(): D = this.safeAs()?.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) as D ?: this diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index 3356a2c4d02..2c13783205b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -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( 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( 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( 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) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 76f6e4b7ea5..dcb29dd6cf9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -981,6 +981,7 @@ public interface Errors { DiagnosticFactory0 NON_LOCAL_SUSPENSION_POINT = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 ILLEGAL_SUSPEND_FUNCTION_CALL = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 ILLEGAL_SUSPEND_PROPERTY_ACCESS = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL = DiagnosticFactory0.create(ERROR); DiagnosticFactory1> PLUGIN_ERROR = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 9f3471bb792..b0fc0e13a5a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -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()); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt index 2dd1295cd72..2112980b886 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt @@ -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()?.isSuspend == true - }?.cast()?.ownerDescriptor?.cast() + val enclosingSuspendFunction = context.scope + .parentsWithSelf.firstOrNull { + it is LexicalScope && it.kind in ALLOWED_SCOPE_KINDS && + it.ownerDescriptor.safeAs()?.isSuspend == true + }?.cast()?.ownerDescriptor?.cast() 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)) + } } } } diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt new file mode 100644 index 00000000000..40d4eaaac0a --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt @@ -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 { + 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" +} diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt new file mode 100644 index 00000000000..3cc75ad640d --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt @@ -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 { + 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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt new file mode 100644 index 00000000000..9a25427166d --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt @@ -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 get(key: CoroutineContext.Key): E? = null + public override fun 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 { + 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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/coroutineContextIntrinsic.kt b/compiler/testData/codegen/bytecodeListing/coroutineContextIntrinsic.kt new file mode 100644 index 00000000000..92adc823fc4 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/coroutineContextIntrinsic.kt @@ -0,0 +1,18 @@ +// WITH_RUNTIME +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* + +suspend fun suspendHere(ctx: CoroutineContext) = suspendCoroutineOrReturn { 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()) +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/coroutineContextIntrinsic.txt b/compiler/testData/codegen/bytecodeListing/coroutineContextIntrinsic.txt new file mode 100644 index 00000000000..c6a61385e33 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/coroutineContextIntrinsic.txt @@ -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 (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 +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.kt new file mode 100644 index 00000000000..d35e5531596 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.kt @@ -0,0 +1,43 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +import kotlin.coroutines.experimental.intrinsics.coroutineContext +import kotlin.coroutines.experimental.CoroutineContext + +fun ordinal() { + coroutineContext +} + +suspend fun named() { + coroutineContext +} + +class A { + val coroutineContext = kotlin.coroutines.experimental.intrinsics.coroutineContext +} + +class Controller { + fun ordinal() { + 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 { coroutineContext } + builderSuspend { coroutineContext } + builderSeveralArgs {_, _,_ -> coroutineContext } + builderSuspendSeveralArgs {_, _,_ -> coroutineContext} +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.txt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.txt new file mode 100644 index 00000000000..0bbc8b110c6 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.txt @@ -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 +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 7a091cc73c8..dc4be3dd31b 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index cf32bcfb174..54fc14edead 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index 574cfda44b3..db6fe67cae1 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 805f3a5bcc4..177293080e4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 43780afee61..a40afcb4169 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index b8d1dab7038..0bd0b10b605 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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"); diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index 48183da4bd6..08008c80150 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.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)) } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 590b8cc498d..247bd842ae2 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -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"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java index 3523c2fcd77..b13c59e06a2 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java @@ -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) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/CoroutineContextFIF.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/CoroutineContextFIF.kt new file mode 100644 index 00000000000..b3c2267248c --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/CoroutineContextFIF.kt @@ -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, 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 + } + } +} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/inlineUtils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/inlineUtils.kt index 8813badb6d3..f8eb96322fc 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/inlineUtils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/inlineUtils.kt @@ -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 diff --git a/libraries/stdlib/src/kotlin/coroutines/experimental/intrinsics/Intrinsics.kt b/libraries/stdlib/src/kotlin/coroutines/experimental/intrinsics/Intrinsics.kt index 10a4dae9711..41d4c23c8fc 100644 --- a/libraries/stdlib/src/kotlin/coroutines/experimental/intrinsics/Intrinsics.kt +++ b/libraries/stdlib/src/kotlin/coroutines/experimental/intrinsics/Intrinsics.kt @@ -45,6 +45,19 @@ import kotlin.coroutines.experimental.processBareContinuationResume public inline suspend fun suspendCoroutineOrReturn(crossinline block: (Continuation) -> 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. diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 38c869563ba..d873c29d155 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -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 { diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt index 78a59e4f657..4d47997bd38 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt @@ -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 {