Support suspendCoroutineUninterceptedOrReturn intrinsic
KT-17336: Fixed
This commit is contained in:
+20
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
@@ -26,11 +27,23 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
|
||||
val SUSPEND_COROUTINE_OR_RETURN_NAME = Name.identifier("suspendCoroutineOrReturn")
|
||||
val INTERCEPTED_NAME = Name.identifier("intercepted")
|
||||
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"))
|
||||
|
||||
val SUSPEND_COROUTINE_UNINTERCEPTED_OR_RETURN_NAME = Name.identifier("suspendCoroutineUninterceptedOrReturn")
|
||||
|
||||
fun FunctionDescriptor.isBuiltInIntercepted(): Boolean {
|
||||
if (name != INTERCEPTED_NAME) return false
|
||||
val original =
|
||||
module.getPackage(COROUTINES_INTRINSICS_PACKAGE_FQ_NAME).memberScope
|
||||
.getContributedFunctions(INTERCEPTED_NAME, NoLookupLocation.FROM_BACKEND)
|
||||
.singleOrNull() as CallableDescriptor
|
||||
return DescriptorEquivalenceForOverrides.areEquivalent(original, this)
|
||||
}
|
||||
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineOrReturn(): Boolean {
|
||||
if (name != SUSPEND_COROUTINE_OR_RETURN_NAME) return false
|
||||
|
||||
@@ -49,3 +62,10 @@ fun FunctionDescriptor.getBuiltInSuspendCoroutineOrReturn() =
|
||||
fun FunctionDescriptor.isBuiltInCoroutineContext() =
|
||||
(this as? PropertyGetterDescriptor)?.correspondingProperty?.fqNameSafe == COROUTINE_CONTEXT_FQ_NAME
|
||||
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(): Boolean {
|
||||
if (name != SUSPEND_COROUTINE_UNINTERCEPTED_OR_RETURN_NAME) return false
|
||||
val original = module.getPackage(COROUTINES_INTRINSICS_PACKAGE_FQ_NAME).memberScope
|
||||
.getContributedFunctions(SUSPEND_COROUTINE_UNINTERCEPTED_OR_RETURN_NAME, NoLookupLocation.FROM_BACKEND)
|
||||
.singleOrNull() as CallableDescriptor
|
||||
return DescriptorEquivalenceForOverrides.areEquivalent(original, this)
|
||||
}
|
||||
|
||||
+68
-2
@@ -172,7 +172,7 @@ private fun NewResolvedCallImpl<VariableDescriptor>.asDummyOldResolvedCall(bindi
|
||||
fun ResolvedCall<*>.isSuspendNoInlineCall() =
|
||||
resultingDescriptor.safeAs<FunctionDescriptor>()
|
||||
?.let {
|
||||
it.isSuspend && (!it.isInline || it.isBuiltInSuspendCoroutineOrReturnInJvm())
|
||||
it.isSuspend && (!it.isInline || it.isBuiltInSuspendCoroutineOrReturnInJvm() || it.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm())
|
||||
} == true
|
||||
|
||||
fun CallableDescriptor.isSuspendFunctionNotSuspensionView(): Boolean {
|
||||
@@ -245,7 +245,6 @@ fun ModuleDescriptor.getContinuationOfTypeOrAny(kotlinType: KotlinType) =
|
||||
)
|
||||
} ?: module.builtIns.nullableAnyType
|
||||
|
||||
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineOrReturnInJvm() =
|
||||
getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION)?.isBuiltInSuspendCoroutineOrReturn() == true
|
||||
|
||||
@@ -289,6 +288,40 @@ fun createMethodNodeForSuspendCoroutineOrReturn(
|
||||
return node
|
||||
}
|
||||
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm() =
|
||||
getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION)?.isBuiltInSuspendCoroutineUninterceptedOrReturn() == true
|
||||
|
||||
fun createMethodNodeForIntercepted(
|
||||
functionDescriptor: FunctionDescriptor,
|
||||
typeMapper: KotlinTypeMapper
|
||||
): MethodNode {
|
||||
assert(functionDescriptor.isBuiltInIntercepted()) {
|
||||
"functionDescriptor must be kotlin.coroutines.intrinsics.intercepted"
|
||||
}
|
||||
|
||||
val node =
|
||||
MethodNode(
|
||||
Opcodes.ASM5,
|
||||
Opcodes.ACC_STATIC,
|
||||
"fake",
|
||||
typeMapper.mapAsmMethod(functionDescriptor).descriptor, null, null
|
||||
)
|
||||
|
||||
node.visitVarInsn(Opcodes.ALOAD, 0)
|
||||
|
||||
node.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
INTERNAL_COROUTINE_INTRINSICS_OWNER_INTERNAL_NAME,
|
||||
NORMALIZE_CONTINUATION_METHOD_NAME,
|
||||
Type.getMethodDescriptor(CONTINUATION_ASM_TYPE, CONTINUATION_ASM_TYPE),
|
||||
false
|
||||
)
|
||||
node.visitInsn(Opcodes.ARETURN)
|
||||
node.visitMaxs(1, 1)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
fun createMethodNodeForCoroutineContext(functionDescriptor: FunctionDescriptor): MethodNode {
|
||||
assert(functionDescriptor.isBuiltInCoroutineContext()) {
|
||||
"functionDescriptor must be kotlin.coroutines.intrinsics.coroutineContext property getter"
|
||||
@@ -318,6 +351,39 @@ fun createMethodNodeForCoroutineContext(functionDescriptor: FunctionDescriptor):
|
||||
return node
|
||||
}
|
||||
|
||||
|
||||
fun createMethodNodeForSuspendCoroutineUninterceptedOrReturn(
|
||||
functionDescriptor: FunctionDescriptor,
|
||||
typeMapper: KotlinTypeMapper
|
||||
): MethodNode {
|
||||
assert(functionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm()) {
|
||||
"functionDescriptor must be kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn"
|
||||
}
|
||||
|
||||
val node =
|
||||
MethodNode(
|
||||
Opcodes.ASM5,
|
||||
Opcodes.ACC_STATIC,
|
||||
"fake",
|
||||
typeMapper.mapAsmMethod(functionDescriptor).descriptor, null, null
|
||||
)
|
||||
|
||||
node.visitVarInsn(Opcodes.ALOAD, 0)
|
||||
node.visitVarInsn(Opcodes.ALOAD, 1)
|
||||
|
||||
node.visitMethodInsn(
|
||||
Opcodes.INVOKEINTERFACE,
|
||||
typeMapper.mapType(functionDescriptor.valueParameters[0]).internalName,
|
||||
OperatorNameConventions.INVOKE.identifier,
|
||||
"(${AsmTypes.OBJECT_TYPE})${AsmTypes.OBJECT_TYPE}",
|
||||
true
|
||||
)
|
||||
node.visitInsn(Opcodes.ARETURN)
|
||||
node.visitMaxs(2, 2)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <D : CallableDescriptor?> D.unwrapInitialDescriptorForSuspendFunction(): D =
|
||||
this.safeAs<SimpleFunctionDescriptor>()?.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) as D ?: this
|
||||
|
||||
@@ -19,6 +19,7 @@ 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.backend.common.isBuiltInIntercepted
|
||||
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags
|
||||
@@ -490,13 +491,23 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
||||
}
|
||||
functionDescriptor.isBuiltInSuspendCoroutineOrReturnInJvm() ->
|
||||
return SMAPAndMethodNode(
|
||||
createMethodNodeForSuspendCoroutineOrReturn(functionDescriptor, state.typeMapper),
|
||||
SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)
|
||||
createMethodNodeForSuspendCoroutineOrReturn(functionDescriptor, state.typeMapper),
|
||||
SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)
|
||||
)
|
||||
functionDescriptor.isBuiltInIntercepted() ->
|
||||
return SMAPAndMethodNode(
|
||||
createMethodNodeForIntercepted(functionDescriptor, state.typeMapper),
|
||||
SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)
|
||||
)
|
||||
functionDescriptor.isBuiltInCoroutineContext() ->
|
||||
return SMAPAndMethodNode(
|
||||
createMethodNodeForCoroutineContext(functionDescriptor),
|
||||
SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)
|
||||
createMethodNodeForCoroutineContext(functionDescriptor),
|
||||
SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)
|
||||
)
|
||||
functionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm() ->
|
||||
return SMAPAndMethodNode(
|
||||
createMethodNodeForSuspendCoroutineUninterceptedOrReturn(functionDescriptor, state.typeMapper),
|
||||
SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
import helpers.*
|
||||
import kotlin.coroutines.experimental.*
|
||||
import kotlin.coroutines.experimental.intrinsics.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
suspend fun suspendHereUnintercepted(): String = suspendCoroutineUninterceptedOrReturn { x ->
|
||||
x.resume("OK")
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithExceptionUnintercepted(): String = suspendCoroutineUninterceptedOrReturn { x ->
|
||||
x.resumeWithException(RuntimeException("OK"))
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendHereIntercepted(): String = suspendCoroutineUninterceptedOrReturn { x ->
|
||||
x.intercepted().resume("OK")
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithExceptionIntercepted(): String = suspendCoroutineUninterceptedOrReturn { x ->
|
||||
x.intercepted().resumeWithException(RuntimeException("OK"))
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(expectedCount: Int, c: suspend () -> String): String {
|
||||
var fromSuspension: String? = null
|
||||
var counter = 0
|
||||
|
||||
val result = try {
|
||||
c.startCoroutineUninterceptedOrReturn(object: Continuation<String> {
|
||||
override val context: CoroutineContext
|
||||
get() = ContinuationDispatcher { counter++ }
|
||||
|
||||
override fun resumeWithException(exception: Throwable) {
|
||||
fromSuspension = "Exception: " + exception.message!!
|
||||
}
|
||||
|
||||
override fun resume(value: String) {
|
||||
fromSuspension = value
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
"Exception: ${e.message}"
|
||||
}
|
||||
|
||||
if (counter != expectedCount) throw RuntimeException("fail 0")
|
||||
return fromSuspension!!
|
||||
}
|
||||
|
||||
class ContinuationDispatcher(val dispatcher: () -> Unit) : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
|
||||
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = DispatchedContinuation(dispatcher, continuation)
|
||||
}
|
||||
|
||||
private class DispatchedContinuation<T>(
|
||||
val dispatcher: () -> Unit,
|
||||
val continuation: Continuation<T>
|
||||
): Continuation<T> {
|
||||
override val context: CoroutineContext = continuation.context
|
||||
|
||||
override fun resume(value: T) {
|
||||
dispatcher()
|
||||
continuation.resume(value)
|
||||
}
|
||||
|
||||
override fun resumeWithException(exception: Throwable) {
|
||||
dispatcher()
|
||||
continuation.resumeWithException(exception)
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (builder(0) { suspendHereUnintercepted() } != "OK") return "fail 2"
|
||||
if (builder(1) { suspendHereIntercepted() } != "OK") return "fail 3"
|
||||
|
||||
if (builder(0) { suspendWithExceptionUnintercepted() } != "Exception: OK") return "fail 4"
|
||||
if (builder(1) { suspendWithExceptionIntercepted() } != "Exception: OK") return "fail 5"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
import helpers.*
|
||||
import kotlin.coroutines.experimental.*
|
||||
import kotlin.coroutines.experimental.intrinsics.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
suspend fun suspendHere(): String = suspendCoroutineUninterceptedOrReturn { x ->
|
||||
x.resume("OK")
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithException(): String = suspendCoroutineUninterceptedOrReturn { x ->
|
||||
x.resumeWithException(RuntimeException("OK"))
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(shouldSuspend: Boolean, c: suspend () -> String): String {
|
||||
var fromSuspension: String? = null
|
||||
var counter = 0
|
||||
|
||||
val result = try {
|
||||
c.startCoroutineUninterceptedOrReturn(object: Continuation<String> {
|
||||
override val context: CoroutineContext
|
||||
get() = ContinuationDispatcher { counter++ }
|
||||
|
||||
override fun resumeWithException(exception: Throwable) {
|
||||
fromSuspension = "Exception: " + exception.message!!
|
||||
}
|
||||
|
||||
override fun resume(value: String) {
|
||||
fromSuspension = value
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
"Exception: ${e.message}"
|
||||
}
|
||||
|
||||
if (counter != 0) throw RuntimeException("fail 0 $counter")
|
||||
|
||||
if (shouldSuspend) {
|
||||
if (result !== COROUTINE_SUSPENDED) throw RuntimeException("fail 1")
|
||||
if (fromSuspension == null) throw RuntimeException("fail 2")
|
||||
return fromSuspension!!
|
||||
}
|
||||
|
||||
if (result === COROUTINE_SUSPENDED) throw RuntimeException("fail 3")
|
||||
return result as String
|
||||
}
|
||||
|
||||
class ContinuationDispatcher(val dispatcher: () -> Unit) : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
|
||||
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = DispatchedContinuation(dispatcher, continuation)
|
||||
}
|
||||
|
||||
private class DispatchedContinuation<T>(
|
||||
val dispatcher: () -> Unit,
|
||||
val continuation: Continuation<T>
|
||||
): Continuation<T> {
|
||||
override val context: CoroutineContext = continuation.context
|
||||
|
||||
override fun resume(value: T) {
|
||||
dispatcher()
|
||||
continuation.resume(value)
|
||||
}
|
||||
|
||||
override fun resumeWithException(exception: Throwable) {
|
||||
dispatcher()
|
||||
continuation.resumeWithException(exception)
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (builder(false) { "OK" } != "OK") return "fail 4"
|
||||
if (builder(true) { suspendHere() } != "OK") return "fail 5"
|
||||
|
||||
if (builder(false) { throw RuntimeException("OK") } != "Exception: OK") return "fail 6"
|
||||
if (builder(true) { suspendWithException() } != "Exception: OK") return "fail 7"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+12
@@ -6064,6 +6064,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("intercepted.kt")
|
||||
public void testIntercepted() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("startCoroutine.kt")
|
||||
public void testStartCoroutine() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt");
|
||||
@@ -6081,6 +6087,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendCoroutineUninterceptedOrReturn.kt")
|
||||
public void testSuspendCoroutineUninterceptedOrReturn() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/coroutines/multiModule")
|
||||
|
||||
@@ -6064,6 +6064,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("intercepted.kt")
|
||||
public void testIntercepted() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("startCoroutine.kt")
|
||||
public void testStartCoroutine() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt");
|
||||
@@ -6081,6 +6087,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendCoroutineUninterceptedOrReturn.kt")
|
||||
public void testSuspendCoroutineUninterceptedOrReturn() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/coroutines/multiModule")
|
||||
|
||||
@@ -6064,6 +6064,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("intercepted.kt")
|
||||
public void testIntercepted() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("startCoroutine.kt")
|
||||
public void testStartCoroutine() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt");
|
||||
@@ -6081,6 +6087,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendCoroutineUninterceptedOrReturn.kt")
|
||||
public void testSuspendCoroutineUninterceptedOrReturn() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/coroutines/multiModule")
|
||||
|
||||
+12
@@ -6628,6 +6628,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("intercepted.kt")
|
||||
public void testIntercepted() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/intercepted.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("startCoroutine.kt")
|
||||
public void testStartCoroutine() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutine.kt");
|
||||
@@ -6645,6 +6651,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/startCoroutineUninterceptedOrReturnInterception.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendCoroutineUninterceptedOrReturn.kt")
|
||||
public void testSuspendCoroutineUninterceptedOrReturn() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intrinsicSemantics/suspendCoroutineUninterceptedOrReturn.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/coroutines/multiModule")
|
||||
|
||||
+2
@@ -52,6 +52,8 @@ public final class FunctionIntrinsics {
|
||||
register(ExceptionPropertyIntrinsicFactory.INSTANCE);
|
||||
register(AsDynamicFIF.INSTANCE);
|
||||
register(CoroutineContextFIF.INSTANCE);
|
||||
register(SuspendCoroutineUninterceptedOrReturnFIF.INSTANCE);
|
||||
register(InterceptedFIF.INSTANCE);
|
||||
}
|
||||
|
||||
private void register(@NotNull FunctionIntrinsicFactory instance) {
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.isBuiltInIntercepted
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||
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.js.translate.utils.TranslationUtils
|
||||
|
||||
object InterceptedFIF: FunctionIntrinsicFactory {
|
||||
override fun getIntrinsic(descriptor: FunctionDescriptor): FunctionIntrinsic? {
|
||||
if (!descriptor.isBuiltInIntercepted()) return null
|
||||
return Intrinsic
|
||||
}
|
||||
|
||||
object Intrinsic: FunctionIntrinsic() {
|
||||
override fun apply(callInfo: CallInfo, arguments: List<JsExpression>, context: TranslationContext): JsExpression {
|
||||
val continuation = callInfo.extensionReceiver ?: error("intercepted shall be extension function")
|
||||
val facadeName = context.getNameForDescriptor(TranslationUtils.getCoroutineProperty(context, "facade"))
|
||||
return JsAstUtils.pureFqn(facadeName, continuation)
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.isBuiltInSuspendCoroutineUninterceptedOrReturn
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsInvocation
|
||||
import org.jetbrains.kotlin.js.backend.ast.metadata.isSuspend
|
||||
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
|
||||
|
||||
object SuspendCoroutineUninterceptedOrReturnFIF: FunctionIntrinsicFactory {
|
||||
override fun getIntrinsic(descriptor: FunctionDescriptor): FunctionIntrinsic? {
|
||||
if (!descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn()) return null
|
||||
return Intrinsic
|
||||
}
|
||||
object Intrinsic: FunctionIntrinsic() {
|
||||
override fun apply(callInfo: CallInfo, arguments: List<JsExpression>, context: TranslationContext): JsExpression {
|
||||
val lambda = arguments.first()
|
||||
val continuation = arguments.last()
|
||||
val invocation = JsInvocation(lambda, continuation)
|
||||
invocation.isSuspend = true
|
||||
return invocation
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,8 @@ import kotlin.coroutines.experimental.CoroutineContext
|
||||
import kotlin.coroutines.experimental.processBareContinuationResume
|
||||
|
||||
/**
|
||||
* Obtains the current continuation instance inside suspend functions and either suspend
|
||||
* currently running coroutine or return result immediately without suspension.
|
||||
* Obtains the current continuation instance inside suspend functions and either suspends
|
||||
* currently running coroutine or returns result immediately without suspension.
|
||||
*
|
||||
* If the [block] returns the special [COROUTINE_SUSPENDED] value, it means that suspend function did suspend the execution and will
|
||||
* not return any result immediately. In this case, the [Continuation] provided to the [block] shall be invoked at some moment in the
|
||||
@@ -42,9 +42,29 @@ import kotlin.coroutines.experimental.processBareContinuationResume
|
||||
@SinceKotlin("1.1")
|
||||
@kotlin.internal.InlineOnly
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
// todo: this shall be ordinary function, but, unfortunately, bootstrap compiler does not have suspendCoroutineUninterceptedOrReturn and intercepted intrinsics
|
||||
public inline suspend fun <T> suspendCoroutineOrReturn(crossinline block: (Continuation<T>) -> Any?): T =
|
||||
throw NotImplementedError("Implementation is intrinsic")
|
||||
|
||||
/**
|
||||
* Obtains the current continuation instance inside suspend functions and either suspends
|
||||
* currently running coroutine or returns result immediately without suspension.
|
||||
*
|
||||
* Unlike [suspendCoroutineOrReturn] it does not intercept continuation.
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline suspend fun <T> suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation<T>) -> Any?): T =
|
||||
throw NotImplementedError("Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic")
|
||||
|
||||
/**
|
||||
* Intercept continuation with [ContinuationInterceptor].
|
||||
*/
|
||||
@SinceKotlin("1.2")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <T> Continuation<T>.intercepted(): Continuation<T> =
|
||||
throw NotImplementedError("Implementation of intercepted is intrinsic")
|
||||
|
||||
/**
|
||||
* Continuation context of current coroutine.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user